release-status 0.2.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,317 @@
1
+ Metadata-Version: 2.3
2
+ Name: release-status
3
+ Version: 0.2.0
4
+ Summary: CLI tool to show release/deployment status across multiple projects
5
+ Author: Krzysztof Boduch
6
+ Author-email: Krzysztof Boduch <boduch.krzysztof@gmail.com>
7
+ Requires-Dist: typer>=0.15.0
8
+ Requires-Dist: rich>=13.0.0
9
+ Requires-Dist: pydantic>=2.10.0
10
+ Requires-Dist: jsonpath-ng>=1.6.0
11
+ Requires-Dist: requests>=2.32.0
12
+ Requires-Python: >=3.13
13
+ Description-Content-Type: text/markdown
14
+
15
+ # release-status
16
+
17
+ CLI tool that shows which commit is deployed to which environment across multiple projects.
18
+
19
+ Fetches commit history from GitHub/GitLab and checks public build endpoints to display two views:
20
+ - **Commits view** -- recent commits annotated with environment names where they're deployed
21
+ - **Environments view** -- each environment with its current deployed SHA and commit info
22
+
23
+ SHAs in the output are clickable links to the commit in the repository (in terminals that support OSC 8 hyperlinks).
24
+
25
+ ## Installation
26
+
27
+ Requires Python 3.13+.
28
+
29
+ ```bash
30
+ # Install globally via uv (recommended — isolated environment)
31
+ uv tool install release-status
32
+
33
+ # Or via pip
34
+ pip install release-status
35
+
36
+ # Update to latest
37
+ release-status update
38
+
39
+ # Or manual update
40
+ uv tool install release-status --force --reinstall
41
+ pip install --upgrade release-status
42
+
43
+ # Uninstall
44
+ uv tool uninstall release-status
45
+ pip uninstall release-status
46
+
47
+ # Or run from the cloned repo (development)
48
+ uv sync
49
+ uv run release-status --help
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ ```bash
55
+ # Create a starter config file (default: ~/.config/release-status/config.json)
56
+ release-status init
57
+
58
+ # Or specify a custom path
59
+ release-status --config ./my-config.json init
60
+
61
+ # List configured projects
62
+ release-status projects
63
+
64
+ # Validate config and check CLI tools / env vars
65
+ release-status check
66
+
67
+ # Show commits with environment deployment markers
68
+ release-status commits MyProject
69
+
70
+ # Project names with spaces need quotes
71
+ release-status commits "My App"
72
+
73
+ # Show environment status
74
+ release-status envs MyProject
75
+
76
+ # Print JSON Schema for config validation
77
+ release-status schema
78
+
79
+ # Clear cached data
80
+ release-status clear-cache
81
+ ```
82
+
83
+ ### Global flags
84
+
85
+ Place these **before** the subcommand:
86
+
87
+ ```bash
88
+ release-status --config path/to/config.json commits MyProject
89
+ release-status --no-cache envs MyProject
90
+ release-status --since 60 commits MyProject # look back 60 days instead of default 30
91
+ ```
92
+
93
+ | Flag | Description |
94
+ |------|-------------|
95
+ | `--config`, `-c` | Path to config file |
96
+ | `--no-cache` | Bypass cache for this run |
97
+ | `--since` | Override days to look back for commits (default from config) |
98
+ | `--branch`, `-b` | Override branch to fetch commits from |
99
+
100
+ ### Views
101
+
102
+ **`commits`** — recent commits with deployment markers:
103
+
104
+ | Column | Description |
105
+ |--------|-------------|
106
+ | SHA | Commit hash, clickable link to the commit in the repository |
107
+ | Date | Commit date |
108
+ | Author | Commit author |
109
+ | Message | First line of commit message |
110
+ | Deployed | Colored environment badges showing which environments have this commit deployed. Each badge is a clickable link to the environment's build URL |
111
+
112
+ If an environment has a fetch error, it's shown below the table.
113
+
114
+ **`envs`** — environment deployment status:
115
+
116
+ | Column | Description |
117
+ |--------|-------------|
118
+ | Environment | Colored badge with environment name, clickable link to the build URL |
119
+ | SHA | Deployed commit hash, clickable link to the commit in the repository |
120
+ | Status | `OK` if version was extracted successfully, `ERROR` if the fetch or extraction failed |
121
+ | Commit Info | Commit date and message when status is `OK`. Error details in red when status is `ERROR` |
122
+
123
+ If a deployed commit is not in the commit history (older than `since_days` or on a different branch), it's fetched individually. These commits are marked with `*` next to the SHA in both views, with a legend shown below the table.
124
+
125
+ Both views show a status line below the table with the `since_days` value and cache TTL.
126
+
127
+ ### Shell completion
128
+
129
+ Tab completion works for commands, options, and project names from your config.
130
+
131
+ ```bash
132
+ # Print the completion script (zsh/bash/fish)
133
+ release-status --show-completion
134
+
135
+ # Or install it directly into your shell profile
136
+ release-status --install-completion
137
+ ```
138
+
139
+ With `--show-completion` you can place the script wherever you prefer.
140
+ `--install-completion` auto-detects the right location (e.g. `~/.zfunc/` for zsh). Restart the terminal after installing.
141
+
142
+ ## Configuration
143
+
144
+ Default location: `~/.config/release-status/config.json`
145
+
146
+ Override with `--config <path>` or `RELEASE_STATUS_CONFIG` environment variable.
147
+
148
+ Repository URL should be the HTTPS clone URL (e.g. `https://github.com/org/repo.git`), not SSH.
149
+
150
+ ### Example
151
+
152
+ ```json
153
+ {
154
+ "cache_dir": "~/.cache/release-status",
155
+ "cache_ttl_minutes": 5,
156
+ "since_days": 180,
157
+ "projects": [
158
+ {
159
+ "name": "MyApp",
160
+ "repository": {
161
+ "url": "https://github.com/org/myapp.git",
162
+ "branch": "main",
163
+ "provider": { "type": "github-cli" }
164
+ },
165
+ "environments": [
166
+ {
167
+ "name": "dev",
168
+ "url": "https://dev.myapp.com/build.json",
169
+ "source": {
170
+ "type": "json",
171
+ "fields": { "version": "$.version" }
172
+ }
173
+ },
174
+ {
175
+ "name": "prod",
176
+ "url": "https://prod.myapp.com/build.json",
177
+ "source": {
178
+ "type": "json",
179
+ "fields": { "version": "$.version" }
180
+ }
181
+ }
182
+ ]
183
+ }
184
+ ]
185
+ }
186
+ ```
187
+
188
+ ### Repository providers
189
+
190
+ #### CLI providers
191
+
192
+ | Type | CLI tool | Auth |
193
+ |------|----------|------|
194
+ | `github-cli` | [`gh`](https://cli.github.com/) | Uses `gh`'s own auth session (`gh auth login`) |
195
+ | `gitlab-cli` | [`glab`](https://gitlab.com/gitlab-org/cli) | Uses `glab`'s own auth session (`glab auth login`) |
196
+
197
+ CLI providers call `gh api` / `glab api` under the hood to fetch commit history. Authentication is handled by the CLI tool itself — no tokens needed in config. Make sure you're logged in (`gh auth login` / `glab auth login`) and have access to the repository. Verify with `gh auth status` / `glab auth status`.
198
+
199
+ #### API providers
200
+
201
+ | Type | Token scope | How to create |
202
+ |------|-------------|---------------|
203
+ | `github-api` | `repo` | https://github.com/settings/tokens → Generate new token (classic) |
204
+ | `gitlab-api` | `read_api` | https://gitlab.com/-/user_settings/personal_access_tokens → Add new token |
205
+
206
+ API providers reference an environment variable name (not the token itself):
207
+
208
+ ```json
209
+ {
210
+ "type": "gitlab-api",
211
+ "token_env": "MY_GITLAB_TOKEN"
212
+ }
213
+ ```
214
+
215
+ Set the token in your shell profile:
216
+
217
+ ```bash
218
+ export MY_GITLAB_TOKEN="glpat-xxxxxxxxxxxx"
219
+ ```
220
+
221
+ Note: avoid using `GITLAB_TOKEN` as the env var name — `glab` CLI reads it and it will override `glab`'s own auth session.
222
+
223
+ ### Environment sources
224
+
225
+ Each environment has a public URL that returns the deployed commit SHA. Two extraction methods:
226
+
227
+ **JSON** -- extract via JSONPath.
228
+
229
+ Example response from `https://dev.example.com/build.json`:
230
+ ```json
231
+ {"version": "abc1234", "build": 42}
232
+ ```
233
+
234
+ Source config:
235
+ ```json
236
+ {
237
+ "type": "json",
238
+ "fields": { "version": "$.version" }
239
+ }
240
+ ```
241
+
242
+ **Regex** -- extract via named groups from HTML/text.
243
+
244
+ Example response from `https://dev.example.com/build.html`:
245
+ ```
246
+ some_data 2026-04-07T10:00:00Z
247
+ abc1234 2026-04-07T10:05:00Z
248
+ ```
249
+
250
+ Source config:
251
+ ```json
252
+ {
253
+ "type": "regex",
254
+ "pattern": "(.*)\\t(?P<commit_time>.*)\\n(?P<version>.*)\\t(?P<pipeline_time>.*)",
255
+ "fields": {
256
+ "version": "version",
257
+ "pipeline_time": "pipeline_time"
258
+ }
259
+ }
260
+ ```
261
+
262
+ The `fields` map is `{ our_field_name: extraction_path }`:
263
+ - For JSON sources: values are JSONPath expressions (e.g. `$.version`)
264
+ - For regex sources: values are regex named group names (e.g. `version`)
265
+
266
+ At minimum, `fields` must contain `"version"` (the commit SHA). You can add extra fields like `pipeline_time` -- every field you configure must be found in the response, otherwise the tool will show an extraction error for that environment.
267
+
268
+ ### Caching
269
+
270
+ Responses are cached for 5 minutes by default. Use `--no-cache` to bypass or `release-status clear-cache` to wipe.
271
+
272
+ Both cache directory and TTL are configurable. Set `cache_ttl_minutes` to `0` to disable caching permanently:
273
+
274
+ ```json
275
+ {
276
+ "cache_dir": "/tmp/my-release-cache",
277
+ "cache_ttl_minutes": 5,
278
+ "since_days": 180,
279
+ "projects": [...]
280
+ }
281
+ ```
282
+
283
+ ## Development
284
+
285
+ ```bash
286
+ uv sync
287
+ uv run pytest -v
288
+ uv run mypy src/
289
+ uv run release-status --config config.example.json check
290
+ ```
291
+
292
+ After making changes, reinstall the global tool with a fresh build:
293
+
294
+ ```bash
295
+ uv tool install . --force --reinstall
296
+ ```
297
+
298
+ `--force` alone may use a cached wheel. `--reinstall` forces a rebuild.
299
+
300
+ ## Releasing
301
+
302
+ Releases are automated via GitHub Actions. To publish a new version:
303
+
304
+ ```bash
305
+ gh workflow run release.yml -f version=1.2.0
306
+ ```
307
+
308
+ Or trigger "Release" workflow from the GitHub Actions UI with the version number.
309
+
310
+ The workflow will:
311
+ 1. Validate the version format (semver)
312
+ 2. Check that the new version is newer than the current one in `pyproject.toml`
313
+ 3. Bump the version in `pyproject.toml`, commit, and tag
314
+ 4. Build and publish to PyPI
315
+ 5. Create a GitHub Release with auto-generated notes
316
+
317
+ **Prerequisites:** `PYPI_TOKEN` secret must be configured in the repository settings.
@@ -0,0 +1,303 @@
1
+ # release-status
2
+
3
+ CLI tool that shows which commit is deployed to which environment across multiple projects.
4
+
5
+ Fetches commit history from GitHub/GitLab and checks public build endpoints to display two views:
6
+ - **Commits view** -- recent commits annotated with environment names where they're deployed
7
+ - **Environments view** -- each environment with its current deployed SHA and commit info
8
+
9
+ SHAs in the output are clickable links to the commit in the repository (in terminals that support OSC 8 hyperlinks).
10
+
11
+ ## Installation
12
+
13
+ Requires Python 3.13+.
14
+
15
+ ```bash
16
+ # Install globally via uv (recommended — isolated environment)
17
+ uv tool install release-status
18
+
19
+ # Or via pip
20
+ pip install release-status
21
+
22
+ # Update to latest
23
+ release-status update
24
+
25
+ # Or manual update
26
+ uv tool install release-status --force --reinstall
27
+ pip install --upgrade release-status
28
+
29
+ # Uninstall
30
+ uv tool uninstall release-status
31
+ pip uninstall release-status
32
+
33
+ # Or run from the cloned repo (development)
34
+ uv sync
35
+ uv run release-status --help
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ```bash
41
+ # Create a starter config file (default: ~/.config/release-status/config.json)
42
+ release-status init
43
+
44
+ # Or specify a custom path
45
+ release-status --config ./my-config.json init
46
+
47
+ # List configured projects
48
+ release-status projects
49
+
50
+ # Validate config and check CLI tools / env vars
51
+ release-status check
52
+
53
+ # Show commits with environment deployment markers
54
+ release-status commits MyProject
55
+
56
+ # Project names with spaces need quotes
57
+ release-status commits "My App"
58
+
59
+ # Show environment status
60
+ release-status envs MyProject
61
+
62
+ # Print JSON Schema for config validation
63
+ release-status schema
64
+
65
+ # Clear cached data
66
+ release-status clear-cache
67
+ ```
68
+
69
+ ### Global flags
70
+
71
+ Place these **before** the subcommand:
72
+
73
+ ```bash
74
+ release-status --config path/to/config.json commits MyProject
75
+ release-status --no-cache envs MyProject
76
+ release-status --since 60 commits MyProject # look back 60 days instead of default 30
77
+ ```
78
+
79
+ | Flag | Description |
80
+ |------|-------------|
81
+ | `--config`, `-c` | Path to config file |
82
+ | `--no-cache` | Bypass cache for this run |
83
+ | `--since` | Override days to look back for commits (default from config) |
84
+ | `--branch`, `-b` | Override branch to fetch commits from |
85
+
86
+ ### Views
87
+
88
+ **`commits`** — recent commits with deployment markers:
89
+
90
+ | Column | Description |
91
+ |--------|-------------|
92
+ | SHA | Commit hash, clickable link to the commit in the repository |
93
+ | Date | Commit date |
94
+ | Author | Commit author |
95
+ | Message | First line of commit message |
96
+ | Deployed | Colored environment badges showing which environments have this commit deployed. Each badge is a clickable link to the environment's build URL |
97
+
98
+ If an environment has a fetch error, it's shown below the table.
99
+
100
+ **`envs`** — environment deployment status:
101
+
102
+ | Column | Description |
103
+ |--------|-------------|
104
+ | Environment | Colored badge with environment name, clickable link to the build URL |
105
+ | SHA | Deployed commit hash, clickable link to the commit in the repository |
106
+ | Status | `OK` if version was extracted successfully, `ERROR` if the fetch or extraction failed |
107
+ | Commit Info | Commit date and message when status is `OK`. Error details in red when status is `ERROR` |
108
+
109
+ If a deployed commit is not in the commit history (older than `since_days` or on a different branch), it's fetched individually. These commits are marked with `*` next to the SHA in both views, with a legend shown below the table.
110
+
111
+ Both views show a status line below the table with the `since_days` value and cache TTL.
112
+
113
+ ### Shell completion
114
+
115
+ Tab completion works for commands, options, and project names from your config.
116
+
117
+ ```bash
118
+ # Print the completion script (zsh/bash/fish)
119
+ release-status --show-completion
120
+
121
+ # Or install it directly into your shell profile
122
+ release-status --install-completion
123
+ ```
124
+
125
+ With `--show-completion` you can place the script wherever you prefer.
126
+ `--install-completion` auto-detects the right location (e.g. `~/.zfunc/` for zsh). Restart the terminal after installing.
127
+
128
+ ## Configuration
129
+
130
+ Default location: `~/.config/release-status/config.json`
131
+
132
+ Override with `--config <path>` or `RELEASE_STATUS_CONFIG` environment variable.
133
+
134
+ Repository URL should be the HTTPS clone URL (e.g. `https://github.com/org/repo.git`), not SSH.
135
+
136
+ ### Example
137
+
138
+ ```json
139
+ {
140
+ "cache_dir": "~/.cache/release-status",
141
+ "cache_ttl_minutes": 5,
142
+ "since_days": 180,
143
+ "projects": [
144
+ {
145
+ "name": "MyApp",
146
+ "repository": {
147
+ "url": "https://github.com/org/myapp.git",
148
+ "branch": "main",
149
+ "provider": { "type": "github-cli" }
150
+ },
151
+ "environments": [
152
+ {
153
+ "name": "dev",
154
+ "url": "https://dev.myapp.com/build.json",
155
+ "source": {
156
+ "type": "json",
157
+ "fields": { "version": "$.version" }
158
+ }
159
+ },
160
+ {
161
+ "name": "prod",
162
+ "url": "https://prod.myapp.com/build.json",
163
+ "source": {
164
+ "type": "json",
165
+ "fields": { "version": "$.version" }
166
+ }
167
+ }
168
+ ]
169
+ }
170
+ ]
171
+ }
172
+ ```
173
+
174
+ ### Repository providers
175
+
176
+ #### CLI providers
177
+
178
+ | Type | CLI tool | Auth |
179
+ |------|----------|------|
180
+ | `github-cli` | [`gh`](https://cli.github.com/) | Uses `gh`'s own auth session (`gh auth login`) |
181
+ | `gitlab-cli` | [`glab`](https://gitlab.com/gitlab-org/cli) | Uses `glab`'s own auth session (`glab auth login`) |
182
+
183
+ CLI providers call `gh api` / `glab api` under the hood to fetch commit history. Authentication is handled by the CLI tool itself — no tokens needed in config. Make sure you're logged in (`gh auth login` / `glab auth login`) and have access to the repository. Verify with `gh auth status` / `glab auth status`.
184
+
185
+ #### API providers
186
+
187
+ | Type | Token scope | How to create |
188
+ |------|-------------|---------------|
189
+ | `github-api` | `repo` | https://github.com/settings/tokens → Generate new token (classic) |
190
+ | `gitlab-api` | `read_api` | https://gitlab.com/-/user_settings/personal_access_tokens → Add new token |
191
+
192
+ API providers reference an environment variable name (not the token itself):
193
+
194
+ ```json
195
+ {
196
+ "type": "gitlab-api",
197
+ "token_env": "MY_GITLAB_TOKEN"
198
+ }
199
+ ```
200
+
201
+ Set the token in your shell profile:
202
+
203
+ ```bash
204
+ export MY_GITLAB_TOKEN="glpat-xxxxxxxxxxxx"
205
+ ```
206
+
207
+ Note: avoid using `GITLAB_TOKEN` as the env var name — `glab` CLI reads it and it will override `glab`'s own auth session.
208
+
209
+ ### Environment sources
210
+
211
+ Each environment has a public URL that returns the deployed commit SHA. Two extraction methods:
212
+
213
+ **JSON** -- extract via JSONPath.
214
+
215
+ Example response from `https://dev.example.com/build.json`:
216
+ ```json
217
+ {"version": "abc1234", "build": 42}
218
+ ```
219
+
220
+ Source config:
221
+ ```json
222
+ {
223
+ "type": "json",
224
+ "fields": { "version": "$.version" }
225
+ }
226
+ ```
227
+
228
+ **Regex** -- extract via named groups from HTML/text.
229
+
230
+ Example response from `https://dev.example.com/build.html`:
231
+ ```
232
+ some_data 2026-04-07T10:00:00Z
233
+ abc1234 2026-04-07T10:05:00Z
234
+ ```
235
+
236
+ Source config:
237
+ ```json
238
+ {
239
+ "type": "regex",
240
+ "pattern": "(.*)\\t(?P<commit_time>.*)\\n(?P<version>.*)\\t(?P<pipeline_time>.*)",
241
+ "fields": {
242
+ "version": "version",
243
+ "pipeline_time": "pipeline_time"
244
+ }
245
+ }
246
+ ```
247
+
248
+ The `fields` map is `{ our_field_name: extraction_path }`:
249
+ - For JSON sources: values are JSONPath expressions (e.g. `$.version`)
250
+ - For regex sources: values are regex named group names (e.g. `version`)
251
+
252
+ At minimum, `fields` must contain `"version"` (the commit SHA). You can add extra fields like `pipeline_time` -- every field you configure must be found in the response, otherwise the tool will show an extraction error for that environment.
253
+
254
+ ### Caching
255
+
256
+ Responses are cached for 5 minutes by default. Use `--no-cache` to bypass or `release-status clear-cache` to wipe.
257
+
258
+ Both cache directory and TTL are configurable. Set `cache_ttl_minutes` to `0` to disable caching permanently:
259
+
260
+ ```json
261
+ {
262
+ "cache_dir": "/tmp/my-release-cache",
263
+ "cache_ttl_minutes": 5,
264
+ "since_days": 180,
265
+ "projects": [...]
266
+ }
267
+ ```
268
+
269
+ ## Development
270
+
271
+ ```bash
272
+ uv sync
273
+ uv run pytest -v
274
+ uv run mypy src/
275
+ uv run release-status --config config.example.json check
276
+ ```
277
+
278
+ After making changes, reinstall the global tool with a fresh build:
279
+
280
+ ```bash
281
+ uv tool install . --force --reinstall
282
+ ```
283
+
284
+ `--force` alone may use a cached wheel. `--reinstall` forces a rebuild.
285
+
286
+ ## Releasing
287
+
288
+ Releases are automated via GitHub Actions. To publish a new version:
289
+
290
+ ```bash
291
+ gh workflow run release.yml -f version=1.2.0
292
+ ```
293
+
294
+ Or trigger "Release" workflow from the GitHub Actions UI with the version number.
295
+
296
+ The workflow will:
297
+ 1. Validate the version format (semver)
298
+ 2. Check that the new version is newer than the current one in `pyproject.toml`
299
+ 3. Bump the version in `pyproject.toml`, commit, and tag
300
+ 4. Build and publish to PyPI
301
+ 5. Create a GitHub Release with auto-generated notes
302
+
303
+ **Prerequisites:** `PYPI_TOKEN` secret must be configured in the repository settings.
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "release-status"
3
+ version = "0.2.0"
4
+ description = "CLI tool to show release/deployment status across multiple projects"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Krzysztof Boduch", email = "boduch.krzysztof@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.13"
10
+ dependencies = [
11
+ "typer>=0.15.0",
12
+ "rich>=13.0.0",
13
+ "pydantic>=2.10.0",
14
+ "jsonpath-ng>=1.6.0",
15
+ "requests>=2.32.0",
16
+ ]
17
+
18
+ [project.scripts]
19
+ release-status = "release_status.cli:app"
20
+
21
+ [dependency-groups]
22
+ dev = [
23
+ "pytest>=8.0.0",
24
+ "pytest-mock>=3.14.0",
25
+ "responses>=0.25.0",
26
+ "mypy>=1.14.0",
27
+ "types-requests>=2.32.0",
28
+ ]
29
+
30
+ [tool.mypy]
31
+ strict = true
32
+
33
+ [build-system]
34
+ requires = ["uv_build>=0.8.17,<0.9.0"]
35
+ build-backend = "uv_build"
File without changes
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ from datetime import datetime, timedelta, timezone
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ class Cache:
10
+ def __init__(
11
+ self,
12
+ cache_dir: Path,
13
+ ttl: timedelta,
14
+ ) -> None:
15
+ self.cache_dir = cache_dir
16
+ self.ttl = ttl
17
+ self.enabled = True
18
+
19
+ def _key_path(self, key: str) -> Path:
20
+ h = hashlib.sha256(key.encode()).hexdigest()[:16]
21
+ return self.cache_dir / f"{h}.json"
22
+
23
+ def get(self, key: str) -> Any | None:
24
+ if not self.enabled:
25
+ return None
26
+ path = self._key_path(key)
27
+ if not path.exists():
28
+ return None
29
+ entry = json.loads(path.read_text())
30
+ cached_at = datetime.fromisoformat(entry["cached_at"])
31
+ if datetime.now(timezone.utc) - cached_at > self.ttl:
32
+ path.unlink()
33
+ return None
34
+ return entry["data"]
35
+
36
+ def set(self, key: str, data: Any) -> None:
37
+ if not self.enabled:
38
+ return
39
+ self.cache_dir.mkdir(parents=True, exist_ok=True)
40
+ entry = {
41
+ "cached_at": datetime.now(timezone.utc).isoformat(),
42
+ "data": data,
43
+ }
44
+ self._key_path(key).write_text(json.dumps(entry))
45
+
46
+ def clear(self) -> int:
47
+ if not self.cache_dir.exists():
48
+ return 0
49
+ count = 0
50
+ for f in self.cache_dir.glob("*.json"):
51
+ f.unlink()
52
+ count += 1
53
+ return count