gate-cache 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,261 @@
1
+ Metadata-Version: 2.3
2
+ Name: gate-cache
3
+ Version: 0.3.0
4
+ Summary: Skip lint and test gates that already passed on the exact same git tree.
5
+ Author: Nate Landau
6
+ Author-email: Nate Landau <github@natenate.org>
7
+ License: MIT License
8
+
9
+ Copyright (c) 2026 Nate Landau
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+ Classifier: Programming Language :: Python :: 3.11
29
+ Classifier: Programming Language :: Python :: 3.12
30
+ Classifier: Programming Language :: Python :: 3.13
31
+ Classifier: Programming Language :: Python :: 3.14
32
+ Requires-Dist: cyclopts>=5.2.0,<6.0.0
33
+ Requires-Dist: nclutils>=3.4.4,<4.0.0
34
+ Requires-Python: >=3.11
35
+ Description-Content-Type: text/markdown
36
+
37
+ # gate-cache
38
+
39
+ Skip a command that already passed on the same code.
40
+
41
+ A lint or test command often runs many times on code that did not change. It runs when you finish the work, in a pre-commit hook, after a rebase that changed nothing, and in a pre-push hook. When an agent works in the repository, it runs the same command again. If the command takes minutes, the repeat runs take most of the time between finished code and a pushed branch.
42
+
43
+ gate-cache remembers which commands passed on which code. It wraps a command and gives it a name. When the command passes, gate-cache writes a marker for that name and for the exact content of the tracked files. The next time the same name runs on the same content, gate-cache finds the marker, skips the command, and exits 0 at once.
44
+
45
+ ```console
46
+ $ gate-cache --name pytest -- uv run pytest
47
+ ============================= 412 passed in 94.20s =============================
48
+
49
+ $ gate-cache --name pytest -- uv run pytest
50
+ gate-cache: pytest already passed on tree aaff74984ccc, skipping
51
+ ```
52
+
53
+ A change to a tracked file changes the content, so no marker matches and the command runs again. A CI runner that starts from a fresh clone has no markers, so it runs every command.
54
+
55
+ ## Install
56
+
57
+ gate-cache requires Python 3.11 or later, on macOS or Linux.
58
+
59
+ ```sh
60
+ uv tool install gate-cache
61
+ ```
62
+
63
+ The prek and pre-commit hooks install gate-cache themselves, so the hooks work without this step.
64
+
65
+ ## Run a command through gate-cache
66
+
67
+ The name goes after `--name`, and the command goes after `--`:
68
+
69
+ ```sh
70
+ gate-cache --name pytest -- uv run pytest
71
+ gate-cache --name ruff -- uv run ruff check .
72
+ ```
73
+
74
+ Each name has its own markers. A command run under one name never skips because of a marker for another name.
75
+
76
+ gate-cache exits with the exit code of the command, or with 0 when it skips the command. It writes a marker only when the command passes, so a command that failed runs again the next time.
77
+
78
+ When the working copy has uncommitted edits or files that git does not track, the content on disk is not a commit-ready snapshot. gate-cache then runs the command and writes no marker.
79
+
80
+ ## Run it as a hook
81
+
82
+ gate-cache works with [prek](https://github.com/j178/prek) and with [pre-commit](https://pre-commit.com) 3.2 or later. Each `gate-cache` hook takes the same arguments as the command line:
83
+
84
+ ```yaml
85
+ repos:
86
+ - repo: https://github.com/natelandau/gate-cache
87
+ rev: v0.1.0
88
+ hooks:
89
+ - id: gate-cache
90
+ name: pytest
91
+ args: [--name, pytest, --, uv, run, pytest]
92
+ files: ^(src/|tests/)
93
+ - id: gate-cache
94
+ name: ruff
95
+ args: [--name, ruff, --, uv, run, ruff, check, .]
96
+ ```
97
+
98
+ The hook runs at commit time and at push time. At commit time, gate-cache matches the content that you commit.
99
+
100
+ The hook runs in its own virtual environment, and the wrapped command inherits that environment. A command started with `uv run`, or a project command such as `just test`, runs in the project environment. `uv run` prints a warning that `VIRTUAL_ENV` does not match the project. The warning has no effect on the run.
101
+
102
+ Hooks, agents, and your terminal all read and write the same markers. When one of them runs `gate-cache --name pytest -- uv run pytest` and it passes, the others skip that name on the same content.
103
+
104
+ ## Examples
105
+
106
+ These examples show gate-cache inside the task runner of a project. When a recipe holds the wrapped command, everything that runs the recipe uses the same name and the same markers.
107
+
108
+ ### justfile
109
+
110
+ ```just
111
+ test:
112
+ gate-cache --name test -- uv run pytest
113
+
114
+ lint:
115
+ gate-cache --name lint -- uv run ruff check .
116
+ ```
117
+
118
+ After `just test` passes, the next `just test` on the same content skips pytest.
119
+
120
+ ### Recipes with arguments
121
+
122
+ gate-cache identifies a command by its name alone, not by its arguments. If `just test -k unit` and `just test` ran under the same name, a pass of the unit tests would also skip the full suite. Giving each set of arguments its own name, or caching only the bare form, keeps the two apart.
123
+
124
+ This recipe caches `just test` and runs `just test -k unit` without the cache:
125
+
126
+ ```just
127
+ test *args:
128
+ #!/usr/bin/env bash
129
+ set -euo pipefail
130
+ if [[ -z "{{ args }}" ]]; then
131
+ exec gate-cache --name test -- uv run pytest
132
+ fi
133
+ exec uv run pytest {{ args }}
134
+ ```
135
+
136
+ ### Makefile
137
+
138
+ ```make
139
+ test:
140
+ gate-cache --name test -- uv run pytest
141
+
142
+ lint:
143
+ gate-cache --name lint -- uv run ruff check .
144
+ ```
145
+
146
+ ### A hook that calls the recipe
147
+
148
+ A local hook can run the recipe instead of the gate-cache hook repository:
149
+
150
+ ```yaml
151
+ repos:
152
+ - repo: local
153
+ hooks:
154
+ - id: test
155
+ name: test
156
+ entry: just test
157
+ language: system
158
+ pass_filenames: false
159
+ stages: [pre-commit, pre-push]
160
+ ```
161
+
162
+ With this hook, a commit, a push, an agent, and your terminal all run `just test` and share its markers. The recipe runs in the project environment, so `uv run` prints no `VIRTUAL_ENV` warning. This form needs `gate-cache` and `just` on the `PATH`.
163
+
164
+ ### CI
165
+
166
+ gate-cache turns off caching when the `CI` variable is on. A variable is on when its value is not empty, `0`, or `false`. Case and spaces around the value do not matter. Many CI systems set `CI=true` in every job, for example GitHub Actions and GitLab CI/CD. Jenkins does not set `CI`.
167
+
168
+ When caching is off, gate-cache runs the command each time and writes no marker.
169
+
170
+ `GATE_CACHE_DISABLE` overrides `CI`. To keep caching on when `CI` is on, set `GATE_CACHE_DISABLE` to `0` or `false`:
171
+
172
+ ```sh
173
+ GATE_CACHE_DISABLE=0 just test
174
+ ```
175
+
176
+ To turn off caching when `CI` is not set, set `GATE_CACHE_DISABLE` to `1`:
177
+
178
+ ```sh
179
+ GATE_CACHE_DISABLE=1 just test
180
+ ```
181
+
182
+ ## See what already passed
183
+
184
+ `gate-cache status` lists the names that passed on the current content, and the names that ran on it without a pass:
185
+
186
+ ```console
187
+ $ gate-cache status
188
+ tree: aaff74984cccd156a469afa7d9ab10e4777beb24
189
+ passed: pytest
190
+ missed: ruff (exit code 1)
191
+ ```
192
+
193
+ A name is missed when its command failed, or when it exited 0 but changed the files. A later pass of the same name removes it from the `missed` line.
194
+
195
+ With `--name`, it reports on one name:
196
+
197
+ ```console
198
+ $ gate-cache status --name ruff
199
+ name: ruff
200
+ tree: aaff74984cccd156a469afa7d9ab10e4777beb24
201
+ marker: /path/to/repo/.git/gate-cache/7d94ca2f57ec0a77f60d328fb405d339fb0c2a645991b97c042ac54403104e9e
202
+ result: miss
203
+ last run: exit code 1
204
+ ```
205
+
206
+ The `last run` line shows only when the name ran on this content without a pass.
207
+
208
+ `status` runs nothing. With `--name`, it exits 0 when a marker matches and 1 when none does. Without `--name`, it exits 0 when at least one name passed. When gate-cache cannot match the content, for example because of uncommitted edits, the `tree` line gives the reason.
209
+
210
+ With `--json`, `status` writes one JSON object to stdout instead of text. The exit codes do not change:
211
+
212
+ ```console
213
+ $ gate-cache status --json
214
+ {
215
+ "enabled": true,
216
+ "tree": "aaff74984cccd156a469afa7d9ab10e4777beb24",
217
+ "reason": null,
218
+ "passed": ["pytest"],
219
+ "missed": [
220
+ {"name": "ruff", "exit_code": 1, "reason": "exit code 1", "timestamp": "2026-10-12T12:31:55Z"}
221
+ ]
222
+ }
223
+ ```
224
+
225
+ `reason` tells why gate-cache cannot match the content, and is `null` when it can. When caching is off, `enabled` is `false` and `reason` names the variable. With `--name`, the object has the `name`, `marker`, `result`, and `last_run` keys in place of `passed` and `missed`.
226
+
227
+ ## Start fresh
228
+
229
+ A marker records only the content of the tracked files. A new tool version, a changed environment variable, or a change to a service does not change that content, so the old markers still match.
230
+
231
+ `gate-cache clear` deletes every marker for the repository, and the records of missed runs:
232
+
233
+ ```console
234
+ $ gate-cache clear
235
+ gate-cache: removed 3 markers
236
+ ```
237
+
238
+ `GATE_CACHE_DISABLE=1` runs every command without the cache, for one command or for a whole session:
239
+
240
+ ```sh
241
+ GATE_CACHE_DISABLE=1 git push
242
+ ```
243
+
244
+ ## Settings
245
+
246
+ Three environment variables control gate-cache.
247
+
248
+ | Variable | Effect | Default |
249
+ | ------------------------- | ------------------------------------------------------------------------------ | ------- |
250
+ | `GATE_CACHE_DISABLE` | If on, turns off caching. If `0` or `false`, keeps caching on when `CI` is on. | not set |
251
+ | `CI` | If on, turns off caching. A value in `GATE_CACHE_DISABLE` overrides it. | not set |
252
+ | `GATE_CACHE_MAX_AGE_DAYS` | gate-cache deletes markers older than this number of days. | `30` |
253
+
254
+ A variable is on when its value is not empty, `0`, or `false`. Case and spaces around the value do not matter.
255
+
256
+ Each marker is one small file in the `.git` directory of the repository, so all worktrees of the repository share the markers.
257
+
258
+ ## Learn more
259
+
260
+ - [How gate-cache works](docs/how-it-works.md) describes the exact rules for a skip, what a marker covers, and the marker format for tools that read markers directly.
261
+ - `gate-cache --help` gives the full command reference.
@@ -0,0 +1,225 @@
1
+ # gate-cache
2
+
3
+ Skip a command that already passed on the same code.
4
+
5
+ A lint or test command often runs many times on code that did not change. It runs when you finish the work, in a pre-commit hook, after a rebase that changed nothing, and in a pre-push hook. When an agent works in the repository, it runs the same command again. If the command takes minutes, the repeat runs take most of the time between finished code and a pushed branch.
6
+
7
+ gate-cache remembers which commands passed on which code. It wraps a command and gives it a name. When the command passes, gate-cache writes a marker for that name and for the exact content of the tracked files. The next time the same name runs on the same content, gate-cache finds the marker, skips the command, and exits 0 at once.
8
+
9
+ ```console
10
+ $ gate-cache --name pytest -- uv run pytest
11
+ ============================= 412 passed in 94.20s =============================
12
+
13
+ $ gate-cache --name pytest -- uv run pytest
14
+ gate-cache: pytest already passed on tree aaff74984ccc, skipping
15
+ ```
16
+
17
+ A change to a tracked file changes the content, so no marker matches and the command runs again. A CI runner that starts from a fresh clone has no markers, so it runs every command.
18
+
19
+ ## Install
20
+
21
+ gate-cache requires Python 3.11 or later, on macOS or Linux.
22
+
23
+ ```sh
24
+ uv tool install gate-cache
25
+ ```
26
+
27
+ The prek and pre-commit hooks install gate-cache themselves, so the hooks work without this step.
28
+
29
+ ## Run a command through gate-cache
30
+
31
+ The name goes after `--name`, and the command goes after `--`:
32
+
33
+ ```sh
34
+ gate-cache --name pytest -- uv run pytest
35
+ gate-cache --name ruff -- uv run ruff check .
36
+ ```
37
+
38
+ Each name has its own markers. A command run under one name never skips because of a marker for another name.
39
+
40
+ gate-cache exits with the exit code of the command, or with 0 when it skips the command. It writes a marker only when the command passes, so a command that failed runs again the next time.
41
+
42
+ When the working copy has uncommitted edits or files that git does not track, the content on disk is not a commit-ready snapshot. gate-cache then runs the command and writes no marker.
43
+
44
+ ## Run it as a hook
45
+
46
+ gate-cache works with [prek](https://github.com/j178/prek) and with [pre-commit](https://pre-commit.com) 3.2 or later. Each `gate-cache` hook takes the same arguments as the command line:
47
+
48
+ ```yaml
49
+ repos:
50
+ - repo: https://github.com/natelandau/gate-cache
51
+ rev: v0.1.0
52
+ hooks:
53
+ - id: gate-cache
54
+ name: pytest
55
+ args: [--name, pytest, --, uv, run, pytest]
56
+ files: ^(src/|tests/)
57
+ - id: gate-cache
58
+ name: ruff
59
+ args: [--name, ruff, --, uv, run, ruff, check, .]
60
+ ```
61
+
62
+ The hook runs at commit time and at push time. At commit time, gate-cache matches the content that you commit.
63
+
64
+ The hook runs in its own virtual environment, and the wrapped command inherits that environment. A command started with `uv run`, or a project command such as `just test`, runs in the project environment. `uv run` prints a warning that `VIRTUAL_ENV` does not match the project. The warning has no effect on the run.
65
+
66
+ Hooks, agents, and your terminal all read and write the same markers. When one of them runs `gate-cache --name pytest -- uv run pytest` and it passes, the others skip that name on the same content.
67
+
68
+ ## Examples
69
+
70
+ These examples show gate-cache inside the task runner of a project. When a recipe holds the wrapped command, everything that runs the recipe uses the same name and the same markers.
71
+
72
+ ### justfile
73
+
74
+ ```just
75
+ test:
76
+ gate-cache --name test -- uv run pytest
77
+
78
+ lint:
79
+ gate-cache --name lint -- uv run ruff check .
80
+ ```
81
+
82
+ After `just test` passes, the next `just test` on the same content skips pytest.
83
+
84
+ ### Recipes with arguments
85
+
86
+ gate-cache identifies a command by its name alone, not by its arguments. If `just test -k unit` and `just test` ran under the same name, a pass of the unit tests would also skip the full suite. Giving each set of arguments its own name, or caching only the bare form, keeps the two apart.
87
+
88
+ This recipe caches `just test` and runs `just test -k unit` without the cache:
89
+
90
+ ```just
91
+ test *args:
92
+ #!/usr/bin/env bash
93
+ set -euo pipefail
94
+ if [[ -z "{{ args }}" ]]; then
95
+ exec gate-cache --name test -- uv run pytest
96
+ fi
97
+ exec uv run pytest {{ args }}
98
+ ```
99
+
100
+ ### Makefile
101
+
102
+ ```make
103
+ test:
104
+ gate-cache --name test -- uv run pytest
105
+
106
+ lint:
107
+ gate-cache --name lint -- uv run ruff check .
108
+ ```
109
+
110
+ ### A hook that calls the recipe
111
+
112
+ A local hook can run the recipe instead of the gate-cache hook repository:
113
+
114
+ ```yaml
115
+ repos:
116
+ - repo: local
117
+ hooks:
118
+ - id: test
119
+ name: test
120
+ entry: just test
121
+ language: system
122
+ pass_filenames: false
123
+ stages: [pre-commit, pre-push]
124
+ ```
125
+
126
+ With this hook, a commit, a push, an agent, and your terminal all run `just test` and share its markers. The recipe runs in the project environment, so `uv run` prints no `VIRTUAL_ENV` warning. This form needs `gate-cache` and `just` on the `PATH`.
127
+
128
+ ### CI
129
+
130
+ gate-cache turns off caching when the `CI` variable is on. A variable is on when its value is not empty, `0`, or `false`. Case and spaces around the value do not matter. Many CI systems set `CI=true` in every job, for example GitHub Actions and GitLab CI/CD. Jenkins does not set `CI`.
131
+
132
+ When caching is off, gate-cache runs the command each time and writes no marker.
133
+
134
+ `GATE_CACHE_DISABLE` overrides `CI`. To keep caching on when `CI` is on, set `GATE_CACHE_DISABLE` to `0` or `false`:
135
+
136
+ ```sh
137
+ GATE_CACHE_DISABLE=0 just test
138
+ ```
139
+
140
+ To turn off caching when `CI` is not set, set `GATE_CACHE_DISABLE` to `1`:
141
+
142
+ ```sh
143
+ GATE_CACHE_DISABLE=1 just test
144
+ ```
145
+
146
+ ## See what already passed
147
+
148
+ `gate-cache status` lists the names that passed on the current content, and the names that ran on it without a pass:
149
+
150
+ ```console
151
+ $ gate-cache status
152
+ tree: aaff74984cccd156a469afa7d9ab10e4777beb24
153
+ passed: pytest
154
+ missed: ruff (exit code 1)
155
+ ```
156
+
157
+ A name is missed when its command failed, or when it exited 0 but changed the files. A later pass of the same name removes it from the `missed` line.
158
+
159
+ With `--name`, it reports on one name:
160
+
161
+ ```console
162
+ $ gate-cache status --name ruff
163
+ name: ruff
164
+ tree: aaff74984cccd156a469afa7d9ab10e4777beb24
165
+ marker: /path/to/repo/.git/gate-cache/7d94ca2f57ec0a77f60d328fb405d339fb0c2a645991b97c042ac54403104e9e
166
+ result: miss
167
+ last run: exit code 1
168
+ ```
169
+
170
+ The `last run` line shows only when the name ran on this content without a pass.
171
+
172
+ `status` runs nothing. With `--name`, it exits 0 when a marker matches and 1 when none does. Without `--name`, it exits 0 when at least one name passed. When gate-cache cannot match the content, for example because of uncommitted edits, the `tree` line gives the reason.
173
+
174
+ With `--json`, `status` writes one JSON object to stdout instead of text. The exit codes do not change:
175
+
176
+ ```console
177
+ $ gate-cache status --json
178
+ {
179
+ "enabled": true,
180
+ "tree": "aaff74984cccd156a469afa7d9ab10e4777beb24",
181
+ "reason": null,
182
+ "passed": ["pytest"],
183
+ "missed": [
184
+ {"name": "ruff", "exit_code": 1, "reason": "exit code 1", "timestamp": "2026-10-12T12:31:55Z"}
185
+ ]
186
+ }
187
+ ```
188
+
189
+ `reason` tells why gate-cache cannot match the content, and is `null` when it can. When caching is off, `enabled` is `false` and `reason` names the variable. With `--name`, the object has the `name`, `marker`, `result`, and `last_run` keys in place of `passed` and `missed`.
190
+
191
+ ## Start fresh
192
+
193
+ A marker records only the content of the tracked files. A new tool version, a changed environment variable, or a change to a service does not change that content, so the old markers still match.
194
+
195
+ `gate-cache clear` deletes every marker for the repository, and the records of missed runs:
196
+
197
+ ```console
198
+ $ gate-cache clear
199
+ gate-cache: removed 3 markers
200
+ ```
201
+
202
+ `GATE_CACHE_DISABLE=1` runs every command without the cache, for one command or for a whole session:
203
+
204
+ ```sh
205
+ GATE_CACHE_DISABLE=1 git push
206
+ ```
207
+
208
+ ## Settings
209
+
210
+ Three environment variables control gate-cache.
211
+
212
+ | Variable | Effect | Default |
213
+ | ------------------------- | ------------------------------------------------------------------------------ | ------- |
214
+ | `GATE_CACHE_DISABLE` | If on, turns off caching. If `0` or `false`, keeps caching on when `CI` is on. | not set |
215
+ | `CI` | If on, turns off caching. A value in `GATE_CACHE_DISABLE` overrides it. | not set |
216
+ | `GATE_CACHE_MAX_AGE_DAYS` | gate-cache deletes markers older than this number of days. | `30` |
217
+
218
+ A variable is on when its value is not empty, `0`, or `false`. Case and spaces around the value do not matter.
219
+
220
+ Each marker is one small file in the `.git` directory of the repository, so all worktrees of the repository share the markers.
221
+
222
+ ## Learn more
223
+
224
+ - [How gate-cache works](docs/how-it-works.md) describes the exact rules for a skip, what a marker covers, and the marker format for tools that read markers directly.
225
+ - `gate-cache --help` gives the full command reference.
@@ -13,7 +13,7 @@ description = "Skip lint and test gates that already passed on the exact same gi
13
13
  name = "gate-cache"
14
14
  readme = "README.md"
15
15
  requires-python = ">=3.11"
16
- version = "0.2.0"
16
+ version = "0.3.0"
17
17
 
18
18
  [[project.authors]]
19
19
  name = "Nate Landau"
@@ -36,6 +36,7 @@ module-name = "gate_cache"
36
36
  annotated_tag = true
37
37
  bump_message = "bump(release): v$current_version → v$new_version"
38
38
  changelog_merge_prerelease = true
39
+ major_version_zero = true
39
40
  tag_format = "v$version"
40
41
  update_changelog_on_bump = true
41
42
  version_files = ["src/gate_cache/__init__.py:__version__"]
@@ -12,7 +12,7 @@
12
12
  name = "gate-cache"
13
13
  readme = "README.md"
14
14
  requires-python = ">=3.11"
15
- version = "0.2.0"
15
+ version = "0.3.0"
16
16
 
17
17
  [project.scripts]
18
18
  gate-cache = "gate_cache.cli:main"
@@ -51,6 +51,7 @@
51
51
  annotated_tag = true
52
52
  bump_message = "bump(release): v$current_version → v$new_version"
53
53
  changelog_merge_prerelease = true
54
+ major_version_zero = true
54
55
  tag_format = "v$version"
55
56
  update_changelog_on_bump = true
56
57
  version_files = ["src/gate_cache/__init__.py:__version__"]
@@ -1,3 +1,3 @@
1
1
  """Skip lint and test gates that already passed on the exact same git tree."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.3.0"
@@ -30,10 +30,13 @@ Exit codes:
30
30
  run the command's own exit code, or 0 when a recorded pass lets it skip.
31
31
  Usage errors exit 2.
32
32
  status 0 when the named gate, or any gate without --name, passed on the
33
- current tree. 1 otherwise.
33
+ current tree. 1 otherwise. --json prints one JSON object instead.
34
34
  clear 0 on success, 1 on failure.
35
35
 
36
- Set GATE_CACHE_DISABLE to any non-empty value other than 0 to always run the command.
36
+ GATE_CACHE_DISABLE and CI are on when the value is not empty, 0, or false (any
37
+ case). If GATE_CACHE_DISABLE is on, the command always runs without the cache.
38
+ If it is 0 or false, caching stays on. If it is empty or not set, CI decides the
39
+ same way.
37
40
  Set GATE_CACHE_MAX_AGE_DAYS to a positive whole number to change how many days a
38
41
  recorded pass lasts (default 30).
39
42
 
@@ -51,7 +54,7 @@ app = App(
51
54
  help_format="plaintext",
52
55
  usage=(
53
56
  "Usage: gate-cache --name NAME -- COMMAND [ARGS...]\n"
54
- " gate-cache status [--name NAME]\n"
57
+ " gate-cache status [--name NAME] [--json]\n"
55
58
  " gate-cache clear"
56
59
  ),
57
60
  result_action="return_int_as_exit_code_else_zero",
@@ -71,8 +74,8 @@ def run(
71
74
 
72
75
  On a skip, prints one line to stderr and exits 0 without running anything. Otherwise
73
76
  runs the command and exits with its exit code. The command runs without caching
74
- (and nothing is recorded) when GATE_CACHE_DISABLE is set, the tree has unstaged
75
- changes or untracked files, or this is not a git repository. A pass is recorded only
77
+ (and nothing is recorded) when GATE_CACHE_DISABLE or CI turns caching off, the tree
78
+ has unstaged changes or untracked files, or this is not a git repository. A pass is recorded only
76
79
  when the command exits 0 and the run leaves the tree unchanged.
77
80
 
78
81
  Parameters
@@ -89,21 +92,28 @@ def run(
89
92
 
90
93
 
91
94
  @app.command
92
- def status(*, name: str | None = None) -> int:
95
+ def status(
96
+ *,
97
+ name: str | None = None,
98
+ as_json: Annotated[bool, Parameter(name="--json", negative="")] = False,
99
+ ) -> int:
93
100
  """Report whether gates would skip, without running anything.
94
101
 
95
102
  With --name, print the tree hash (or why it is unknown), the marker path,
96
- and hit or miss. Exit 0 on a hit, 1 otherwise.
103
+ hit or miss, and how the last run on this tree failed when it did. Exit 0 on a
104
+ hit, 1 otherwise.
97
105
 
98
- Without --name, list every gate that passed on the current tree.
99
- Exit 0 when at least one gate matches, 1 otherwise.
106
+ Without --name, list the gates that passed on the current tree and the gates
107
+ that ran on it without passing. Exit 0 when at least one gate passed, 1 otherwise.
100
108
 
101
109
  Parameters
102
110
  ----------
103
111
  name
104
- Gate name to look up. Omit it to list every gate that passed on this tree.
112
+ Gate name to look up. Omit it to list every gate that ran on this tree.
113
+ as_json
114
+ Print one JSON object to stdout instead of text. Exit codes are unchanged.
105
115
  """
106
- return gate_status(name)
116
+ return gate_status(name, as_json=as_json)
107
117
 
108
118
 
109
119
  @app.command