Skip to content

Check the repository is clean and pushed before creating a release - #196

Open
jinskeep-morpc wants to merge 1 commit into
mainfrom
feat/release-worktree-guard
Open

jinskeep-morpc wants to merge 1 commit into
mainfrom
feat/release-worktree-guard

Conversation

@jinskeep-morpc

Copy link
Copy Markdown
Collaborator

Problem

gh release create with no --target cuts the tag at the default branch's HEAD on GitHub, not at the local HEAD. Running a workflow notebook end to end therefore produces a release whose tag predates the build it describes: the rebuilt descriptors, the HTML export and the metadata are still only in the working tree when the release cell runs.

Three states produce that outcome, and all three are now checked:

  1. Uncommitted changes in the working tree.
  2. Commits that have not been pushed.
  3. A current branch that is not the one the tag will be cut on.

Changes

  • _check_worktree_synced(dir, dryRun=False) in morpc/frictionless/release.py, called first in create_release() — before the asset-existence loop and before any gh call, so nothing is published and no tag is consumed when it fails. It raises if the directory is not in a repository, if the tree is dirty (untracked-but-not-ignored files included; the message lists the offending paths), if the branch has no upstream, if it is ahead of or behind that upstream, or if it is not the remote's default branch.
  • dryRun=True downgrades all of these to a warning. A dry run is what one does mid-build with a dirty tree, so failing it would make it useless.
  • New allowDirty=False parameter on create_release() as an escape hatch. It warns when set.
  • Nothing is fetched, so the ahead/behind comparison is against the last-fetched state of the remote ref. That does not affect the case this guards against, which is local work that has not gone out. Documented in the docstring.
  • If <remote>/HEAD is not set locally — many clones never set it — only the default-branch check is skipped, with a warning naming git remote set-head. The other checks stand.
  • Version bumped to 0.7.1; reference/dev_notes.md entry added.

Tests

13 new tests in tests/test_release.py, run against real throwaway repositories (a bare remote plus a clone, so the upstream and origin/HEAD are genuine rather than mocked): every failure condition, the ignored-file and dry-run cases, and two integration tests asserting that a dirty tree raises before any gh call and that allowDirty=True proceeds.

An autouse fixture stubs the preflight for the ~20 pre-existing create_release tests, which build resources in tmp_path and would otherwise all fail for a reason none of them is testing.

pytest tests/test_release.py tests/test_gpkg.py → 98 passed. The full suite has 4 failures in tests/test_utils.py (ISO 8601 parsing); they fail identically on main and are unrelated to this change.

Follow-up needed in workflow repos

This lands a guard that workflow repos cannot currently satisfy, so it needs companion changes before it is adopted:

  • morpc-repotemplate-standardize: stop gitignoring *.html, move the HTML export cell above the release cell, and add a "commit and push here" step between them. Consequence: the rendered HTML will not show the output of the release cell itself — unavoidable, since create_release() runs inside the notebook.
  • Consumer repos: add *.log to .gitignore and apply the same reorder. morpc-parcels-standardize tracks both its .log and .html and rewrites the log during the run, so it will trip the guard on every release until that is fixed.

🤖 Generated with Claude Code

https://claude.ai/code/session_016oSEA313NSbRsysYFsbviS

gh release create with no --target cuts the tag at the default branch's HEAD
on GitHub, not at the local HEAD. Running a notebook end to end therefore
produced a release whose tag predated the build it describes: the rebuilt
descriptors and metadata were still only in the working tree when the release
cell ran.

_check_worktree_synced() now runs first in create_release() and raises if the
directory is not in a repository, if the working tree is dirty, if the branch
has no upstream, if it is ahead of or behind that upstream, or if it is not the
remote's default branch. A dry run downgrades these to a warning, and the new
allowDirty parameter skips them entirely.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016oSEA313NSbRsysYFsbviS
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.

1 participant