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.
- release_status-0.2.0/PKG-INFO +317 -0
- release_status-0.2.0/README.md +303 -0
- release_status-0.2.0/pyproject.toml +35 -0
- release_status-0.2.0/src/release_status/__init__.py +0 -0
- release_status-0.2.0/src/release_status/cache.py +53 -0
- release_status-0.2.0/src/release_status/cli.py +495 -0
- release_status-0.2.0/src/release_status/config.py +175 -0
- release_status-0.2.0/src/release_status/models.py +69 -0
- release_status-0.2.0/src/release_status/providers.py +366 -0
- release_status-0.2.0/src/release_status/resolvers.py +90 -0
- release_status-0.2.0/src/release_status/version.py +109 -0
- release_status-0.2.0/src/release_status/views.py +195 -0
|
@@ -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
|