Skip to content

fix(release): fold refreshed lockfile into the version-bump commit#15

Merged
MAfarrag merged 4 commits into
mainfrom
fix/fold-lockfile-into-release-commit
Jul 19, 2026
Merged

fix(release): fold refreshed lockfile into the version-bump commit#15
MAfarrag merged 4 commits into
mainfrom
fix/fold-lockfile-into-release-commit

Conversation

@MAfarrag

@MAfarrag MAfarrag commented Jul 19, 2026

Copy link
Copy Markdown
Member

Description

cz bump commits the version bump and creates the tag atomically, but uv/pixi lockfiles can only be
regenerated after the new version is written to pyproject.toml. So the tag has always pointed at a commit whose
lockfile still pins the pre-bump versions. Consumers work around this with a follow-up commit that syncs the lock
past the tag, which leaves the tagged tree stale: git checkout <tag> && uv sync --locked mismatches (especially
in uv workspaces with many == cross-pins).

This PR folds the refreshed lockfile into the version-bump commit before anything is pushed, so the tag itself
contains a consistent lock — no follow-up commit, no force-push of published refs.

What the fix does (new step in actions/release/github/action.yml, between the bump and the push):

  1. cd to the repo root (workspaces keep the lock at the top even when WORK_DIR is a sub-package).
  2. Verify HEAD is the tagged commit — if the first-release recovery path ran (cz bump --files-only, which does
    not tag), skip with a warning rather than amend the wrong commit.
  3. Refresh the lock via uv lock / pixi lock. Hard-fail on solver/network errors rather than ship a stale lock.
  4. If the lock changed: git add, git commit --amend --no-edit --no-verify, then git tag -f <version> onto the
    amended commit.
  5. Sanity-check the tag now points at HEAD.

The existing push step then publishes the amended commit and the re-pointed tag as if that had been the state all
along. pip is skipped (no native lockfile).

Also in this PR — the release test suite (.github/workflows/test-release-github.yml) was only runnable under
its push: branches: [main] trigger: every job hard-codes git push origin main, and the action itself does a bare
git push, both of which need a local main. Dispatching the suite on a feature branch died at setup with
src refspec main does not match any, so the suite could never be validated pre-merge. A per-job normalize step
(git checkout -B main + git config push.default current) makes the whole suite runnable from the Run-workflow
button on any branch. This is how the fix below was validated in real CI.

Issues

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Full suite dispatched on this branch and all 34 jobs pass
(run: https://github.com/serapeum-org/github-actions/actions/runs/29693095409). New/affected jobs:

  • Test release - lockfile folded into tag (uv) — proves the fold amends + retags with a refreshed uv.lock;
    asserts the tagged lock differs from the pre-release snapshot, records the new version, HEAD is the tagged
    commit, there is no follow-up commit past the tag, and a re-lock is a no-op (the issue enhancement(release/github): fold the refreshed lockfile into the version-bump commit so the tag is reproducible #14 acceptance check).
  • Test release - lockfile folded into tag (pixi) — a version-only bump does not change this fixture's
    pixi.lock (the project is not a pypi/editable dep of its own env), so the fold correctly no-ops; asserts the
    invariants that hold either way: tag on the bump commit, no follow-up commit, tagged lock satisfies a re-lock.
  • Test release - pip unaffected by lockfile fold — pip has no lockfile, so the fold step is guarded off; the
    tag stays on the bump commit with no amend and no lockfile is produced.

The core bump → lock → amend → retag mechanic was also reproduced locally against the real uv fixture with
uv + commitizen, confirming uv lock records the new version and git checkout <tag> yields a consistent lock.

Checklist:

  • updated version number in pyproject.toml. (n/a — actions are released via namespaced tags, e.g. github-release/vX)
  • added changes to History.rst. (n/a — repo has no History.rst)
  • updated the latest version in README file. (done at release-tag time)
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes. (full suite green in CI, link above)
  • documentation are updated. (follow-up: note in the action README that uv/pixi lockfiles are now inside the release tag)

MAfarrag added 4 commits July 19, 2026 16:42
…loses #14)

cz bump commits and tags atomically, but uv/pixi lockfiles can only be
regenerated after the new version is written to pyproject.toml — which
means the tagged commit has always shipped with a stale lock. Consumers
work around it with a follow-up commit that syncs the lock past the tag,
so `git checkout <tag> && uv sync --locked` mismatches (especially bad in
uv workspaces with many == cross-pins).

Insert a new step between the bump and the push that, while everything
is still local:
  1. cd to the repo root (workspaces keep the lock at the top).
  2. Verify HEAD is the tagged commit — if the recovery path ran and
     didn't tag, skip with a warning.
  3. Refresh the lock via `uv lock` / `pixi lock`.
  4. If the lock changed, git add + git commit --amend --no-edit
     --no-verify, then git tag -f <version> onto the amended commit.
  5. Sanity-check the tag now points at HEAD.

The push step then publishes the amended commit and the correctly
re-pointed tag; no force-push against the remote because none of it
was published yet. pip is skipped (no native lockfile).

Add a matrix test job (uv + pixi) that snapshots the pre-release
lockfile, runs the action, and asserts:
  - the new tag exists,
  - HEAD equals the tagged commit (no follow-up past the tag),
  - the tagged lockfile differs from the pre-release snapshot,
  - the tagged lockfile records the new version.
…sion guard

Add the issue #14 acceptance check to the uv/pixi happy-path job: after the
release, re-run the lock against the tagged pyproject.toml and assert no diff
(the package-manager-agnostic equivalent of `git checkout <tag> && <pm> sync
--locked`), proving the committed lock is consistent, not merely refreshed.

Add test-release-pip-no-lockfile-fold: a pip release must leave the tag on the
bump commit with no amend and produce no lockfile, guarding that the fold step
stays skipped for package-manager: pip.
Every job hard-codes `git push origin main` and the release action itself
does a bare `git push`, both of which require a local `main` branch. That
holds under the push-to-main trigger (checkout lands on main) but not under
workflow_dispatch on a feature branch, where the suite died at setup with
"src refspec main does not match any" before any assertion ran.

Add a Normalize step after each fake-remote setup that runs
`git checkout -B main` and `git config push.default current`, so a local
main exists at the current commit and bare pushes resolve regardless of the
dispatched ref. This lets the suite (and the new lockfile-fold jobs) be
validated from the Run workflow button on any branch, not only post-merge.
…ncy checks

A version-only bump changes uv.lock (it records the workspace member version)
but not this pixi fixture's pixi.lock, because the fixture does not install the
project as a pypi/editable dependency of its own environment — so pixi.lock
never records the project version and the fold step correctly no-ops.

Gate the "lock differs from pre-release" and "lock records new version" proofs
to uv, where folding must happen. For pixi keep the invariants that hold either
way: tag sits on the bump commit, no follow-up commit past the tag, and the
tagged lock satisfies a re-lock (issue #14 acceptance). Document the uv/pixi
difference in the job comment.
@MAfarrag MAfarrag changed the title fix(release): fold refreshed lockfile into the version-bump commit (c… fix(release): fold refreshed lockfile into the version-bump commit Jul 19, 2026
@MAfarrag
MAfarrag merged commit 1bd5e64 into main Jul 19, 2026
135 checks passed
@MAfarrag
MAfarrag deleted the fix/fold-lockfile-into-release-commit branch July 19, 2026 15:59
MAfarrag added a commit to serapeum-org/earthlens that referenced this pull request Jul 19, 2026
…ile-sync steps

The release/github composite action now folds the refreshed lockfile into the
tagged version-bump commit (serapeum-org/github-actions#15), so the two steps
that re-locked and pushed a separate uv.lock commit after the tag are redundant.
Point the ref at @main (which carries the fold; @github-release/main was not a
valid ref) and remove them.
MAfarrag added a commit that referenced this pull request Jul 20, 2026
…fold (#17)

- Docs: the earthlens[all] exclusions table wrongly said pyrosm ships no
  wheel. pyrosm 0.11 and osmium 4.3.1 both ship wheels (cp310-cp314); the
  sdist-only dep is pyrosm's transitive cykhash, so osm-pbf stays out of
  [all] to keep the install wheel-only (no C compiler). Fixed the table
  row and the install snippet.
- CI: point github-release.yml at the release/github action's @main,
  which folds the refreshed lockfile into the tagged version-bump commit,
  and drop the two now-redundant steps that pushed a separate uv.lock
  commit after the tag.

Closes #794, #795
Refs: #783, #14, #15
MAfarrag added a commit to serapeum-org/earthlens that referenced this pull request Jul 20, 2026
… specific versions (#793)

- Docs: correct the osm-pbf exclusion reason. pyrosm 0.11 and osmium
  4.3.1 both ship wheels; the sdist-only dep is pyrosm's transitive
  cykhash, so osm-pbf stays out of `all` to keep it wheel-only. Fixed
  in installation.md, the pyproject.toml all-extra comment, and the two
  osm reference docs.
- CI: pin the four serapeum-org/github-actions composite actions to
  specific versions (release/github@v1.4.0, python-setup/uv@v1.2.0,
  mkdocs-deploy@v1.3.0, release/pypi@v1.0.2) and adopt their new
  package-manager-version / version inputs, pinning uv to 0.11.29.
- Drop the two redundant lockfile-sync steps in github-release.yml:
  release/github@v1.4.0 folds the refreshed uv.lock into the tagged
  version-bump commit itself.

Closes #794, #795
Refs: #783, serapeum-org/github-actions#14, serapeum-org/github-actions#15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

enhancement(release/github): fold the refreshed lockfile into the version-bump commit so the tag is reproducible

1 participant