feat: add release automation workflows#344
feat: add release automation workflows#344akashsinghal wants to merge 6 commits intooras-project:mainfrom
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #344 +/- ##
=======================================
Coverage 91.76% 91.76%
=======================================
Files 64 64
Lines 2755 2755
Branches 364 364
=======================================
Hits 2528 2528
Misses 138 138
Partials 89 89 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
5a25ab7 to
a2f76f4
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces GitHub Actions automation for the ORAS .NET library release process, replacing manual issue creation and release note drafting with two new workflows while preserving the existing deliberate release flow with manual gates.
Changes:
- Added
release-vote.ymlworkflow that creates release vote issues with maintainer checkboxes and PR changelogs when manually triggered - Added
release-github.ymlworkflow that automatically generates draft releases with categorized release notes when version tags are pushed - Updated
RELEASE_CHECKLIST.mdto document the new automated workflow steps
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
.github/workflows/release-vote.yml |
Implements manual workflow to create vote issues by parsing maintainers and extracting PR numbers from git history |
.github/workflows/release-github.yml |
Implements tag-triggered workflow to generate draft releases with categorized PRs using conventional commit prefixes |
RELEASE_CHECKLIST.md |
Updates release process documentation to reflect new automated workflows and command examples |
b5993a6 to
8c9b6b2
Compare
8c9b6b2 to
fe85641
Compare
fe85641 to
8a8bf0d
Compare
8a8bf0d to
55fbafa
Compare
55fbafa to
45d48f0
Compare
45d48f0 to
4880393
Compare
4880393 to
073f541
Compare
- Add release-vote.yml: workflow_dispatch to create vote issue with maintainer checklist and PR changelog - Add release-github.yml: tag-push triggered draft release with categorized notes (Breaking/Features/Fixes/Other) - Update RELEASE_CHECKLIST.md to document new automated flow - PR metadata fallback from commit messages when API lookup fails - First-release edge case handling (no previous tag) Signed-off-by: Akash Singhal <akashsinghal@microsoft.com>
073f541 to
635125f
Compare
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Akash Singhal <akashksinghal98@gmail.com> Signed-off-by: Akash Singhal <akashsinghal@microsoft.com>
2e5c827 to
78333dd
Compare
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Akash Singhal <akashksinghal98@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Akash Singhal <akashksinghal98@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Akash Singhal <akashksinghal98@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Akash Singhal <akashksinghal98@gmail.com>
shizhMSFT
left a comment
There was a problem hiding this comment.
Caution
These review comments are generated by GitHub Copilot using Claude Opus 4.6, mimicking shizhMSFT's code review style. This is not the real shizhMSFT — just an AI approximation. Comments are advisory only.
Overall the automation looks solid — draft-only releases and NuGet gating on publish are good design decisions. A few inline comments below, primarily around script injection in the workflow run: blocks.
|
|
||
| - name: Validate tag name | ||
| run: | | ||
| TAG_NAME="${{ inputs.tag_name }}" |
There was a problem hiding this comment.
${{ inputs.tag_name }} is directly interpolated into the shell by GitHub's template engine before the script runs. This is vulnerable to script injection — a crafted input can execute arbitrary commands.
We should pass all user-controlled inputs via env: mapping:
- name: Validate tag name
env:
TAG_NAME: ${{ inputs.tag_name }}
run: |
if ! echo "$TAG_NAME" ...Same pattern applies to all ${{ inputs.* }} and ${{ steps.*.outputs.* }} expressions used directly in run: blocks across all three workflow files.
| - name: Resolve commit SHA | ||
| id: resolve | ||
| run: | | ||
| if [ -n "${{ inputs.commit_sha }}" ]; then |
There was a problem hiding this comment.
Same here for ${{ inputs.commit_sha }}.
| TAG_NAME="${{ inputs.tag_name }}" | ||
| COMMIT_SHA="${{ steps.resolve.outputs.sha }}" | ||
| PREV_TAG="${{ steps.prev_tag.outputs.tag }}" | ||
| MAINTAINERS="${{ steps.maintainers.outputs.list }}" |
There was a problem hiding this comment.
Same issue. Multiline step outputs (maintainers.outputs.list, changelog.outputs.pr_list) are particularly dangerous — embedded newlines or shell metacharacters in maintainer names or PR titles can break out of the double-quoted string.
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| TAG_NAME="${GITHUB_REF_NAME}" | ||
| PREV_TAG="${{ steps.prev_tag.outputs.tag }}" |
There was a problem hiding this comment.
Same concern — an existing tag containing shell metacharacters could be picked up by git tag --merged and injected here.
| id: version | ||
| run: echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT | ||
| run: | | ||
| TAG_NAME="${{ github.event.release.tag_name }}" |
There was a problem hiding this comment.
Same here for ${{ github.event.release.tag_name }}.
| # commit message parsing if the API lookup fails. | ||
| if [ -n "$PREV_TAG" ]; then | ||
| PR_NUMBERS=$(git log --oneline "$PREV_TAG..$TAG_NAME" \ | ||
| | grep -oP '#\K\d+' | sort -n -u || true) |
There was a problem hiding this comment.
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| NUPKG: ./src/OrasProject.Oras/bin/Release/OrasProject.Oras.${{ steps.version.outputs.version }}.nupkg | ||
| run: | | ||
| sha256sum "$NUPKG" | awk '{print $1}' > "${NUPKG}.sha256" |
There was a problem hiding this comment.
nit: awk '{print $1}' strips the filename from the checksum. The standard sha256sum --check format expects <hash> <filename>. Should we keep the full output so users can verify with sha256sum --check *.sha256?
|
|
||
| # Determine if this is a pre-release | ||
| PRERELEASE_FLAG="" | ||
| if echo "$TAG_NAME" | grep -qE '[-](alpha|beta|rc|preview)'; then |
There was a problem hiding this comment.
Should we check for the presence of a hyphen after the patch version instead? Per SemVer2, any version with a pre-release identifier (i.e. a - suffix after MAJOR.MINOR.PATCH) is a pre-release. The current pattern misses identifiers like dev, nightly, etc.
| - name: Validate tag name | ||
| run: | | ||
| if ! echo "${GITHUB_REF_NAME}" \ | ||
| | grep -qP '^v\d+\.\d+\.\d+(-[a-zA-Z0-9.\-]+)?(\+[a-zA-Z0-9.\-]+)?$'; then |
There was a problem hiding this comment.
nit: the SemVer validation regex is duplicated across all three workflows. Consider extracting to a reusable workflow or composite action to avoid drift.
What this PR does / why we need it
Adds two GitHub Actions workflows to automate manual steps in the release process, replacing error-prone manual issue creation and release note drafting with automation. The release steps themselves don't change — this PR automates the execution of existing steps while preserving the same deliberate flow.
Additionally,
release-nuget.ymlis updated to trigger onrelease: publishedinstead ofpush: tags: v*, ensuring NuGet packages are only published after a maintainer explicitly publishes the draft release. The workflow also uploads the.nupkgand a SHA256 checksum as release assets for transparency and offline verification.Release process with this PR
v1.0.0-rc.1)release-voteworkflow — from the Actions tab, provide the tag name and optional commit SHA. A vote issue is created with maintainer checklist and PR changelog.git tag <tag_name> <commit_sha> && git push origin <tag_name>. This creates a draft GitHub Release with auto-generated structured notes..nupkg+ SHA256 checksum as release assets.Validation
All three workflows were end-to-end tested on a personal fork. The full flow was validated multiple times across iterations:
release-vote— Triggered viaworkflow_dispatchwithv0.7.0-test. Vote issue created with correct maintainer count (4), checkbox list parsed fromMAINTAINERS.md, PR changelog populated, tag name validation passed. First-release edge case (no previous tags) handled correctly.release-github— Tag push created a draft release with categorized notes (Breaking Changes, New Features, Bug Fixes, Other Changes, Detailed Commits). Pre-release detection, tag validation, andgit tag --mergedancestry all verified.release-nuget— Triggered only on release publish (not on tag push). Build succeeded,.nupkgand.sha256checksum uploaded as release assets before the NuGet push step. NuGet push failed as expected (no API key configured on fork), confirming that assets are attached even if NuGet push encounters a transient failure.--skip-duplicateensures re-runs are safe.What changed
release-vote.yml(workflow_dispatch):git rev-parse --verifyMAINTAINERS.mdto build a checkbox list of approversgrep -con checkbox lines for accuracygit tag --mergedto find previous tag reachable from the target commitgit logbetween the previous tag and the target commitrelease-github.yml(tag push trigger):v*tag is pushed — tag push remains a deliberate manual action by the release manager so maintainers control when a release happensgit tag --mergedto find previous tag reachable from the current tag (correct for release branches)gh pr viewAPI with a fallback to commit message parsing when API lookup failscontents: writefor release creation,pull-requests: readfor PR metadata lookuprelease-nuget.yml(updated trigger + release assets):push: tags: v*torelease: types: [published].nupkgand SHA256 checksum as release assets before NuGet push — assets survive transient push failures--skip-duplicateon NuGet push for safe re-runscontents: writefor release asset uploadsRELEASE_CHECKLIST.mdupdated to document the new automated flow including release asset uploads.Key design decisions
.nupkgand its SHA256 checksum are attached to the GitHub release for archival, transparency, and offline verification. Assets are uploaded before NuGet push so they persist even if the push fails. NuGet.org remains the primary distribution channel.git tag --merged: Previous tag is resolved relative to the target commit, ensuring correct behavior for both linear releases and release branches/backports.gh pr viewfails, falls back to parsing commit messages. Fallback authors use plain text (no@) to avoid tagging the wrong person. When no PRs are found at all, falls back to raw commit listing.grep -xFvfor tag filtering: Uses whole-line fixed-string matching to avoid regex metacharacter issues with version dots.git logand omitting changelog comparison URL when appropriate.contents: read/write,issues: write,pull-requests: read).Which issue(s) this PR resolves / fixes
Fixes #343
Please check the following list