Add closed TestPyPI candidate source for release qualify and campaign - #723
Add closed TestPyPI candidate source for release qualify and campaign#723jeffhuber wants to merge 1 commit into
Conversation
…#713) Support package_source values pypi (default) and testpypi across `release qualify` and `release campaign`. testpypi installs from the canonical TestPyPI simple index with production PyPI as a dependency-only extra index, reusing the existing bounded pip-install retry behavior. The source is bound into campaign identity (idempotency key, resume-mismatch checks), local adapter argv construction, and hosted dispatch instructions/markers; a legacy campaign missing the field reads back as pypi. Never accepts or persists an arbitrary URL or credential -- only the closed pypi/ testpypi vocabulary and its two fixed canonical index URLs. CODE_MOWER_BUILDER:claude
Code Review ✅ ApprovedImplements the closed TestPyPI candidate source contract, adding a OptionsAuto-apply is off → Gitar will not commit updates to this branch. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Gitar |
Summary
Implements the closed TestPyPI candidate source contract from #713 (part of epic #709):
package_sourcevocabulary (pypidefault,testpypi) on bothcode-mower release qualifyandcode-mower release campaign.testpypiinstalls from the canonical TestPyPI simple index (https://test.pypi.org/simple/) with production PyPI (https://pypi.org/simple/) as a dependency-only extra index. Never an arbitrary URL or credential.release qualify's built-in runner threads the source into the existingrun_package_install_rehearsalpip-install path, reusing its existing bounded retry behavior for package-index propagation.release campaignbinds the source into:ReleaseCampaign.package_source, defaults topypi);{package_source}placeholder, threaded to the maintainedcodex/claude_audit/antigravity_cli/muse_cliadapter templates and intocampaign_adapters.py's CLI, prompt text, and Claude's sandboxed network allowlist);_existing_campaign_conflict), matching the existing--qualification-contextpattern exactly -- an explicit--package-sourcethat disagrees with a stored campaign's source is rejected as a conflict, not silently advanced.pypi(its documented default);_watch_campaign_validation_errortolerates the missing field the same way.docs/release-qualification.mdgets a new "TestPyPI candidates" subsection plus updated resume-mismatch/placeholder prose;docs/pypi-release.md's propagation-triage section gets the equivalentrelease qualify/release campaigncandidate-first callout next to the existingmigration package-install-rehearsalone.Out of scope (per the issue): private artifact / authenticated GitHub Actions artifact support.
Privacy
Only the closed
pypi/testpypiidentifier (and the two fixed canonical index URLs it can resolve to) is ever stored, dispatched, or displayed. No credentials, arbitrary URLs, source, diffs, prompts, transcripts, raw stdout/stderr, local paths, or auth output are persisted or uploaded.Test plan
tests/test_release_qualify.py::PackageSourceTests), local pip-command construction for both sources, campaign identity binding/idempotency-key binding, resume-mismatch rejection, backward-compatible legacy-campaign default, local adapter argv construction (tests/test_release_campaigns.py), hosted dispatch marker/body content and canonical-URL redaction, hosted-comment result binding, and the maintained-adapter CLI/prompt/sandbox-allowlist surface (tests/test_campaign_adapters.py).python -m unittest discover -s tests(1451 tests, all pass)python -m ruff check .python scripts/privacy_scan.pypython -m compileall -q src scriptspython scripts/smoke_easy_mode.py --code-mower-bin .venv/bin/code-mower --jsongit diff --checkHead SHA:
f5bc0fd7bc6d8a4f6d56a04d53b981f7cb6601ad🤖 Generated with Claude Code