Every merge to master previously produced three separate commits (bump
version -> update changelog, tagged here -> update README badges), so
the published tag never carried the current release's own README
badges, and the badge commit leaked into the next release's changelog
since no cliff.toml parser skipped it.
- Extract the bump/changelog/badge assembly into
.gitea/scripts/release-commit.sh, used by both the merge-to-master
path and the hand-pushed-tag sync path, so every tag now points at
one complete "chore: release vX.Y.Z" commit. Push the commit and its
tag atomically.
- sync-version-on-tag now refuses to touch a tag whose commit isn't
reachable from master (rather than silently rewriting an unreviewed
tree), and builds a proper release commit via the same script when
it does need to correct a hand-pushed tag's version.
- publish-package now depends only on sync-version-on-tag: since a tag
can only pass that guard if its commit is already on master, and
master is always fully checked, re-running the lint/type/test matrix
on tag pushes was redundant.
- Factor the repeated checkout/setup-uv/env/sync steps into a local
composite action (.gitea/actions/setup), fix `test`'s `needs` to
include ruff-format, and bump actions/upload-artifact to v4.
- cliff.toml: skip "chore: release ..." commits from the changelog.
Verified by dry-running release-commit.sh against a scratch worktree
for all three code paths (patch bump, --no-bump, explicit VERSION
sync), confirming idempotency and that the resulting commit carries
pyproject.toml, .bumpversion.toml, uv.lock, CHANGELOG.md and README.md
together.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnotyatakKNS4NLjDbfYw1
Rewrite README.md from scratch as a scannable landing page (hero, one
mermaid pipeline diagram, quick start, deep detail folded into
collapsible sections) instead of the old flat prose dump duplicating
CLAUDE.md.
Swap the static "CI" badge for a live Gitea Actions status badge, and
add an update-badges job to ci.yml that recomputes the version and
test-count badges on every push to master and pushes an update only
when they actually changed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JL1hFbhLv5uwjTXqkWTLnH
A bare "pull_request:" key parses to null in YAML, which the runner
apparently doesn't treat as "trigger with defaults" the way an empty
mapping does — the open PR for this branch got no CI run at all.
- Drop [skip ci] from the bump-version/changelog/tag-sync commits so
master's tip always has a check run instead of only the merge commit.
- Filter those chore commits out of the changelog via message pattern
instead of the now-removed [skip ci] tag.
- Only run CI on push to master (plus tags); pull requests to any branch
still run the full suite.
- Add a publish-package job that builds and publishes to the Gitea PyPI
registry on every tag push, after tests and version sync pass.
The prior e2e run (task 1348) failed on the bump-version job's push step
with a 403 Forbidden — CI_TOKEN lacked write access. Note this on the
checkout step so the requirement isn't lost, now that the token has been
rescoped. Trivial commit to re-open a merge request and re-run the job
end to end.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Version bumps and release tags were entirely manual; the only CI automation
was sync-version-on-tag, which corrects pyproject.toml if a hand-pushed tag
drifted. This flips that: a new bump-version job (needs the four existing
checks, gated to actual merge commits on master via HEAD^@'s parent count so
direct/squash/rebase pushes are untouched) uses bump-my-version to auto-bump
the patch version when a merged branch didn't already bump it itself, then
generates a changelog entry with git-cliff and pushes a matching vX.Y.Z tag.
git-cliff's cliff.toml is tuned to this repo's plain imperative commit style
(no feat:/fix: prefixes): commits are grouped Added/Fixed/Removed/Changed by
leading verb, "(gitea #N)" is linkified, and merge/[skip ci] commits are
dropped. Per user decision during planning: the changelog generator folds in
@lars's comment on the issue (asking to fold in changelog generation rather
than deferring it), and CHANGELOG.md starts fresh with no backfill of
v0.2.0-v0.3.3.
sync-version-on-tag is left untouched as the safety net for hand-tagging.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
v4 requires the @actions/artifact v2 backend, which this self-hosted Gitea
instance doesn't support yet (GHESNotSupportedError) — v3 uses the older
API Gitea's Actions runner implements.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Test job now reports coverage (term + xml) and uploads it as a build
artifact, so coverage regressions are visible per-PR.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The bind-mounted uv cache and the job workspace are on different
filesystems, so uv already falls back to copying installed files; this just
tells it to do so directly instead of logging a hardlink-failed warning
every job.
Log evidence showed setup-uv still sets UV_CACHE_DIR to a tool-cache tmp path
(/tmp/setup-uv-cache) even with enable-cache: false, clobbering the workflow
env pointing at the bind-mounted cache. Re-export it via GITHUB_ENV in a step
right after setup-uv so it wins for the rest of the job.
setup-uv's default enable-cache: auto sets its own UV_CACHE_DIR (a tool-cache
tmp path) as a later step, clobbering the workflow-level UV_CACHE_DIR that
points at the bind-mounted persistent cache. Disable setup-uv's own cache
handling so our mount stays in effect.
The self-hosted act_runner doesn't support upload/download-artifact, so drop
that plumbing and instead run jobs in an explicit container with a persistent
host directory mounted at /uv-cache (UV_CACHE_DIR), backed by valid_volumes on
the runner. uv sync still runs per job but hits a warm local cache instead of
re-downloading/building packages every time.
Uploads the synced .venv as an artifact from a single setup job instead of
re-running uv sync (and re-downloading torch) in every job. Drops the
build/publish job in favor of a lighter job that, on a pushed tag, checks the
tag against the uv project version and — if they differ — bumps the version,
commits it to master, and recreates the tag on the new commit.
ruff check, ruff format check, ty check, and pytest now run as four
independent jobs instead of one sequential lint job gating test —
faster wall-clock CI since none of these checks depend on each other.
build still waits on all four before bumping/publishing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Unquoted on: gets parsed as the YAML 1.1 boolean true instead of the
string "on", so Gitea couldn't find a trigger key and didn't register
the workflow at all.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three-stage workflow: lint/format/type-check (ruff, ty), pytest,
and (master pushes only) a version-bump + uv build + publish to the
Gitea package registry. Bump commit is tagged [skip ci] to avoid
retriggering the workflow.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>