Cut 0.1.0.dev4 and add a two-workflow auto-tag-then-release pipeline#50
Merged
Conversation
v0.1.0.dev3 was cut in source control by #48 but never tagged or released -- no v0.1.0.dev3 tag was ever pushed, so #49's optimizer moment-transport fix, pipeline integration, and preregistered benchmark landed on main without ever being versioned. This cut folds both changesets into a single honest release: PyPI, the GitHub Release, and the Zenodo deposit all skip directly from 0.1.0.dev2 to 0.1.0.dev4. - pyproject / __init__ / codemeta / CITATION / STATUS / .zenodo.json -> 0.1.0.dev4 - .zenodo.json erratum extended to describe the dev3 and dev4 content and to note dev3's non-release; isNewVersionOf already pointed at dev2's real Zenodo DOI (10.5281/zenodo.20787126), since dev3 has none - CHANGELOG: Unreleased rolled into [0.1.0.dev4]; the existing [0.1.0.dev3] section is kept for the record with a pointer forward, and its compare link is repointed at the merge commit since no tag exists for it Verified: 101 tests pass at 100% line+branch coverage; pre-commit (ruff, black, mypy --strict, bandit) clean; mkdocs build --strict and python -m build produce groupoid-0.1.0.dev4.
The release pipeline required a manual 'git tag vX.Y.Z && git push origin vX.Y.Z' after a release-cut PR merged, and that step had no automation and no reminder -- which is exactly how 0.1.0.dev3 (#48) shipped real code changes on main for two weeks without ever being tagged, released, or deposited to Zenodo, until #49 landed on top of it un-versioned too. release.yml now also triggers on push to main. A new detect job reads the version out of pyproject.toml; if no v<version> tag exists yet, it creates and pushes one (as github-actions[bot]) and the rest of the workflow proceeds exactly as it would from a manual tag push: build, provenance/SBOM attestation, PyPI publish, Sigstore signing, GitHub Release. A push to main with no version change is a no-op, since the tag already exists. Tag pushes and workflow_dispatch keep their existing behavior unchanged (detect short-circuits to should_release=true without touching pyproject.toml for those). The pypi GitHub Environment gate is untouched: if it has required reviewers configured, the publish step still waits for that approval. This automates the tagging step only, not the human publish decision. CONTRIBUTING.md documents the resulting release process end to end.
The previous commit (a4f2199) added a 'detect' job inside release.yml that both created the release tag (on a push to main) and, in the same run, continued straight into build/publish/sign/release. Reviewed before merge: GitHub does not re-trigger a workflow from a push made with the run's own GITHUB_TOKEN, so that design would not have double-published in practice -- but it made the publish path's safety depend on that anti-recursion behavior rather than on visible workflow structure, which is the wrong thing to rely on for something that publishes to a public registry. release.yml is reverted to exactly its pre-automation form: tag-push and workflow_dispatch only, no detect job, no branches:[main] trigger. It is now byte-identical to the version before a4f2199. A new .github/workflows/auto-tag-release.yml does the tagging and nothing else: on a push to main that touches pyproject.toml, it checks whether v<version> already exists and creates it if not, after verifying the version has a recognized release format (X.Y.Z, optionally .devN/aN/bN/rcN, no local-version metadata) and that CHANGELOG.md documents it. That tag push is what triggers release.yml, exactly as a manual 'git tag && git push' would -- the actual publish path now has exactly one trigger (a tag push) to reason about, structurally, not by relying on token-recursion semantics. CONTRIBUTING.md's Releasing section is updated to describe the two- workflow split.
Two consecutive CI runs on this PR (an initial run and a rerun of the failed jobs) both failed identically across lint, test (3.10/3.11), security, benchmark, and docs: pip installing torch from https://download.pytorch.org/whl/cpu hit SSLV3_ALERT_HANDSHAKE_FAILURE against download-r2.pytorch.org (the Cloudflare R2-backed CDN behind that index) after pip's own 5 internal retries were exhausted. Unrelated to this PR's diff -- every failing job shares this same pre-existing install step. pip's built-in retries reuse the same connection pool within one process, which does not help against a handshake failure on that connection; a verified-locally property of this fix is that each retry here is a completely fresh pip install invocation (a fresh TLS connection), which is a meaningfully different retry strategy, not just 'try again and hope'. Extracts the install into .github/scripts/install_torch_cpu.sh (five attempts, linear backoff: 10s/20s/30s/40s, explicit error annotation and exit 1 if all attempts are exhausted -- never silently falls through to a partial environment) and calls it from every one of the five occurrences of this install step across ci.yml and docs-deploy.yml, replacing the raw pip install call verbatim; nothing else in any of those steps changes. Verified: the script's three paths (immediate success, delayed success, exhausted failure) exercised directly with a stubbed pip/sleep, each producing the expected exit code and log output; all workflow YAML files still parse; the full local gate (pytest at 100% coverage, ruff, black, mypy --strict, bandit, pre-commit) is unaffected, since none of this touches groupoid/ or tests/.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two things, in order.
Why: the process broke last time
0.1.0.dev3was cut in source control by #48, but the tag push that should have followed never happened — my sandboxed session cannot push tags (the git proxy returns 403; only the designated branch push is authorized). Then #49 merged substantial new code (optimizer moment-transport fix, pipeline integration, a preregistered benchmark) on top of that un-tagged commit. Net result: two weeks of real changes onmain, never released, never versioned, never deposited to Zenodo.1. Cut
0.1.0.dev4(63d001f)Folds both un-released changesets into one honest release, since
0.1.0.dev3never separately shipped:pyproject.toml,groupoid/__init__.py,codemeta.json,CITATION.cff,STATUS.md(both mentions)..zenodo.json: version bumped, erratum text extended to describe both the dev3 and dev4 content and to note dev3's non-release.isNewVersionOfalready correctly pointed at dev2's real Zenodo DOI (10.5281/zenodo.20787126), since dev3 has none to chain from.CHANGELOG.md:Unreleasedrolled into[0.1.0.dev4]; the existing[0.1.0.dev3]section is kept for the historical record with a pointer forward, and its compare link repointed at the merge commit (af42cb4) since no tag exists for it.2. Automate the step that broke — as two single-purpose workflows (e265379, revising a4f2199)
An earlier version of this PR put the auto-tagging logic inside
release.ymlitself, triggered on both tag-push and push-to-main, with adetectjob gating the rest of that same run. Caught in review before merge: GitHub does not re-trigger a workflow from a push made with that run's ownGITHUB_TOKEN, so it would not actually have double-published — but that design made the publish path's safety depend on an anti-recursion behavior instead of on visible workflow structure, which is the wrong thing to lean on for something that publishes to a public registry. Reworked into two workflows:.github/workflows/release.ymlis reverted to exactly its pre-automation form — byte-identical to before this PR. Tag-push andworkflow_dispatchonly; no branch trigger, no detect job. It does what it always did: build, provenance/SBOM attestation, PyPI publish, Sigstore signing, GitHub Release..github/workflows/auto-tag-release.ymlis new and does exactly one thing: on a push tomainthat touchespyproject.toml, it checks whether the version's tag already exists; if not, it validates the version has a recognized release format (X.Y.Z, optionally.devN/aN/bN/rcN, no local-version metadata) and thatCHANGELOG.mddocuments it, then creates and pushes the tag (asgithub-actions[bot]) and stops. Nothing else. That tag push is what triggersrelease.yml, exactly as a manualgit tag && git pushwould.The result: the actual publish path has exactly one trigger (a tag push) to reason about, structurally — not "two triggers that happen not to double-fire because of how
GITHUB_TOKENworks." A push tomainwith no version change, or an already-tagged version, is a no-op in the tagging workflow and never reachesrelease.ymlat all.Deliberately not automated: the
pypiGitHub Environment gate is untouched. If you have required reviewers configured there, publishing still waits for that approval — this closes the "forgot to tag" gap, not the "should a human approve this publish" decision, which stays yours.CONTRIBUTING.mddocuments the resulting two-workflow release process.3. CI resilience: retry the flaky torch install (a78dfb6)
Unrelated to the release automation, but discovered blocking this PR: two consecutive CI runs both failed identically across
lint,test(3.10/3.11),security,benchmark, anddocs— every one of them hitSSLV3_ALERT_HANDSHAKE_FAILUREagainstdownload-r2.pytorch.org(the CDN behind PyTorch's CPU wheel index) after pip's own internal retries were exhausted. This is a pre-existing step inci.yml/docs-deploy.yml, untouched by this PR's other changes, and the failure is external (confirmed by hitting the identical error on a full rerun).Extracted the install into
.github/scripts/install_torch_cpu.sh: five attempts with linear backoff (10s/20s/30s/40s), each attempt a completely freshpip installinvocation (a fresh TLS connection — meaningfully different from pip's own internal retries, which reuse one connection pool within a single process), explicit error andexit 1if all attempts are exhausted rather than silently falling through to a partial environment. All five occurrences of the raw install line acrossci.ymlanddocs-deploy.ymlnow call this script instead. Verified locally: all three paths (immediate success, delayed success, exhausted failure) exercised directly against a stubbedpip/sleep, each producing the expected exit code.Important: merging this PR fires a real release
Because
0.1.0.dev4's tag doesn't exist yet, the merge commit itself will triggerauto-tag-release.yml→ pushv0.1.0.dev4→release.yml's full pipeline (build, attest, publish to PyPI pending your environment approval if configured, sign, release, and the resulting Zenodo deposit). This is intentional — it's exactly what clears the current backlog — but flagging it so it's not a surprise.Verification
pre-commit run --all-files,mkdocs build --strict,python -m build(producesgroupoid-0.1.0.dev4.tar.gz/.whl): all green.zenodo.json/codemeta.jsonparse as valid JSON;CITATION.cffparses as valid YAML; all workflow files parse as valid YAML with the expected jobsv0.1.0.dev4as not yet existinggit diffagainst the pre-automation commit confirmsrelease.ymlis byte-identical to before this PR