chore(workflow): added release workflow#2940
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
| set -euo pipefail | ||
|
|
||
| # 1. Validate the typed version | ||
| if ! [[ "$NEW_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then |
There was a problem hiding this comment.
Issue: This step accepts any valid MAJOR.MINOR.PATCH and the drafting step even reasons about major bumps, but the downstream Python publish workflow only fires for 1.x:
# python-pypi-publish-on-release.yml
if: startsWith(github.event.release.tag_name, 'python/v1.')So a python/v2.0.0 release here would tag the SHA and create the GitHub release, but silently never publish to PyPI — the most dangerous kind of release failure because the run looks green. (The TypeScript gate is typescript/v, with no major pin, so TS is unaffected.)
Suggestion: Either relax the publish gate to python/v to match this pipeline's capability, or, if the v1. pin is intentional, reject package == 'python' majors >= 2 here with a clear error so the limitation is explicit rather than a silent no-op.
There was a problem hiding this comment.
Resolved in 5c53369 — scan-commits now rejects package == 'python' majors >= 2 loudly (lines 88–99) with an error message pointing at the python/v1. gate. This is exactly the "reject loudly until the gate is widened" option. 👍 Thanks for closing the silent-no-op hole.
| print(f"Could not parse Bedrock response as JSON: {exc}", file=sys.stderr) | ||
| return 2 | ||
|
|
||
| bump = result.get("bump", "unknown") |
There was a problem hiding this comment.
Issue: bump from the model is written straight to bump.txt without validating it's one of major/minor/patch. The docstring promises that constraint, and the run summary presents it as the advisory bump. A malformed/hallucinated value would flow through unchecked.
Suggestion: Low priority since it's advisory-only, but a quick if bump not in {"major","minor","patch"}: bump = "unknown" keeps the output honest with the documented contract.
| publish-release: | ||
| name: Create GitHub release | ||
| needs: [scan-commits, draft-summary, approve-release] | ||
| if: inputs.dry_run != true |
There was a problem hiding this comment.
Issue: approve-release runs unconditionally (it only needs scan + draft-summary), and publish-release is what's gated by dry_run != true. That means a dry run still pauses on the release-${{ inputs.package }} environment and burns two reviewer approvals just to produce the rendered notes — yet dry_run defaults to true, so this is the common path. It also trains reviewers to approve dry runs reflexively, which erodes the value of the gate on real releases.
Suggestion: Add if: inputs.dry_run != true to approve-release (and keep it as a needs of publish-release) so approvals are only requested for actual releases. The drafted notes in the run summary are already available without the approval pause.
There was a problem hiding this comment.
Resolved in 5c53369 — approve-release now carries if: inputs.dry_run != true (line 347) with a comment explaining the rubber-stamp concern, and publish-release still gates independently. Dry runs no longer consume reviewer approvals. 👍
|
Issue: The PR description is the unfilled template — no summary, no linked issue, and the Testing section is blank. For a release pipeline that adds a Suggestion: Fill in the description with the why, link any tracking issue, and note how the pipeline was validated end to end (e.g. a dry-run in a fork/test repo). Even "couldn't fully exercise X because it needs the configured release environment" is useful to state explicitly. |
|
Assessment: Request Changes Well-structured release pipeline with a genuinely thoughtful security model — the Review themes
The security thinking behind the |
ae5d5af to
a833c83
Compare
|
Re-review after force-push (
Happy to dig into any of these if a recommendation is unclear — assessment remains Request Changes until the publish-gate and setup-doc items are addressed. |
a833c83 to
5c53369
Compare
|
Re-review after
Two optional, non-blocking nits remain — neither needs to hold up merge:
Assessment: Approve — the publish-gate fix in particular turns the most dangerous failure mode into an explicit error. The two remaining nits are yours to take or leave. |
Description
Adds a manually-triggered, per-package release pipeline that pins
mainto a SHA, runs the gates against that SHA, drafts AI release notes via Bedrock, pauses on a 2-reviewer approval, then atomically tags + creates the GitHub release. The existingpython-pypi-publish-on-release.yml/typescript-npm-publish-on-release.ymllisteners do the actual PyPI / npm upload.Pipeline steps (release.yml)
scan-commits— checks out with full history + tags, validates the typed version is bareMAJOR.MINOR.PATCH, finds the highest existingpython/v*ortypescript/v*tag, rejects duplicates / non-monotonic / pre-existing tags, then pinsrelease_sha = git rev-parse origin/main. Every later job uses this SHA — nevermain— so the pipeline is deterministic ifmainmoves mid-release. Refuses if zero commits since the prev tag.if: inputs.package == ...selects:python-integration-test.yml,python-security-audit.yml(new),python-test-package-build.yml(new)typescript-integration-test.yml,typescript-security-audit.yml,typescript-test-package-pack.ymldraft-summary— assumes theAWS_ROLE_ARNBedrock role inus-west-2, runs.github/scripts/draft-release-notes.py(Bedrock Converse → JSON{bump, notes}). On any failure (regional outage, throttling, IAM, unparseable JSON) the script exits non-zero and the workflow falls back to a banner +git shortlog. Uploadsrelease-notes.md+bump.txtas a 30-day artifact and renders both into$GITHUB_STEP_SUMMARY.approve-release— pauses onenvironment: release-${{ inputs.package }}. That environment must be configured in repo settings withrequired_reviewers >= 2andDeployment branches → main only.publish-release(skipped whendry_run) —gh release create "$NEW_TAG" --target "$RELEASE_SHA" --title "$NEW_TAG" --notes-file release-notes.md. The single-call--target <sha>form atomically creates the tag and the release, avoiding the orphan-tag failure mode where a separately-pushed tag survives a failedrelease create.Supporting changes
.github/scripts/draft-release-notes.py(new, 173 lines) — collectsgit log(full bodies) +git diff --statbetween the prev tag and the pinned SHA, prompts Bedrock (defaultus.anthropic.claude-sonnet-4-5-20250929-v1:0) for{bump, notes}, writesrelease-notes.mdandbump.txtto$GITHUB_WORKSPACE. The bump suggestion is advisory only — the maintainer types the version at dispatch..github/workflows/python-security-audit.yml(new) — reusableworkflow_call. Builds the full dependency closure in a temp venv and runspip-audit. Mirrorstypescript-security-audit.yml.continue-on-error: true(informational; PR-time vulns are gated by Dependency Review inci.yml, pre-existing advisories by Dependabot)..github/workflows/python-test-package-build.yml(new) — reusableworkflow_call.python -m buildinstrands-py/, thenpip install dist/*.whlin a venv created outside$GITHUB_WORKSPACEandpython -c "import strands"from that tmp dir. The out-of-tree venv is load-bearing: a venv inside the repo can fall back to in-tree source and mask an incomplete wheel (the Python equivalent of the wheel-packaging bugtypescript-test-package-pack.ymlwas written to catch).python-integration-test.yml/typescript-integration-test.yml— addedworkflow_call(ref)entry point (sorelease.ymlcan reuse them against the pinned SHA while keeping AWS secrets) plus a defense-in-depthvalidate-call-refjob that rejects fork repos andrefs/pull/*/owner:branch-shaped refs. The trust model is documented in a block comment above each new trigger: today's only caller (release.yml) isworkflow_dispatch-gated, which is what justifies skippingauthorization-checkon theworkflow_callpath.How this was exercised
The
workflow_calltrust changes and therelease.ymlorchestration are not fully exercisable until therelease-python/release-typescriptdeployment environments exist on the repo (they do not yet — see step 4) — flagging this explicitly because the fork-rejection / ref-shape guards invalidate-call-refcannot be validated end-to-end from this PR alone.Related Issues
Documentation PR
N/A — release pipeline is contributor-facing only.
Type of Change
Other: CI / release infrastructure.
Testing
hatch run prepare(no Python source files changed; lint/format unaffected).Checklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.