feat: release pipeline + dynamic version from git tag #8

Merged
bjoern merged 2 commits from feature/release-pipeline into main 2026-06-28 20:48:34 +02:00
Member

Release pipeline + dynamic version from git tag

How it works

  1. You create a git tag (e.g. v0.2.0) and publish a release in Forgejo
  2. The release.yml workflow fires on release:published
  3. It extracts the version from the tag name (v0.2.00.2.0)
  4. Patches __init__.py with the tagged version
  5. Builds a wheel via python -m build
  6. Verifies the built wheel actually reports the right version (zai-tray-checker --version)
  7. Attaches the .whl file to the release

Version handling

  • pyproject.toml now uses dynamic = ["version"] instead of a hardcoded string
  • Version source: zai_tray_checker.__init__.__version__ via [tool.setuptools.dynamic]
  • The release pipeline patches __init__.py before building so the wheel carries the tag version
  • For dev/source installs, it falls back to the 0.1.0 in __init__.py

CLI --version

$ zai-tray-checker --version
zai-tray-checker 0.1.0

Runs before QApplication initializes, so it works headless. Also supports -V.

To create a release

git tag v0.2.0
git push origin v0.2.0
# Then create a release in Forgejo UI pointing to the tag

The pipeline will build the wheel and attach it automatically.

Tests: 69 total, all passing

3 new tests in test_version.py:

  • Version string exists and is semver-like
  • --version flag prints correct version
  • -V shortcut works
## Release pipeline + dynamic version from git tag ### How it works 1. You create a git tag (e.g. `v0.2.0`) and publish a release in Forgejo 2. The `release.yml` workflow fires on `release:published` 3. It extracts the version from the tag name (`v0.2.0` → `0.2.0`) 4. Patches `__init__.py` with the tagged version 5. Builds a wheel via `python -m build` 6. Verifies the built wheel actually reports the right version (`zai-tray-checker --version`) 7. Attaches the `.whl` file to the release ### Version handling - `pyproject.toml` now uses `dynamic = ["version"]` instead of a hardcoded string - Version source: `zai_tray_checker.__init__.__version__` via `[tool.setuptools.dynamic]` - The release pipeline patches `__init__.py` before building so the wheel carries the tag version - For dev/source installs, it falls back to the `0.1.0` in `__init__.py` ### CLI `--version` ```bash $ zai-tray-checker --version zai-tray-checker 0.1.0 ``` Runs before `QApplication` initializes, so it works headless. Also supports `-V`. ### To create a release ```bash git tag v0.2.0 git push origin v0.2.0 # Then create a release in Forgejo UI pointing to the tag ``` The pipeline will build the wheel and attach it automatically. ### Tests: 69 total, all passing 3 new tests in `test_version.py`: - Version string exists and is semver-like - `--version` flag prints correct version - `-V` shortcut works
feat: release pipeline + dynamic version from git tag
All checks were successful
CI / test (pull_request) Successful in 21s
152aa848ad
Release workflow (.forgejo/workflows/release.yml):
- Triggers on release:published
- Extracts version from tag name (strips leading 'v')
- Patches __init__.py with the tagged version
- Builds wheel via python -m build
- Verifies the built wheel with --version
- Attaches .whl to the release via Gitea API

Dynamic version (pyproject.toml):
- version = dynamic, read from __init__.__version__ via setuptools
- Release pipeline patches __init__.py before building
- No more hardcoded version in pyproject.toml

CLI --version flag:
- zai-tray-checker --version prints 'zai-tray-checker X.Y.Z'
- Also supports -V shortcut
- Runs before QApplication init (no display needed)

3 new tests (test_version.py):
- Version string exists and is semver-like
- --version flag prints correct version
- -V shortcut works

69 tests total, all passing.
Author
Member

🤖 Hermes automated review: minor comments

Reviewed the full diff of feature/release-pipeline (152aa84) against main (fa69758) — 6 files, +124/-1. The release pipeline + dynamic version design is sound: tag → sed patch __init__.pypython -m build --wheel → verify --version → attach asset. The CLI --version/-V handling runs before QApplication initializes (correct, headless-safe). pyproject.toml dynamic-version wiring via [tool.setuptools.dynamic] is correct.

Local checks: pytest -q69 passed in 0.74s (including the 3 new test_version.py cases). No CI result was posted yet for this head SHA at review time (PR is minutes old), so tests were run locally per the CI-absent policy.

Minor (non-blocking) findings

  • release.yml:30sed with unescaped $VERSION in replacement. VERSION="${TAG#v}" is interpolated into a sed regex/replacement. Since only [^/]+-style git tags are expected this is fine in practice, but a tag containing /, &, or shell metacharacters could break the substitution or corrupt __init__.py. Low risk (requires push/tag rights). Optional hardening: validate VERSION against a semver regex (^[0-9]+\.[0-9]+\.[0-9]+) before the sed, or use a Python one-liner (python -c "...") to rewrite the line instead of sed.

  • release.yml:38python -m build --wheel --no-isolation. --no-isolation requires setuptools and wheel to already be present in the venv. pip install build does pull setuptools as a dependency, so this almost certainly works on a default runner image — but if the runner's base image ever ships a stripped-down pip, the build step would fail with an opaque error. Consider dropping --no-isolation (it adds a few seconds but is more robust), or explicitly pip install wheel setuptools in the setup step.

  • main.py:773 — substring-based flag check. "-V" in sys.argv would false-positive if -V ever appeared as a substring of another token. Harmless for this single-purpose CLI; flagging only for awareness if richer argument parsing is added later.

No issues found

  • No hardcoded secrets; ${GH_TOKEN} is used correctly in the curl Authorization header.
  • No shell injection on untrusted user input (tag/release IDs come from authenticated Forgejo events).
  • No logic errors, race conditions, or data-loss risks.
  • Tests exist and pass for the new functionality.

Note: This is a PR conversation comment, not a formal Forgejo review approval — the current MCP integration cannot create review approval states or inline diff comments.

Automated daily review. I never merge PRs.

## 🤖 Hermes automated review: minor comments Reviewed the full diff of `feature/release-pipeline` (152aa84) against `main` (fa69758) — 6 files, +124/-1. The release pipeline + dynamic version design is sound: tag → `sed` patch `__init__.py` → `python -m build --wheel` → verify `--version` → attach asset. The CLI `--version`/`-V` handling runs before `QApplication` initializes (correct, headless-safe). `pyproject.toml` dynamic-version wiring via `[tool.setuptools.dynamic]` is correct. **Local checks:** `pytest -q` → **69 passed in 0.74s** (including the 3 new `test_version.py` cases). No CI result was posted yet for this head SHA at review time (PR is minutes old), so tests were run locally per the CI-absent policy. ### Minor (non-blocking) findings - **`release.yml:30` — `sed` with unescaped `$VERSION` in replacement.** `VERSION="${TAG#v}"` is interpolated into a `sed` regex/replacement. Since only `[^/]+`-style git tags are expected this is fine in practice, but a tag containing `/`, `&`, or shell metacharacters could break the substitution or corrupt `__init__.py`. Low risk (requires push/tag rights). Optional hardening: validate `VERSION` against a semver regex (`^[0-9]+\.[0-9]+\.[0-9]+`) before the `sed`, or use a Python one-liner (`python -c "..."`) to rewrite the line instead of `sed`. - **`release.yml:38` — `python -m build --wheel --no-isolation`.** `--no-isolation` requires `setuptools` and `wheel` to already be present in the venv. `pip install build` does pull `setuptools` as a dependency, so this almost certainly works on a default runner image — but if the runner's base image ever ships a stripped-down `pip`, the build step would fail with an opaque error. Consider dropping `--no-isolation` (it adds a few seconds but is more robust), or explicitly `pip install wheel setuptools` in the setup step. - **`main.py:773` — substring-based flag check.** `"-V" in sys.argv` would false-positive if `-V` ever appeared as a substring of another token. Harmless for this single-purpose CLI; flagging only for awareness if richer argument parsing is added later. ### No issues found - No hardcoded secrets; `${GH_TOKEN}` is used correctly in the `curl` `Authorization` header. - No shell injection on untrusted user input (tag/release IDs come from authenticated Forgejo events). - No logic errors, race conditions, or data-loss risks. - Tests exist and pass for the new functionality. **Note:** This is a PR conversation comment, not a formal Forgejo review approval — the current MCP integration cannot create review approval states or inline diff comments. *Automated daily review. I never merge PRs.*
fix: harden release pipeline per review feedback
All checks were successful
CI / test (pull_request) Successful in 21s
c43058e4b9
- Replace sed with Python one-liner for __version__ patching
  (avoids metachar injection from tag names)
- Add semver validation: tag must match X.Y.Z or pipeline fails
- Drop --no-isolation from build (more robust on different runners)
- Explicitly install wheel + setuptools in venv
bjoern merged commit bf5841310b into main 2026-06-28 20:48:34 +02:00
bjoern deleted branch feature/release-pipeline 2026-06-28 20:48:34 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
TeamAI/zai-tray-checker!8
No description provided.