Skip to content

Commit 05b87f2

Browse files
committed
Add --ignore-changes option for filtering changes by file patterns
1 parent 40b653e commit 05b87f2

File tree

9 files changed

+187
-28
lines changed

9 files changed

+187
-28
lines changed

README.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,34 @@ last tagged version.
197197
The `k16.kmono.version/resolve-package-changes-since` can be used to find commits that have changed the package since a
198198
given git revision.
199199

200+
#### Ignoring Changes by File Pattern
201+
202+
When using change detection (`--changed` or `--changed-since`), you can filter out changes to specific files using
203+
`--ignore-changes`. This accepts a comma-separated list of regexp patterns matched against file paths relative to the
204+
package root. If **all** changed files in a package match the ignore patterns, that package is treated as unchanged.
205+
206+
```bash
207+
# Ignore markdown-only changes
208+
kmono exec --changed --ignore-changes ".*\.md$" -- npm test
209+
210+
# Ignore multiple patterns (comma-separated)
211+
kmono exec --changed --ignore-changes ".*\.md$,LICENSE,^docs/" -- npm build
212+
213+
# Works with --changed-since too
214+
kmono exec --changed-since main --ignore-changes ".*\.md$" -- npm test
215+
```
216+
217+
This is useful in CI/CD workflows where documentation-only changes shouldn't trigger builds or tests.
218+
219+
Patterns can also be configured at the workspace level in `deps.edn` (see [Workspace Configuration](#workspace-configuration)):
220+
221+
```clojure
222+
{:kmono/workspace {:group com.example
223+
:ignore-changes [".*\\.md$" "^docs/" "LICENSE"]}}
224+
```
225+
226+
When both CLI and workspace-level patterns are provided, the CLI `--ignore-changes` takes precedence.
227+
200228
These tools and others can be used to build sophisticated build and release pipelines for kmono workspaces.
201229

202230
## API Documentation
@@ -263,6 +291,7 @@ configuration accepts the following properties:
263291
| `:repl-aliases` | `[keyword?]` | `nil` | A set of `deps` aliases to include when running `kmono repl` |
264292
| `:aliases` | `[keyword?]` | `nil` | A set of `deps` aliases to include for all kmono workspace commands by default |
265293
| `:package-aliases` | `[keyword?]` | `nil` | A set of namespaced [alias globs](#alias-globs) that describe the aliases of packages within the workspace to include in the classpath |
294+
| `:ignore-changes` | `[string?]` | `nil` | A list of regexp patterns for file paths to ignore when determining package changes (used with `--changed` / `--changed-since`) |
266295
267296
Example:
268297
@@ -271,7 +300,9 @@ Example:
271300
{:kmono/workspace {:group com.example
272301
:packages #{"./(packages|modules)/**"}
273302
;; Include any `:test` aliases from all (`*`) packages in the workspace
274-
:package-aliases [:*/test]}
303+
:package-aliases [:*/test]
304+
;; Ignore doc-only changes when using --changed
305+
:ignore-changes [".*\\.md$" "LICENSE"]}
275306
276307
:paths ["src" "resources"]
277308

packages/kmono-cli/src/k16/kmono/cli/commands/exec.clj

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,21 @@
1717
include-dependents run-in-order concurrency]
1818
:as opts}
1919
args]
20-
(let [{:keys [root packages]} (common.context/load-context opts)
20+
(let [{:keys [root packages config]} (common.context/load-context opts)
21+
ignore-changes (:ignore-changes config)
22+
change-opts (when ignore-changes
23+
{:ignore-changes ignore-changes})
2124

2225
packages
2326
(cond-> packages
2427
(or changed skip-unchanged)
2528
(->> (kmono.version/resolve-package-versions root)
26-
(kmono.version/resolve-package-changes root)
29+
(kmono.version/resolve-package-changes root change-opts)
2730
(core.graph/filter-by kmono.version/package-changed?
2831
{:include-dependents include-dependents}))
2932

3033
changed-since
31-
(->> (kmono.version/resolve-package-changes-since root changed-since)
34+
(->> (kmono.version/resolve-package-changes-since root changed-since change-opts)
3235
(core.graph/filter-by kmono.version/package-changed?
3336
{:include-dependents include-dependents}))
3437

@@ -65,6 +68,7 @@
6568
:skip-unchanged opts/skip-unchanged-opt
6669
:changed opts/changed-opt
6770
:changed-since opts/changed-since-opt
71+
:ignore-changes opts/ignore-changes-opt
6872
:filter opts/package-filter-opt
6973
:include-dependents include-dependents
7074
:concurrency opts/concurrency-opt}

packages/kmono-cli/src/k16/kmono/cli/commands/query.clj

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,19 @@
2525
(log/error "Either --with-changes or --with-changes-since is required when setting --filter-unchanged")
2626
(System/exit 1))
2727

28-
{:keys [root packages]} (common.context/load-context opts)
28+
{:keys [root packages config]} (common.context/load-context opts)
29+
ignore-changes (:ignore-changes config)
30+
change-opts (when ignore-changes
31+
{:ignore-changes ignore-changes})
2932
packages (cond->> packages
3033
with-versions
3134
(kmono.version/resolve-package-versions root)
3235

3336
with-changes
34-
(kmono.version/resolve-package-changes root)
37+
(kmono.version/resolve-package-changes root change-opts)
3538

3639
with-changes-since
37-
(kmono.version/resolve-package-changes-since root with-changes-since)
40+
(kmono.version/resolve-package-changes-since root with-changes-since change-opts)
3841

3942
filter-unchanged
4043
(core.graph/filter-by kmono.version/package-changed?
@@ -107,6 +110,7 @@
107110
:options {:with-versions with-versions
108111
:with-changes-since with-changes-since
109112
:with-changes with-changes
113+
:ignore-changes opts/ignore-changes-opt
110114
:filter-unchanged filter-unchanged
111115
:include-dependents include-dependents
112116
:filter opts/package-filter-opt

packages/kmono-cli/src/k16/kmono/cli/commands/run.clj

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,23 @@
1414
(set! *warn-on-reflection* true)
1515

1616
(defn- run-command [{:keys [M T X skip-unchanged changed changed-since] :as opts} args]
17-
(let [{:keys [root packages]} (common.context/load-context opts)
17+
(let [{:keys [root packages config]} (common.context/load-context opts)
1818
filter' (:filter opts)
19+
ignore-changes (:ignore-changes config)
20+
change-opts (when ignore-changes
21+
{:ignore-changes ignore-changes})
1922
packages (cond-> packages
2023
filter'
2124
(->> (core.graph/filter-by (core.packages/name-matches? filter')))
2225

2326
(or changed skip-unchanged)
2427
(->> (kmono.version/resolve-package-versions root)
25-
(kmono.version/resolve-package-changes root)
28+
(kmono.version/resolve-package-changes root change-opts)
2629
(core.graph/filter-by kmono.version/package-changed?
2730
{:include-dependents true}))
2831

2932
changed-since
30-
(->> (kmono.version/resolve-package-changes-since root changed-since)
33+
(->> (kmono.version/resolve-package-changes-since root changed-since change-opts)
3134
(core.graph/filter-by kmono.version/package-changed?
3235
{:include-dependents true})))
3336

@@ -68,6 +71,7 @@
6871
:skip-unchanged opts/skip-unchanged-opt
6972
:changed opts/changed-opt
7073
:changed-since opts/changed-since-opt
74+
:ignore-changes opts/ignore-changes-opt
7175
:concurrency opts/concurrency-opt
7276
:filter opts/package-filter-opt
7377

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
(ns k16.kmono.cli.common.config)
22

33
(defn merge-workspace-config
4-
[workspace-config {:keys [package-aliases aliases]}]
4+
[workspace-config {:keys [package-aliases aliases ignore-changes]}]
55
(cond-> workspace-config
66
package-aliases (assoc :package-aliases package-aliases)
7-
aliases (update :aliases into aliases)))
7+
aliases (update :aliases into aliases)
8+
ignore-changes (assoc :ignore-changes ignore-changes)))

packages/kmono-cli/src/k16/kmono/cli/common/opts.clj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,8 @@
6868
{:desc "Filter packages by those that have changed since the specified git <rev>"
6969
:ref "<rev>"
7070
:coerce :string})
71+
72+
(def ignore-changes-opt
73+
{:desc "Comma-separated regexp patterns of file paths to ignore when determining changes"
74+
:coerce :string
75+
:parse-fn (fn [value] (str/split value #","))})

packages/kmono-core/src/k16/kmono/core/schema.clj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
[:aliases {:optional true}
2020
[:vector :keyword]]
2121
[:package-aliases {:optional true}
22-
[:vector :keyword]]])
22+
[:vector :keyword]]
23+
24+
[:ignore-changes {:optional true}
25+
[:sequential :string]]])
2326

2427
(def ?PackageConfig
2528
[:map

packages/kmono-version/src/k16/kmono/version.clj

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
[k16.kmono.core.thread :as core.thread]
77
[k16.kmono.git :as git]
88
[k16.kmono.git.commit :as git.commit]
9+
[k16.kmono.git.files :as git.files]
910
[k16.kmono.git.tags :as git.tags]
1011
[k16.kmono.version.semver :as semver]))
1112

@@ -68,15 +69,37 @@
6869
(transient {}))
6970
persistent!)))
7071

72+
(defn- file-ignored?
73+
"Returns true if the file path matches any of the ignore patterns."
74+
[patterns file-path]
75+
(boolean (some #(re-find (re-pattern %) file-path) patterns)))
76+
77+
(defn- has-meaningful-changes?
78+
"Returns true if the package has changed files that don't all match ignore patterns.
79+
Files are matched against patterns using paths relative to the package root."
80+
[project-root pkg ref ignore-changes]
81+
(let [files (git.files/find-changed-files-since
82+
project-root {:ref ref
83+
:subdir (:relative-path pkg)})
84+
pkg-prefix (str (:relative-path pkg) "/")
85+
relative-files (map #(str/replace-first % pkg-prefix "") files)]
86+
(boolean (seq (remove #(file-ignored? ignore-changes %) relative-files)))))
87+
7188
(defn- -resolve-package-changes-since
72-
[project-root packages rev-fn]
89+
[project-root packages rev-fn {:keys [ignore-changes]}]
7390
(git/with-repo [_ project-root]
7491
(into {}
7592
(core.thread/batch
7693
(fn find-commits [[pkg-name pkg]]
77-
(let [commits (git.commit/find-commits-since
78-
project-root {:ref (rev-fn pkg)
94+
(let [ref (rev-fn pkg)
95+
commits (git.commit/find-commits-since
96+
project-root {:ref ref
7997
:subdir (:relative-path pkg)})
98+
commits (if (and (seq ignore-changes) (seq commits) ref)
99+
(if (has-meaningful-changes? project-root pkg ref ignore-changes)
100+
commits
101+
[])
102+
commits)
80103
pkg (assoc pkg :commits (vec commits))]
81104
[pkg-name pkg]))
82105
32)
@@ -90,25 +113,39 @@
90113
and version. See `k16.kmono.version/resolve-package-versions` for a
91114
description on how this tag is expected to be formatted.
92115
93-
Any commits found will be appended to the packages `:commits` key."
116+
Any commits found will be appended to the packages `:commits` key.
117+
118+
An optional `opts` map may be provided with `:ignore-changes` - a sequence
119+
of regexp patterns. When all changed files in a package match these patterns,
120+
the package is treated as unchanged."
94121
{:malli/schema [:-> :string core.schema/?PackageMap core.schema/?PackageMap]}
95-
[project-root packages]
96-
(-resolve-package-changes-since project-root
97-
packages
98-
(fn [pkg]
99-
(when (:version pkg)
100-
(create-package-version-tag pkg)))))
122+
([project-root packages]
123+
(resolve-package-changes project-root nil packages))
124+
([project-root opts packages]
125+
(-resolve-package-changes-since project-root
126+
packages
127+
(fn [pkg]
128+
(when (:version pkg)
129+
(create-package-version-tag pkg)))
130+
opts)))
101131

102132
(defn resolve-package-changes-since
103133
"For each package try find all commits that modified files in the package
104134
subdirectory since the given rev.
105135
106-
Any commits found will be appended to the packages `:commits` key."
136+
Any commits found will be appended to the packages `:commits` key.
137+
138+
An optional `opts` map may be provided with `:ignore-changes` - a sequence
139+
of regexp patterns. When all changed files in a package match these patterns,
140+
the package is treated as unchanged."
107141
{:malli/schema [:-> :string :string core.schema/?PackageMap core.schema/?PackageMap]}
108-
[project-root rev packages]
109-
(-resolve-package-changes-since project-root
110-
packages
111-
(constantly rev)))
142+
([project-root rev packages]
143+
(resolve-package-changes-since project-root rev nil packages))
144+
([project-root rev opts packages]
145+
(-resolve-package-changes-since project-root
146+
packages
147+
(constantly rev)
148+
opts)))
112149

113150
(defn package-changed?
114151
"A filter function designed to be used with `k16.kmono.core.graph/filter-by`.

packages/kmono-version/test/k16/kmono/changes_test.clj

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,73 @@
5757
:message "Change package b"
5858
:body ""}]}}
5959
packages))))
60+
61+
(deftest ignore-changes-all-files-match-test
62+
(git.tags/create-tags *repo* {:tags ["com.kepler16/a@1.0.0"
63+
"com.kepler16/b@1.1.0"]})
64+
65+
(fs/create-file (fs/file *repo* "packages/a/README.md"))
66+
(commit *repo* "docs: add readme to package a")
67+
68+
(let [config (core.config/resolve-workspace-config *repo*)
69+
packages (->> (core.packages/resolve-packages *repo* config)
70+
(kmono.version/resolve-package-versions *repo*)
71+
(kmono.version/resolve-package-changes *repo* {:ignore-changes [".*\\.md$"]}))]
72+
(is (match? {'com.kepler16/a {:version "1.0.0"
73+
:commits []}
74+
'com.kepler16/b {:version "1.1.0"
75+
:commits []}}
76+
packages))))
77+
78+
(deftest ignore-changes-partial-match-test
79+
(git.tags/create-tags *repo* {:tags ["com.kepler16/a@1.0.0"
80+
"com.kepler16/b@1.1.0"]})
81+
82+
(fs/create-file (fs/file *repo* "packages/a/README.md"))
83+
(fs/create-dirs (fs/file *repo* "packages/a/src"))
84+
(fs/create-file (fs/file *repo* "packages/a/src/core.clj"))
85+
(commit *repo* "feat: add code and docs to package a")
86+
87+
(let [config (core.config/resolve-workspace-config *repo*)
88+
packages (->> (core.packages/resolve-packages *repo* config)
89+
(kmono.version/resolve-package-versions *repo*)
90+
(kmono.version/resolve-package-changes *repo* {:ignore-changes [".*\\.md$"]}))]
91+
(is (match? {'com.kepler16/a {:version "1.0.0"
92+
:commits [{:message "feat: add code and docs to package a"
93+
:body ""}]}
94+
'com.kepler16/b {:version "1.1.0"
95+
:commits []}}
96+
packages))))
97+
98+
(deftest ignore-changes-multiple-patterns-test
99+
(git.tags/create-tags *repo* {:tags ["com.kepler16/a@1.0.0"
100+
"com.kepler16/b@1.1.0"]})
101+
102+
(fs/create-file (fs/file *repo* "packages/a/README.md"))
103+
(fs/create-file (fs/file *repo* "packages/a/LICENSE"))
104+
(commit *repo* "docs: add readme and license")
105+
106+
(let [config (core.config/resolve-workspace-config *repo*)
107+
packages (->> (core.packages/resolve-packages *repo* config)
108+
(kmono.version/resolve-package-versions *repo*)
109+
(kmono.version/resolve-package-changes *repo* {:ignore-changes [".*\\.md$" "LICENSE"]}))]
110+
(is (match? {'com.kepler16/a {:version "1.0.0"
111+
:commits []}
112+
'com.kepler16/b {:version "1.1.0"
113+
:commits []}}
114+
packages))))
115+
116+
(deftest ignore-changes-since-rev-test
117+
(let [start-commit (get-current-commit *repo*)
118+
119+
_ (fs/create-file (fs/file *repo* "packages/b/CHANGELOG.md"))
120+
121+
_ (commit *repo* "docs: add changelog to package b")
122+
123+
config (core.config/resolve-workspace-config *repo*)
124+
packages (->> (core.packages/resolve-packages *repo* config)
125+
(kmono.version/resolve-package-changes-since *repo* start-commit {:ignore-changes [".*\\.md$"]}))]
126+
127+
(is (match? {'com.kepler16/a {:commits []}
128+
'com.kepler16/b {:commits []}}
129+
packages))))

0 commit comments

Comments
 (0)