Conversation
- Rewrite aiter-release.yaml to align with CI nightly pipeline:
- Prebuild kernel validation (>=10 .so files)
- Smoke test (import + version check)
- Docker image push on tag (rocm/aiter-ci:{tag}-py{ver})
- S3 wheel upload (releases + staging paths)
- GitHub Release creation with wheel assets
- workflow_dispatch with configurable inputs
- Add release/** branch triggers to all CI workflows:
aiter-test, atom-test, sglang, triton, vllm
- Add RELEASE_PROCESS.md documenting release lifecycle
- Add scripts/generate_changelog.sh for auto-categorized changelogs
- Add scripts/release_checklist.md for pre/post release validation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🏷️ CI GuideRuns automatically on every PR:
Extended tests (opt-in via labels):
|
There was a problem hiding this comment.
Pull request overview
This PR introduces release engineering infrastructure for AITER by adding a tag-triggered release workflow, documenting the release lifecycle, and ensuring CI runs on release/** branches to support regular release cycles.
Changes:
- Rewrote
aiter-release.yamlto build precompiled-kernel wheels on tag push, validate prebuilts, upload wheels (artifacts + S3), push tagged Docker images, and create GitHub Releases. - Updated multiple CI workflows to also trigger on
release/**branches. - Added release process documentation plus a changelog generator and release checklist.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/aiter-release.yaml |
New end-to-end release workflow (build/validate/upload/release) triggered by tags and workflow dispatch. |
.github/workflows/aiter-test.yaml |
Allow CI to run on release/** branches. |
.github/workflows/atom-test.yaml |
Allow downstream ATOM validation to run on release/** branches. |
.github/workflows/sglang_downstream.yaml |
Allow downstream SGLang validation to run on release/** branches. |
.github/workflows/triton-test.yaml |
Allow Triton tests to run on release/** branches. |
.github/workflows/vllm_benchmark.yaml |
Allow vLLM benchmark workflow to run on release/** branches. |
RELEASE_PROCESS.md |
Documents the release/RC/hotfix flow and expectations. |
scripts/generate_changelog.sh |
Generates categorized release notes from git history. |
scripts/release_checklist.md |
Operational checklist for pre-release/release/post-release steps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| # Generate changelog and create GitHub Release on tag push | ||
| create_release: | ||
| if: ${{ startsWith(github.ref, 'refs/tags/v') && github.event.inputs.skip_github_release != 'true' }} |
There was a problem hiding this comment.
In create_release.if, skip_github_release is declared as a boolean input but the condition compares it to the string 'true'. Depending on how GitHub evaluates boolean inputs, this can cause the release to be created even when the checkbox is set. Prefer a boolean-safe check (e.g., !github.event.inputs.skip_github_release with a fallback for tag-push where inputs is unset).
| if: ${{ startsWith(github.ref, 'refs/tags/v') && github.event.inputs.skip_github_release != 'true' }} | |
| if: ${{ startsWith(github.ref, 'refs/tags/v') && (github.event.inputs == null || !github.event.inputs.skip_github_release) }} |
| # Find previous tag | ||
| PREV_TAG=$(git tag --sort=-version:refname | grep -v "$TAG" | head -1) | ||
| echo "Generating changelog: ${PREV_TAG}..${TAG}" | ||
|
|
||
| if [ -f "scripts/generate_changelog.sh" ]; then | ||
| bash scripts/generate_changelog.sh "$PREV_TAG" "$TAG" /tmp/release_notes.md | ||
| else | ||
| # Fallback: use git log | ||
| echo "# ${TAG} Release Notes" > /tmp/release_notes.md | ||
| echo "" >> /tmp/release_notes.md | ||
| git log "${PREV_TAG}..${TAG}" --format="- %s" >> /tmp/release_notes.md |
There was a problem hiding this comment.
PREV_TAG selection will be empty when the repo has no prior tags (or if grep -v "$TAG" filters everything), which will break changelog generation (git log "${PREV_TAG}..${TAG}"). Add a guard for the “first release tag” case (e.g., fall back to the first commit / skip the range and generate notes from git log "$TAG").
| # Find previous tag | |
| PREV_TAG=$(git tag --sort=-version:refname | grep -v "$TAG" | head -1) | |
| echo "Generating changelog: ${PREV_TAG}..${TAG}" | |
| if [ -f "scripts/generate_changelog.sh" ]; then | |
| bash scripts/generate_changelog.sh "$PREV_TAG" "$TAG" /tmp/release_notes.md | |
| else | |
| # Fallback: use git log | |
| echo "# ${TAG} Release Notes" > /tmp/release_notes.md | |
| echo "" >> /tmp/release_notes.md | |
| git log "${PREV_TAG}..${TAG}" --format="- %s" >> /tmp/release_notes.md | |
| # Find previous tag; allow no match for the first release tag | |
| PREV_TAG=$(git tag --sort=-version:refname | grep -Fxv "$TAG" | head -1 || true) | |
| if [ -n "$PREV_TAG" ]; then | |
| echo "Generating changelog: ${PREV_TAG}..${TAG}" | |
| if [ -f "scripts/generate_changelog.sh" ]; then | |
| bash scripts/generate_changelog.sh "$PREV_TAG" "$TAG" /tmp/release_notes.md | |
| else | |
| # Fallback: use git log between the previous tag and this tag | |
| echo "# ${TAG} Release Notes" > /tmp/release_notes.md | |
| echo "" >> /tmp/release_notes.md | |
| git log "${PREV_TAG}..${TAG}" --format="- %s" >> /tmp/release_notes.md | |
| fi | |
| else | |
| echo "No previous tag found; generating changelog from all commits in ${TAG}" | |
| echo "# ${TAG} Release Notes" > /tmp/release_notes.md | |
| echo "" >> /tmp/release_notes.md | |
| git log "$TAG" --format="- %s" >> /tmp/release_notes.md |
| for WHL in dist/*.whl; do | ||
| WHL_NAME=$(basename ${WHL}) | ||
| echo "Uploading ${WHL_NAME} to S3..." | ||
| # Upload to release-specific path | ||
| aws s3 cp ${WHL} s3://framework-whls-nightlies/whl-releases/gfx942-gfx950/${TAG}/${WHL_NAME} | ||
| # Also upload to staging for downstream CI compatibility | ||
| aws s3 cp ${WHL} s3://framework-whls-nightlies/whl-staging/gfx942-gfx950/${WHL_NAME} |
There was a problem hiding this comment.
The workflow allows overriding GPU_ARCHS, but the S3 upload path is hard-coded to gfx942-gfx950. If someone dispatches a build with a different arch set, the wheels will be uploaded under the wrong folder. Either derive the S3 prefix from ${GPU_ARCHS} (normalized) or remove the arch override to keep the uploaded path consistent with what was built.
| for WHL in dist/*.whl; do | |
| WHL_NAME=$(basename ${WHL}) | |
| echo "Uploading ${WHL_NAME} to S3..." | |
| # Upload to release-specific path | |
| aws s3 cp ${WHL} s3://framework-whls-nightlies/whl-releases/gfx942-gfx950/${TAG}/${WHL_NAME} | |
| # Also upload to staging for downstream CI compatibility | |
| aws s3 cp ${WHL} s3://framework-whls-nightlies/whl-staging/gfx942-gfx950/${WHL_NAME} | |
| RAW_GPU_ARCHS="${GPU_ARCHS:-gfx942;gfx950}" | |
| S3_GPU_ARCHS=$(printf '%s' "${RAW_GPU_ARCHS}" | tr ',;: ' '-' | tr -s '-') | |
| for WHL in dist/*.whl; do | |
| WHL_NAME=$(basename ${WHL}) | |
| echo "Uploading ${WHL_NAME} to S3 under ${S3_GPU_ARCHS}..." | |
| # Upload to release-specific path | |
| aws s3 cp ${WHL} s3://framework-whls-nightlies/whl-releases/${S3_GPU_ARCHS}/${TAG}/${WHL_NAME} | |
| # Also upload to staging for downstream CI compatibility | |
| aws s3 cp ${WHL} s3://framework-whls-nightlies/whl-staging/${S3_GPU_ARCHS}/${WHL_NAME} |
| curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" | ||
| unzip -q awscliv2.zip | ||
| sudo ./aws/install | ||
| rm -rf awscliv2.zip aws |
There was a problem hiding this comment.
Installing AWS CLI via sudo ./aws/install assumes the runner has sudo and permits system-level installs. On many self-hosted/k8s runners this will fail. Consider using a GitHub Action that provides the AWS CLI, installing via pip install --user awscli, or running aws s3 cp inside the build container where you control privileges.
| curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" | |
| unzip -q awscliv2.zip | |
| sudo ./aws/install | |
| rm -rf awscliv2.zip aws | |
| python3 -m pip install --user awscli | |
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" |
| # Get all commit subjects with PR numbers | ||
| COMMITS=$(git log "${FROM_REF}..${TO_REF}" --format="%s" --reverse) | ||
| TOTAL=$(echo "$COMMITS" | wc -l) | ||
|
|
||
| # Temp files for categories | ||
| TMP=$(mktemp -d) | ||
| trap 'rm -rf "$TMP"' EXIT | ||
|
|
||
| touch "$TMP/features" "$TMP/performance" "$TMP/fixes" "$TMP/refactor" "$TMP/ci" "$TMP/other" | ||
|
|
||
| while IFS= read -r line; do | ||
| # Extract PR number if present | ||
| PR_NUM=$(echo "$line" | grep -oP '#\d+' | tail -1 || true) | ||
| PR_LINK="" | ||
| if [ -n "$PR_NUM" ]; then | ||
| PR_LINK=" (${REPO_URL}/pull/${PR_NUM#\#})" | ||
| fi | ||
|
|
||
| # Clean up subject (remove trailing PR reference for display) | ||
| SUBJECT=$(echo "$line" | sed 's/ (#[0-9]*)$//') | ||
|
|
||
| ENTRY="- ${SUBJECT}${PR_LINK}" | ||
|
|
||
| # Categorize by prefix/keywords | ||
| case "$line" in | ||
| *"[feat]"*|*"feat("*|*"feat:"*|"Add "*|"add "*|"support"*|"Support"*|"Enable "*|"enable "*|"Introduce "*|"new "*|"New "*) | ||
| echo "$ENTRY" >> "$TMP/features" ;; | ||
| *"[Perf]"*|*"tune"*|*"Tune"*|*"tuned"*|*"Retune"*|*"retune"*|*"optim"*|*"Optim"*|*"perf"*|*"speed"*) | ||
| echo "$ENTRY" >> "$TMP/performance" ;; | ||
| *"fix"*|*"Fix"*|*"FIX"*|*"bug"*|*"Bug"*|*"hotfix"*|*"Revert"*|*"revert"*|*"accuracy"*) | ||
| echo "$ENTRY" >> "$TMP/fixes" ;; | ||
| *"refactor"*|*"Refactor"*|*"replace"*|*"Replace"*|*"remove"*|*"Remove"*|*"rm "*|*"[OPUS]"*|*"opus"*|*"migrate"*|*"clean"*) | ||
| echo "$ENTRY" >> "$TMP/refactor" ;; | ||
| "CI:"*|"CI "*|*"[CI]"*|*"test"*|*"Test"*|*"build"*|*"Build"*) | ||
| echo "$ENTRY" >> "$TMP/ci" ;; | ||
| *) | ||
| echo "$ENTRY" >> "$TMP/other" ;; | ||
| esac | ||
| done <<< "$COMMITS" |
There was a problem hiding this comment.
If there are zero commits between the two refs, COMMITS becomes empty but the here-string loop still runs once with an empty line, producing a bogus - entry and counts of 1. Handle the empty range explicitly (or iterate directly over git log ... | while read ... and compute TOTAL via git rev-list --count).
| while IFS= read -r line; do | ||
| # Extract PR number if present | ||
| PR_NUM=$(echo "$line" | grep -oP '#\d+' | tail -1 || true) | ||
| PR_LINK="" | ||
| if [ -n "$PR_NUM" ]; then | ||
| PR_LINK=" (${REPO_URL}/pull/${PR_NUM#\#})" | ||
| fi |
There was a problem hiding this comment.
grep -oP requires GNU grep with PCRE support and will fail on default macOS/BSD grep. Since this script is likely to be run locally during releases, consider switching to a POSIX-compatible extraction (e.g., sed/grep -E) or documenting the GNU grep dependency.
The Checks workflow (pre-checks.yaml) produces the signal artifact that all other CI workflows depend on via check_signal.sh. Without this trigger, all CI jobs on release branches fail at the check-signal step because no artifact exists for that commit SHA. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
[P1] Verify prebuilt kernels and smoke test from /tmp instead of /workspace to ensure Python imports from site-packages (the installed wheel) rather than the mounted source tree. Without this, validation passes even if the wheel has no prebuilt kernels. [P2] Handle first-ever release tag gracefully — PREV_TAG can be empty. Also check for hand-written RELEASE_NOTES file first and skip changelog generation entirely if found. [P2] Derive S3 upload path from GPU_ARCHS input instead of hardcoding gfx942-gfx950. Manual dispatches with different arch sets now upload to the correct prefix. [Doc] Fix RELEASE_PROCESS.md — CI runs on both push and PRs targeting release/** branches, not "push only". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Establish formal release engineering infrastructure for AITER to support regular release cycles.
Changes
Release workflow (
aiter-release.yaml) — full rewrite:.sofiles)rocm/aiter-ci:{tag}-py{ver})workflow_dispatchwith configurable inputs (runner, GPU archs, Docker images, Python versions)description:field that broke workflow parsingCI workflow triggers — added
release/**branch support:aiter-test.yamlatom-test.yamlsglang_downstream.yamltriton-test.yamlvllm_benchmark.yamlRelease process documentation:
RELEASE_PROCESS.md— full release lifecycle (branch → RC → validation → publish → hotfix)scripts/generate_changelog.sh— auto-categorizes commits by PR prefixscripts/release_checklist.md— pre/post release validation checklistMotivation
AITER has accumulated 334 commits since v0.1.11.post1 (2026-03-05) without a release. SGLang upgrade has been blocked for 7 weeks. This PR establishes the infrastructure needed for a sustainable 2-week release cadence.
ROCm Version Support Plan
Target support matrix for upcoming releases:
Test plan
release/**branch pushworkflow_dispatchis recognized on main after merge🤖 Generated with Claude Code