Skip to content

Commit 3d20614

Browse files
committed
Add --ignore-changes option for filtering changes by file patterns
- Add `--ignore-changes` CLI flag accepting comma-separated regexp patterns - Filter out commits where all changed files match ignore patterns - Support workspace-level `:ignore-changes` in `:kmono/workspace` config - Support per-package `:ignore-changes` override in `:kmono/package` config - Precedence order: CLI > per-package > workspace - Per-commit filtering: each commit is individually evaluated against patterns
1 parent 40b653e commit 3d20614

File tree

11 files changed

+294
-37
lines changed

11 files changed

+294
-37
lines changed

README.md

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,42 @@ 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 run --changed --ignore-changes ".*\.md$" -M :test
209+
210+
# Ignore multiple patterns (comma-separated)
211+
kmono run --changed --ignore-changes ".*\.md$,LICENSE,^docs/" -M :build
212+
213+
# Works with --changed-since too
214+
kmono run --changed-since main --ignore-changes ".*\.md$" -M :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+
Individual packages can override the workspace-level patterns by setting `:ignore-changes` in their `:kmono/package`
227+
config (see [Package Configuration](#package-configuration)):
228+
229+
```clojure
230+
;; packages/my-lib/deps.edn
231+
{:kmono/package {:ignore-changes [".*\\.md$" "^test-fixtures/"]}}
232+
```
233+
234+
The precedence order is: CLI `--ignore-changes` > per-package `:ignore-changes` > workspace `:ignore-changes`.
235+
200236
These tools and others can be used to build sophisticated build and release pipelines for kmono workspaces.
201237

202238
## API Documentation
@@ -263,6 +299,7 @@ configuration accepts the following properties:
263299
| `:repl-aliases` | `[keyword?]` | `nil` | A set of `deps` aliases to include when running `kmono repl` |
264300
| `:aliases` | `[keyword?]` | `nil` | A set of `deps` aliases to include for all kmono workspace commands by default |
265301
| `: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 |
302+
| `:ignore-changes` | `[string?]` | `nil` | A list of regexp patterns for file paths to ignore when determining package changes (used with `--changed` / `--changed-since`) |
266303
267304
Example:
268305
@@ -271,7 +308,9 @@ Example:
271308
{:kmono/workspace {:group com.example
272309
:packages #{"./(packages|modules)/**"}
273310
;; Include any `:test` aliases from all (`*`) packages in the workspace
274-
:package-aliases [:*/test]}
311+
:package-aliases [:*/test]
312+
;; Ignore doc-only changes when using --changed
313+
:ignore-changes [".*\\.md$" "LICENSE"]}
275314
276315
:paths ["src" "resources"]
277316
@@ -285,9 +324,10 @@ Packages in the workspace can optionally provide their own configuration metadat
285324
286325
| Field | Type | Default | Description |
287326
| ----------- | -------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
288-
| `:group` | `symbol?` | `nil` | The mvn group to use for this package. If not specified, the `:group` specified in the root `:kmono/workspace` configuration will be used. |
289-
| `:name` | `string? \| symbol?` | `$dir` | The name of the package. If not set the name of the parent directory containing the packages' `deps.edn` file will be used as the package name |
290-
| `:excluded` | `boolean?` | `false` | Whether or not this package is excluded from the project workspace |
327+
| `:group` | `symbol?` | `nil` | The mvn group to use for this package. If not specified, the `:group` specified in the root `:kmono/workspace` configuration will be used. |
328+
| `:name` | `string? \| symbol?` | `$dir` | The name of the package. If not set the name of the parent directory containing the packages' `deps.edn` file will be used as the package name |
329+
| `:excluded` | `boolean?` | `false` | Whether or not this package is excluded from the project workspace |
330+
| `:ignore-changes` | `[string?]` | `nil` | Override the workspace-level `:ignore-changes` patterns for this package |
291331
292332
Example
293333
@@ -296,7 +336,9 @@ Example
296336
{:kmono/package {;; Maven artifacts group
297337
:group com.example
298338
;; Override the default package name
299-
:name example-lib}
339+
:name example-lib
340+
;; Override workspace-level ignore-changes for this package
341+
:ignore-changes [".*\\.md$" "^test-fixtures/"]}
300342

301343
:paths ["src"]
302344
:deps {...}

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/packages.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
(not (fs/same-file? project-root package-path)))
1717
(let [relative-path (str (fs/relativize project-root package-path))
1818
package (merge {:name (symbol (fs/file-name package-path))}
19-
(select-keys workspace-config [:group])
20-
(select-keys config [:group :name :deps-edn])
19+
(select-keys workspace-config [:group :ignore-changes])
20+
(select-keys config [:group :name :deps-edn :ignore-changes])
2121
{:absolute-path (str package-path)
2222
:relative-path relative-path
2323
:depends-on #{}})

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,19 @@
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
2629
[:group {:optional true} :symbol]
2730
[:name {:optional true}
2831
[:maybe [:or :string :symbol]]]
29-
[:excluded {:optional true} :boolean]])
32+
[:excluded {:optional true} :boolean]
33+
[:ignore-changes {:optional true}
34+
[:sequential :string]]])
3035

3136
(def ?Coordinate
3237
[:map
@@ -56,7 +61,9 @@
5661
[:dependents [:set :symbol]]
5762

5863
[:absolute-path :string]
59-
[:relative-path :string]])
64+
[:relative-path :string]
65+
66+
[:ignore-changes {:optional true} [:sequential :string]]])
6067

6168
(def ?PackageMap
6269
[:map-of :symbol ?Package])

packages/kmono-git/src/k16/kmono/git/files.clj

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
(:import
66
[java.io ByteArrayOutputStream]
77
[org.eclipse.jgit.diff DiffEntry DiffFormatter]
8-
[org.eclipse.jgit.lib Constants Repository]))
8+
[org.eclipse.jgit.lib Constants Repository]
9+
[org.eclipse.jgit.revwalk RevCommit RevWalk]))
910

1011
(set! *warn-on-reflection* true)
1112

@@ -41,3 +42,30 @@
4142
(or (nil? subdir)
4243
(str/starts-with? entry subdir)))))
4344
results))))))
45+
46+
(defn find-commit-changed-files
47+
"Return file paths changed by a specific commit. If `subdir` is provided, only
48+
include files within that subdir."
49+
[^String repo-path {:keys [sha subdir]}]
50+
(git/with-repo [repo repo-path]
51+
(let [commit-id (Repository/.resolve repo sha)
52+
subdir-prefix (when subdir (str subdir "/"))]
53+
(with-open [walk (RevWalk. repo)]
54+
(let [commit (RevWalk/.parseCommit walk commit-id)
55+
parent-tree (when (pos? (RevCommit/.getParentCount commit))
56+
(let [parent (RevWalk/.parseCommit walk
57+
(aget (RevCommit/.getParents commit) 0))]
58+
(RevCommit/.getTree parent)))
59+
commit-tree (RevCommit/.getTree commit)]
60+
(with-open [out (ByteArrayOutputStream.)
61+
df (DiffFormatter. out)]
62+
(DiffFormatter/.setRepository df repo)
63+
(let [diffs (DiffFormatter/.scan df parent-tree commit-tree)]
64+
(into []
65+
(comp
66+
(map #(DiffEntry/.getNewPath %))
67+
(remove #(= % DiffEntry/DEV_NULL))
68+
(filter (fn [path]
69+
(or (nil? subdir-prefix)
70+
(str/starts-with? path subdir-prefix)))))
71+
diffs))))))))

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

Lines changed: 53 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,38 @@
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- commit-has-meaningful-changes?
78+
"Returns true if the commit has changed files that don't all match ignore
79+
patterns. Files are matched relative to the package root."
80+
[project-root pkg ignore-changes commit]
81+
(let [files (git.files/find-commit-changed-files
82+
project-root {:sha (:sha commit)
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+
pkg-ignore-changes (or (:ignore-changes pkg) ignore-changes)
99+
commits (if (and (seq pkg-ignore-changes) (seq commits) ref)
100+
(filterv #(commit-has-meaningful-changes?
101+
project-root pkg pkg-ignore-changes %)
102+
commits)
103+
commits)
80104
pkg (assoc pkg :commits (vec commits))]
81105
[pkg-name pkg]))
82106
32)
@@ -90,25 +114,39 @@
90114
and version. See `k16.kmono.version/resolve-package-versions` for a
91115
description on how this tag is expected to be formatted.
92116
93-
Any commits found will be appended to the packages `:commits` key."
117+
Any commits found will be appended to the packages `:commits` key.
118+
119+
An optional `opts` map may be provided with `:ignore-changes` - a sequence of
120+
regexp patterns. Commits where all changed files match these patterns are
121+
filtered out."
94122
{: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)))))
123+
([project-root packages]
124+
(resolve-package-changes project-root nil packages))
125+
([project-root opts packages]
126+
(-resolve-package-changes-since project-root
127+
packages
128+
(fn [pkg]
129+
(when (:version pkg)
130+
(create-package-version-tag pkg)))
131+
opts)))
101132

102133
(defn resolve-package-changes-since
103134
"For each package try find all commits that modified files in the package
104135
subdirectory since the given rev.
105136
106-
Any commits found will be appended to the packages `:commits` key."
137+
Any commits found will be appended to the packages `:commits` key.
138+
139+
An optional `opts` map may be provided with `:ignore-changes` - a sequence of
140+
regexp patterns. Commits where all changed files match these patterns are
141+
filtered out."
107142
{: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)))
143+
([project-root rev packages]
144+
(resolve-package-changes-since project-root rev nil packages))
145+
([project-root rev opts packages]
146+
(-resolve-package-changes-since project-root
147+
packages
148+
(constantly rev)
149+
opts)))
112150

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

0 commit comments

Comments
 (0)