Repository navigation
fix(release): resume immutable v0.5.0 publication - #282
Conversation
WalkthroughThe release preflight adds an opt-in ChangesRelease preflight recovery
Estimated code review effort: 2 (Simple) | ~10 minutes 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/ci/test-release-publish-preflight.py`:
- Around line 100-111: Update the assertions in the test around the extracted
candidate-preflight block to verify workflow semantics rather than only string
presence: require WAIVE_UNRELEASED_ENTRIES=true and a non-empty RECOVERY_REASON
exclusively in the workflow_dispatch recovery branch, ensure the reason is
recorded, immutable v0.5.0 same-SHA recovery is validated, and
--allow-unreleased-entries is added only after those checks. Also assert that
the release-triggered path cannot use the waiver.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: eacded6c-e213-458e-aa53-bc51169047b6
⛔ Files ignored due to path filters (4)
.github/workflows/publish.yamlis excluded by!**/.github/**CHANGELOG.mdis excluded by!**/*.mddocs/development/v0.5.0-release-operator-runbook.mdis excluded by!**/*.md,!**/docs/**docs/reference/changelog.mdis excluded by!**/*.md,!**/docs/**
📒 Files selected for processing (2)
scripts/ci/release-publish-preflight.pyscripts/ci/test-release-publish-preflight.py
| workflow = WORKFLOW.read_text(encoding="utf-8") | ||
| preflight = workflow.split(" candidate-preflight:\n", 1)[1].split("\n publish-pypi:", 1)[0] | ||
| assert "release-publish-preflight.py" in preflight | ||
| assert "github.event.release.tag_name" in preflight | ||
| assert "github.sha" in preflight | ||
| assert "refs/remotes/origin/main" in preflight | ||
| assert "workflow_dispatch:" in workflow | ||
| assert "waive_unreleased_entries:" in workflow | ||
| assert "RECOVERY_REASON" in preflight | ||
| assert "--allow-unreleased-entries" in preflight | ||
| assert "git show" in preflight | ||
| assert "refs/remotes/origin/main:scripts/ci/release-publish-preflight.py" in preflight |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
workflow="$(fd -t f -g 'publish.yaml' . | head -n1)"
test -n "$workflow"
rg -n -C 12 \
'workflow_dispatch:|waive_unreleased_entries|RECOVERY_REASON|allow-unreleased-entries|v0\.5\.0|git show|refs/remotes/origin/main:scripts/ci/release-publish-preflight.py' \
"$workflow"Repository: CurateLabs/graphforge
Length of output: 159
🏁 Script executed:
set -euo pipefail
printf '%s\n' 'Candidate workflow files:'
git ls-files | rg '(^|/)(publish|release).*\.ya?ml$|\.github/workflows/.*\.ya?ml$' || true
printf '%s\n' 'Relevant identifiers:'
rg -n -C 8 \
'candidate-preflight|waive_unreleased_entries|RECOVERY_REASON|allow-unreleased-entries|release-publish-preflight|workflow_dispatch|v0\.5\.0|refs/remotes/origin/main' \
.github scripts 2>/dev/null || true
printf '%s\n' 'Test file:'
sed -n '1,180p' scripts/ci/test-release-publish-preflight.pyRepository: CurateLabs/graphforge
Length of output: 50379
🏁 Script executed:
set -euo pipefail
printf '%s\n' 'Workflow source section:'
sed -n '1,82p' .github/workflows/publish.yaml
printf '%s\n' 'Workflow test section:'
sed -n '80,125p' scripts/ci/test-release-publish-preflight.py
printf '%s\n' 'All workflow assertions and recovery references:'
rg -n -C 4 \
'assert |waive_unreleased_entries|recovery_reason|RECOVERY_REASON|allow-unreleased-entries|RELEASE_TAG|RELEASE_SHA|GITHUB_EVENT_NAME|github\.event\.release\.tag_name|github\.sha' \
scripts/ci/test-release-publish-preflight.py .github/workflows/publish.yaml
printf '%s\n' 'Preflight waiver implementation and callers:'
rg -n -C 8 \
'allow-unreleased-entries|allow_unreleased|unreleased|def validate|argparse|release-publish-preflight' \
scripts/ci/release-publish-preflight.py scripts/ci/test-release-publish-preflight.pyRepository: CurateLabs/graphforge
Length of output: 43706
🏁 Script executed:
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
workflow = Path(".github/workflows/publish.yaml").read_text(encoding="utf-8")
test = Path("scripts/ci/test-release-publish-preflight.py").read_text(encoding="utf-8")
preflight = workflow.split(" candidate-preflight:\n", 1)[1].split("\n publish-pypi:", 1)[0]
# Reproduce the test's relevant raw-token checks.
checks = [
"release-publish-preflight.py",
"github.event.release.tag_name",
"github.sha",
"refs/remotes/origin/main",
"workflow_dispatch:",
"waive_unreleased_entries:",
"RECOVERY_REASON",
"--allow-unreleased-entries",
"git show",
"refs/remotes/origin/main:scripts/ci/release-publish-preflight.py",
]
assert all(token in (workflow if token == "workflow_dispatch:" or token == "waive_unreleased_entries:" else preflight)
for token in checks)
# A comment-only bypass marker satisfies the current assertion while the
# executable waiver operation is absent.
mutated = preflight.replace(
' preflight_args+=(--allow-unreleased-entries)\n',
' # preflight_args+=(--allow-unreleased-entries)\n',
)
assert "--allow-unreleased-entries" in mutated
assert "preflight_args+=(--allow-unreleased-entries)" not in mutated
# The current source has a real executable waiver operation and a non-empty
# reason check, but the test does not bind either token to that recovery branch.
assert 'test -n "$RECOVERY_REASON"' in preflight
assert 'preflight_args+=(--allow-unreleased-entries)' in preflight
branch = preflight.split('if test "$GITHUB_EVENT_NAME" = release; then', 1)[1].split("\n fi", 1)[0]
assert 'test "$WAIVE_UNRELEASED_ENTRIES" = true' in branch
assert 'test -n "$RECOVERY_REASON"' in branch
assert 'preflight_args+=(--allow-unreleased-entries)' in branch
assert 'printf \'Recovery reason: %s\\n\' "$RECOVERY_REASON"' in branch
print("raw assertions accept a comment-only waiver marker")
print("current waiver and reason checks are executable and inside the non-release branch")
PYRepository: CurateLabs/graphforge
Length of output: 248
🏁 Script executed:
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
workflow = Path(".github/workflows/publish.yaml").read_text(encoding="utf-8")
preflight = workflow.split(" candidate-preflight:\n", 1)[1].split("\n publish-pypi:", 1)[0]
tokens = [
"release-publish-preflight.py",
"github.event.release.tag_name",
"github.sha",
"refs/remotes/origin/main",
"workflow_dispatch:",
"waive_unreleased_entries:",
"RECOVERY_REASON",
"--allow-unreleased-entries",
"git show",
"refs/remotes/origin/main:scripts/ci/release-publish-preflight.py",
]
for token in tokens:
source = workflow if token in {"workflow_dispatch:", "waive_unreleased_entries:"} else preflight
print(f"{token!r}: {token in source}")
print("\nRelevant executable lines:")
for number, line in enumerate(preflight.splitlines(), 1):
if any(token in line for token in (
"GITHUB_EVENT_NAME", "WAIVE_UNRELEASED_ENTRIES", "RECOVERY_REASON",
"allow-unreleased-entries", "git show", "printf 'Recovery reason",
)):
print(f"{number}: {line}")
print("\nBranch extraction:")
marker = 'if test "$GITHUB_EVENT_NAME" = release; then'
print("marker present:", marker in preflight)
if marker in preflight:
suffix = preflight.split(marker, 1)[1]
print(suffix.split("\n fi", 1)[0])
PYRepository: CurateLabs/graphforge
Length of output: 1826
Make the recovery workflow test semantic. Assert that only the workflow_dispatch recovery branch requires WAIVE_UNRELEASED_ENTRIES=true and a non-empty RECOVERY_REASON, records the reason, validates the immutable v0.5.0 same-SHA recovery, and adds --allow-unreleased-entries after those checks. Assert that the release path cannot use the waiver.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/ci/test-release-publish-preflight.py` around lines 100 - 111, Update
the assertions in the test around the extracted candidate-preflight block to
verify workflow semantics rather than only string presence: require
WAIVE_UNRELEASED_ENTRIES=true and a non-empty RECOVERY_REASON exclusively in the
workflow_dispatch recovery branch, ensure the reason is recorded, immutable
v0.5.0 same-SHA recovery is validated, and --allow-unreleased-entries is added
only after those checks. Also assert that the release-triggered path cannot use
the waiver.
Closes #281
Summary
Evidence
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by CodeRabbit
New Features
Bug Fixes
Note
Add manual recovery dispatch to resume immutable v0.5.0 publication
workflow_dispatchtrigger to publish.yaml withrelease_tag,waive_unreleased_entries, andrecovery_reasoninputs to allow restarting a stalled release.candidate-preflightjob verifies the immutable tag exists and is an ancestor ofmain, fetches the currentrelease-publish-preflight.pyfromorigin/main, and runs it with--allow-unreleased-entriesto waive only the stale[Unreleased]changelog check.--allow-unreleased-entriesflag to release-publish-preflight.py so the validator can skip the non-empty[Unreleased]section error when explicitly requested.origin/maintip).Macroscope summarized ce3ce2c.