Skip to content

Post-release automation #1488

Post-release automation

Post-release automation #1488

Workflow file for this run

# =============================================================================
# Post-Release Automation
# =============================================================================
#
# Triggered after the pypi.yml workflow succeeds for a release event. Automates:
# A) Tagging main with the next dev tag (e.g., v0.5.1-dev after v0.5.0)
# B) Bumping fallback_version on main via PR (main is a protected branch)
# C) Updating npm lockfile on the release branch via PR
#
# =============================================================================
name: Post-release automation
on:
workflow_run:
workflows: ["Build, test, and publish packages"]
types:
- completed
workflow_dispatch:
inputs:
tag:
description: 'Release tag (e.g., v0.6.0). Use to re-run post-release steps after a failure.'
required: true
type: string
permissions:
contents: write
pull-requests: write
jobs:
post-release:
name: Post-release housekeeping
# Only run when:
# - workflow_dispatch (manual testing), OR
# - the pypi.yml workflow completed successfully AND was triggered by a release
if: |
github.event_name == 'workflow_dispatch' || (
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'release'
)
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
token: ${{ secrets.RELEASE_PAT }}
- name: Parse release version
id: parse
env:
GH_TOKEN: ${{ github.token }}
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
TAG="${{ inputs.tag }}"
else
# The triggering workflow was a release event — find the tag from the
# commit SHA that the workflow ran on, or fall back to the latest release.
HEAD_SHA="${{ github.event.workflow_run.head_sha }}"
TAG=$(git tag --points-at "$HEAD_SHA" 2>/dev/null | grep '^v' | head -1)
if [ -z "$TAG" ]; then
# Fallback: query the latest published release via GitHub API
TAG=$(gh api repos/${{ github.repository }}/releases/latest --jq '.tag_name')
fi
if [ -z "$TAG" ]; then
echo "::error::Could not determine release tag"
exit 1
fi
fi
# Strip 'v' prefix
VERSION="${TAG#v}"
# Split into components
MAJOR="${VERSION%%.*}"
REST="${VERSION#*.}"
MINOR="${REST%%.*}"
PATCH="${REST#*.}"
# Strip any pre-release suffix from patch (e.g., rc1)
PATCH="${PATCH%%[a-zA-Z-]*}"
NEXT_PATCH=$((PATCH + 1))
NEXT_DEV_TAG="v${MAJOR}.${MINOR}.${NEXT_PATCH}-dev"
NEXT_FALLBACK="${MAJOR}.${MINOR}.${NEXT_PATCH}.dev0"
RELEASE_BRANCH="release-${MAJOR}.${MINOR}.x"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "major=${MAJOR}" >> $GITHUB_OUTPUT
echo "minor=${MINOR}" >> $GITHUB_OUTPUT
echo "patch=${PATCH}" >> $GITHUB_OUTPUT
echo "next_patch=${NEXT_PATCH}" >> $GITHUB_OUTPUT
echo "next_dev_tag=${NEXT_DEV_TAG}" >> $GITHUB_OUTPUT
echo "next_fallback=${NEXT_FALLBACK}" >> $GITHUB_OUTPUT
echo "release_branch=${RELEASE_BRANCH}" >> $GITHUB_OUTPUT
echo "Release version: ${VERSION}"
echo "Next dev tag: ${NEXT_DEV_TAG}"
echo "Next fallback: ${NEXT_FALLBACK}"
echo "Release branch: ${RELEASE_BRANCH}"
# -----------------------------------------------------------------------
# Step A: Tag main with the next dev tag
# -----------------------------------------------------------------------
- name: Push dev tag to main
run: |
DEV_TAG="${{ steps.parse.outputs.next_dev_tag }}"
# Check if tag already exists
if git rev-parse "$DEV_TAG" >/dev/null 2>&1; then
echo "Tag $DEV_TAG already exists, skipping"
exit 0
fi
# Tag the HEAD of main
MAIN_SHA=$(git rev-parse origin/main)
git tag "$DEV_TAG" "$MAIN_SHA"
git push origin "$DEV_TAG"
echo "Pushed tag $DEV_TAG to main ($MAIN_SHA)"
- name: Set up uv
uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0
# -----------------------------------------------------------------------
# Step B: Bump fallback_version on main and open PR
# -----------------------------------------------------------------------
- name: Create fallback_version bump PR
env:
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
run: |
NEXT_FALLBACK="${{ steps.parse.outputs.next_fallback }}"
VERSION="${{ steps.parse.outputs.version }}"
BRANCH="post-release/bump-fallback-${VERSION}"
# Check if PR already exists
EXISTING=$(gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number')
if [ -n "$EXISTING" ]; then
echo "PR #${EXISTING} already exists for $BRANCH, skipping"
exit 0
fi
git checkout -b "$BRANCH" origin/main
# Update fallback_version in root pyproject.toml
sed -i "s/^fallback_version = .*/fallback_version = \"${NEXT_FALLBACK}\"/" pyproject.toml
# Update fallback_version in src/llama_stack_api/pyproject.toml
sed -i "s/^fallback_version = .*/fallback_version = \"${NEXT_FALLBACK}\"/" src/llama_stack_api/pyproject.toml
# Bump llama-stack-client minimum version to match the release
sed -i "s/\"llama-stack-client>=.*\"/\"llama-stack-client>=${VERSION}\"/" pyproject.toml
# Regenerate lockfile to resolve the updated client version
uv lock
# Check if there are changes
if git diff --quiet; then
echo "No changes to fallback_version, skipping PR"
exit 0
fi
git config --local user.name "github-actions[bot]"
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git add pyproject.toml src/llama_stack_api/pyproject.toml uv.lock
git commit -s -m "chore: bump fallback_version to ${NEXT_FALLBACK} after ${VERSION} release"
git push origin "$BRANCH"
gh pr create \
--base main \
--head "$BRANCH" \
--title "chore: bump fallback_version to ${NEXT_FALLBACK}" \
--body "$(cat <<EOF
Automated post-release version bump after v${VERSION}.
Updates fallback_version in both pyproject.toml files to ${NEXT_FALLBACK}.
This PR was created automatically by the post-release workflow.
EOF
)"
echo "Created PR for fallback_version bump to ${NEXT_FALLBACK}"
# -----------------------------------------------------------------------
# Step C: Update npm lockfile on release branch via PR
# -----------------------------------------------------------------------
- name: Set up Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Create npm lockfile update PR for release branch
env:
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
run: |
VERSION="${{ steps.parse.outputs.version }}"
RELEASE_BRANCH="${{ steps.parse.outputs.release_branch }}"
BRANCH="post-release/npm-lockfile-${VERSION}"
# Check if release branch exists
if ! git rev-parse "origin/${RELEASE_BRANCH}" >/dev/null 2>&1; then
echo "Release branch ${RELEASE_BRANCH} does not exist, skipping npm update"
exit 0
fi
# Check if PR already exists
EXISTING=$(gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number')
if [ -n "$EXISTING" ]; then
echo "PR #${EXISTING} already exists for $BRANCH, skipping"
exit 0
fi
git checkout -b "$BRANCH" "origin/${RELEASE_BRANCH}"
# Regenerate uv lockfile so pre-commit passes
uv lock
# Update npm lockfile
cd src/llama_stack_ui
npm install "llama-stack-client@^${VERSION}"
cd ../..
# Check if there are changes
if git diff --quiet; then
echo "No npm lockfile changes, skipping PR"
exit 0
fi
git config --local user.name "github-actions[bot]"
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git add uv.lock src/llama_stack_ui/package.json src/llama_stack_ui/package-lock.json
git commit -s -m "chore: update llama-stack-client to ^${VERSION} in UI lockfile"
git push origin "$BRANCH"
gh pr create \
--base "$RELEASE_BRANCH" \
--head "$BRANCH" \
--title "chore: update llama-stack-client to ^${VERSION} in UI lockfile" \
--body "$(cat <<EOF
Automated post-release npm lockfile update after v${VERSION}.
Updates llama-stack-client to ^${VERSION} in the UI package lockfile.
This PR was created automatically by the post-release workflow.
EOF
)"
echo "Created PR for npm lockfile update on ${RELEASE_BRANCH}"