chore(release): backport unified release workflows from main (#6318)

This commit is contained in:
Kathy Wu
2026-07-06 11:56:18 -07:00
committed by GitHub
parent 4caf782af7
commit e495609c1d
5 changed files with 294 additions and 110 deletions
+43 -12
View File
@@ -1,26 +1,55 @@
# Step 3 (optional): Cherry-picks a commit from main to the release/candidate branch.
# Use between step 1 and step 4 to include bug fixes in an in-progress release.
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Step 3 (optional): Cherry-picks a commit from a base branch (main or v1) into the active release candidate branch.
# Use this between step 1 and step 4 to include hotfixes in an in-progress release.
# Note: Does NOT auto-trigger release-please to preserve manual changelog edits.
name: "Release: Cherry-pick"
on:
workflow_dispatch:
inputs:
branch:
description: 'Branch line of the release candidate (main or v1)'
required: true
default: 'main'
type: choice
options:
- main
- v1
commit_sha:
description: 'Commit SHA to cherry-pick'
required: true
type: string
permissions:
contents: write
jobs:
cherry-pick:
if: github.repository == 'google/adk-python'
runs-on: ubuntu-latest
steps:
- name: Determine Branch Configurations
id: config
run: |
BRANCH="${{ inputs.branch }}"
if [ "$BRANCH" = "v1" ]; then
echo "candidate_branch=release/v1-candidate" >> $GITHUB_OUTPUT
else
echo "candidate_branch=release/candidate" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v6
with:
ref: release/candidate
ref: ${{ steps.config.outputs.candidate_branch }}
fetch-depth: 0
- name: Configure git
@@ -30,14 +59,16 @@ jobs:
- name: Cherry-pick commit
run: |
echo "Cherry-picking ${INPUTS_COMMIT_SHA} to release/candidate"
CANDIDATE_BRANCH="${{ steps.config.outputs.candidate_branch }}"
echo "Cherry-picking ${INPUTS_COMMIT_SHA} to $CANDIDATE_BRANCH"
git cherry-pick ${INPUTS_COMMIT_SHA}
env:
INPUTS_COMMIT_SHA: ${{ inputs.commit_sha }}
- name: Push changes
run: |
git push origin release/candidate
echo "Successfully cherry-picked commit to release/candidate"
echo "Note: Release Please is NOT auto-triggered to preserve manual changelog edits."
echo "Run release-please.yml manually if you want to regenerate the changelog."
CANDIDATE_BRANCH="${{ steps.config.outputs.candidate_branch }}"
git push origin "$CANDIDATE_BRANCH"
echo "Successfully cherry-picked commit to $CANDIDATE_BRANCH"
echo "If you want to regenerate the changelog PR, run the 'Release: Cut' workflow manually"
echo "with action='regenerate' and branch='${{ inputs.branch }}'."
+132 -23
View File
@@ -1,46 +1,155 @@
# Step 1: Starts the release process by creating a release/candidate branch.
# Generates a changelog PR for review (step 2).
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Unified release manager. Supports:
# 1. Cutting a new release candidate branch from main or v1.
# 2. Regenerating/updating the changelog PR on an existing candidate branch.
name: "Release: Cut"
on:
workflow_dispatch:
inputs:
action:
description: 'Action to perform'
required: true
default: 'cut'
type: choice
options:
- cut
- regenerate
branch:
description: 'Branch to release from (main or v1)'
required: true
default: 'main'
type: choice
options:
- main
- v1
commit_sha:
description: 'Commit SHA to cut from (leave empty for latest main)'
description: 'Optional Commit SHA (only used for "cut" action; overrides branch latest)'
required: false
type: string
permissions:
contents: write
actions: write
pull-requests: write
jobs:
cut-release:
cut-or-regenerate:
if: github.repository == 'google/adk-python'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ inputs.commit_sha || 'main' }}
- name: Check for existing release/candidate branch
env:
GH_TOKEN: ${{ github.token }}
- name: Determine Branch Configurations
id: config
run: |
if git ls-remote --exit-code --heads origin release/candidate &>/dev/null; then
echo "Error: release/candidate branch already exists"
echo "Please finalize or delete the existing release candidate before starting a new one"
BRANCH="${{ inputs.branch }}"
if [ "$BRANCH" = "v1" ]; then
echo "base_ref=v1" >> $GITHUB_OUTPUT
echo "candidate_branch=release/v1-candidate" >> $GITHUB_OUTPUT
echo "config_file=.github/release-please-config-v1.json" >> $GITHUB_OUTPUT
echo "manifest_file=.github/.release-please-manifest-v1.json" >> $GITHUB_OUTPUT
else
echo "base_ref=main" >> $GITHUB_OUTPUT
echo "candidate_branch=release/candidate" >> $GITHUB_OUTPUT
echo "config_file=.github/release-please-config.json" >> $GITHUB_OUTPUT
echo "manifest_file=.github/.release-please-manifest.json" >> $GITHUB_OUTPUT
fi
# Action: CUT NEW RELEASE
- name: Checkout base ref (Cut)
if: inputs.action == 'cut'
uses: actions/checkout@v6
with:
ref: ${{ inputs.commit_sha || steps.config.outputs.base_ref }}
token: ${{ secrets.RELEASE_PAT }}
- name: Check for existing candidate branch (Cut)
if: inputs.action == 'cut'
run: |
CANDIDATE_BRANCH="${{ steps.config.outputs.candidate_branch }}"
if git ls-remote --exit-code --heads origin "$CANDIDATE_BRANCH" &>/dev/null; then
echo "Error: Branch $CANDIDATE_BRANCH already exists."
echo "Please finalize or delete the existing release candidate before starting a new one."
exit 1
fi
- name: Create and push release/candidate branch
- name: Create and push candidate branch (Cut)
if: inputs.action == 'cut'
run: |
git checkout -b release/candidate
git push origin release/candidate
echo "Created branch: release/candidate"
CANDIDATE_BRANCH="${{ steps.config.outputs.candidate_branch }}"
git checkout -b "$CANDIDATE_BRANCH"
git push origin "$CANDIDATE_BRANCH"
echo "Created and pushed branch: $CANDIDATE_BRANCH"
- name: Trigger Release Please
# Action: REGENERATE EXISTING PR
- name: Checkout existing candidate branch (Regenerate)
if: inputs.action == 'regenerate'
uses: actions/checkout@v6
with:
ref: ${{ steps.config.outputs.candidate_branch }}
token: ${{ secrets.RELEASE_PAT }}
# Run Release Please
- name: Run Release Please
id: release_please
uses: googleapis/release-please-action@v4
with:
token: ${{ secrets.RELEASE_PAT }}
config-file: ${{ steps.config.outputs.config_file }}
manifest-file: ${{ steps.config.outputs.manifest_file }}
target-branch: ${{ steps.config.outputs.candidate_branch }}
# Curate the changelog: draft a Highlights section on top of the
# release-please output and commit it back to the release PR branch. The
# script falls back to an empty Highlights template if drafting fails, so
# this step never blocks the release.
- name: Set up Python
if: steps.release_please.outputs.prs_created == 'true'
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install changelog curation dependencies
if: steps.release_please.outputs.prs_created == 'true'
run: pip install --upgrade google-genai
- name: Curate changelog Highlights
if: steps.release_please.outputs.prs_created == 'true'
# Curation is a nice-to-have layered on top of the release PR; never let
# it turn the release run red (e.g. a push race or a transient error).
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
RELEASE_PR: ${{ steps.release_please.outputs.pr }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GOOGLE_GENAI_USE_VERTEXAI: '0'
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
run: |
gh workflow run release-please.yml --repo ${{ github.repository }} --ref release/candidate
echo "Triggered Release Please workflow"
set -euo pipefail
PR_BRANCH=$(echo "$RELEASE_PR" | jq -r '.headBranchName')
echo "Curating changelog on release PR branch: $PR_BRANCH"
git fetch origin "$PR_BRANCH"
git checkout -B "$PR_BRANCH" FETCH_HEAD
python scripts/curate_changelog.py --changelog CHANGELOG.md
if git diff --quiet -- CHANGELOG.md; then
echo "No changelog changes to commit."
exit 0
fi
USER_JSON=$(gh api user)
git config user.name "$(echo "$USER_JSON" | jq -r '.login')"
git config user.email "$(echo "$USER_JSON" | jq -r '.id')+$(echo "$USER_JSON" | jq -r '.login')@users.noreply.github.com"
git add CHANGELOG.md
git commit -m "chore: add curated highlights to changelog"
# Rebase onto any concurrent PR-branch updates so the push doesn't fail on a stale ref.
git pull --rebase origin "$PR_BRANCH"
git push origin "$PR_BRANCH"
+53 -16
View File
@@ -1,5 +1,19 @@
# Step 4: Triggers when the changelog PR is merged to release/candidate.
# Records last-release-sha and renames release/candidate to release/v{version}.
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Triggers automatically when the changelog PR is merged to a candidate branch.
# Records the last-release-sha for Release Please and renames the branch to release/v{version}.
name: "Release: Finalize"
on:
@@ -7,6 +21,7 @@ on:
types: [closed]
branches:
- release/candidate
- release/v1-candidate
permissions:
contents: write
@@ -14,7 +29,7 @@ permissions:
jobs:
finalize:
if: github.event.pull_request.merged == true
if: github.event.pull_request.merged == true && github.repository == 'google/adk-python'
runs-on: ubuntu-latest
steps:
- name: Check for release-please PR
@@ -29,10 +44,25 @@ jobs:
echo "is_release_pr=false" >> $GITHUB_OUTPUT
fi
- name: Determine Branch Configurations
if: steps.check.outputs.is_release_pr == 'true'
id: config
run: |
CANDIDATE_BRANCH="${{ github.event.pull_request.base.ref }}"
if [ "$CANDIDATE_BRANCH" = "release/v1-candidate" ]; then
echo "base_branch=v1" >> $GITHUB_OUTPUT
echo "config_file=.github/release-please-config-v1.json" >> $GITHUB_OUTPUT
echo "manifest_file=.github/.release-please-manifest-v1.json" >> $GITHUB_OUTPUT
else
echo "base_branch=main" >> $GITHUB_OUTPUT
echo "config_file=.github/release-please-config.json" >> $GITHUB_OUTPUT
echo "manifest_file=.github/.release-please-manifest.json" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v6
if: steps.check.outputs.is_release_pr == 'true'
with:
ref: release/candidate
ref: ${{ github.event.pull_request.base.ref }}
token: ${{ secrets.RELEASE_PAT }}
fetch-depth: 0
@@ -40,7 +70,7 @@ jobs:
if: steps.check.outputs.is_release_pr == 'true'
id: version
run: |
VERSION=$(jq -r '.["."]' .github/.release-please-manifest.json)
VERSION=$(jq -r '.["."]' "${{ steps.config.outputs.manifest_file }}")
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Extracted version: $VERSION"
@@ -56,21 +86,28 @@ jobs:
- name: Record last-release-sha for release-please
if: steps.check.outputs.is_release_pr == 'true'
run: |
git fetch origin main
CUT_SHA=$(git merge-base origin/main HEAD)
echo "Release was cut from main at: $CUT_SHA"
jq --arg sha "$CUT_SHA" '. + {"last-release-sha": $sha}' \
.github/release-please-config.json > tmp.json && mv tmp.json .github/release-please-config.json
git add .github/release-please-config.json
git commit -m "chore: update last-release-sha for next release"
git push origin release/candidate
BASE_BRANCH="${{ steps.config.outputs.base_branch }}"
CONFIG_FILE="${{ steps.config.outputs.config_file }}"
CANDIDATE_BRANCH="${{ github.event.pull_request.base.ref }}"
- name: Rename release/candidate to release/v{version}
git fetch origin "$BASE_BRANCH"
CUT_SHA=$(git merge-base "origin/$BASE_BRANCH" HEAD)
echo "Release was cut from $BASE_BRANCH at: $CUT_SHA"
jq --arg sha "$CUT_SHA" '. + {"last-release-sha": $sha}' \
"$CONFIG_FILE" > tmp.json && mv tmp.json "$CONFIG_FILE"
git add "$CONFIG_FILE"
git commit -m "chore: update last-release-sha for next $BASE_BRANCH release"
git push origin "$CANDIDATE_BRANCH"
- name: Rename candidate to release/v{version}
if: steps.check.outputs.is_release_pr == 'true'
run: |
VERSION="v${STEPS_VERSION_OUTPUTS_VERSION}"
git push origin "release/candidate:refs/heads/release/$VERSION" ":release/candidate"
echo "Renamed release/candidate to release/$VERSION"
CANDIDATE_BRANCH="${{ github.event.pull_request.base.ref }}"
git push origin "$CANDIDATE_BRANCH:refs/heads/release/$VERSION" ":$CANDIDATE_BRANCH"
echo "Renamed $CANDIDATE_BRANCH to release/$VERSION"
env:
STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }}
-41
View File
@@ -1,41 +0,0 @@
# Runs release-please to create/update a PR with version bump and changelog.
# Triggered only by workflow_dispatch (from release-cut.yml).
# Does NOT auto-run on push to preserve manual changelog edits after cherry-picks.
name: "Release: Please"
on:
# Only run via workflow_dispatch (triggered by release-cut.yml)
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- name: Check if release/candidate still exists
id: check
env:
GH_TOKEN: ${{ github.token }}
run: |
if gh api repos/${{ github.repository }}/branches/release/candidate --silent 2>/dev/null; then
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "release/candidate branch no longer exists, skipping"
echo "exists=false" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v6
if: steps.check.outputs.exists == 'true'
with:
ref: release/candidate
- uses: googleapis/release-please-action@v4
if: steps.check.outputs.exists == 'true'
with:
token: ${{ secrets.RELEASE_PAT }}
config-file: .github/release-please-config.json
manifest-file: .github/.release-please-manifest.json
target-branch: release/candidate
+66 -18
View File
@@ -1,5 +1,20 @@
# Step 6: Builds and publishes the package to PyPI from a release/v{version} branch.
# Creates a merge-back PR (step 7) to sync release changes to main.
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Builds and publishes the package to PyPI from a release/v* branch.
# Supports both main (v2+) stable releases and v1 pre-releases (with auto PEP 440 version mapping).
# Creates a merge-back PR to sync changes back to the base branch (main or v1).
name: "Release: Publish to PyPi"
on:
@@ -11,35 +26,66 @@ permissions:
jobs:
publish:
if: github.repository == 'google/adk-python'
runs-on: ubuntu-latest
steps:
- name: Validate branch
run: |
if [[ ! "${GITHUB_REF_NAME}" =~ ^release/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Must run from a release/v* branch (e.g., release/v0.3.0)"
if [[ ! "${GITHUB_REF_NAME}" =~ ^release/v[0-9]+\.[0-9]+\.[0-9]+ ]]; then
echo "Error: Must run from a release/v* branch (e.g., release/v0.3.0 or release/v1.35.0-alpha.1)"
exit 1
fi
- name: Extract version
id: version
run: |
VERSION="${GITHUB_REF_NAME}"
VERSION="${VERSION#release/v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Publishing version: $VERSION"
- uses: actions/checkout@v6
- name: Determine Release Type and Extract Version
id: version
run: |
BRANCH_NAME="${GITHUB_REF_NAME}"
VERSION="${BRANCH_NAME#release/v}"
# Check if this version matches the one in the v1 manifest to determine if it's a v1 release
if [ -f .github/.release-please-manifest-v1.json ] && jq -e --arg v "$VERSION" '.["."] == $v' .github/.release-please-manifest-v1.json &>/dev/null; then
echo "is_v1=true" >> $GITHUB_OUTPUT
echo "base_branch=v1" >> $GITHUB_OUTPUT
SEMVER="$VERSION"
echo "semver=$SEMVER" >> $GITHUB_OUTPUT
echo "Semver version (v1): $SEMVER"
# PEP 440 Conversion (e.g., 2.0.0-alpha.1 -> 2.0.0a1)
PEP440=$(echo "$SEMVER" | sed -E 's/-alpha\./a/; s/-beta\./b/; s/-rc\./rc/')
echo "pep440=$PEP440" >> $GITHUB_OUTPUT
echo "PEP 440 version (v1): $PEP440"
else
echo "is_v1=false" >> $GITHUB_OUTPUT
echo "base_branch=main" >> $GITHUB_OUTPUT
echo "semver=$VERSION" >> $GITHUB_OUTPUT
echo "pep440=$VERSION" >> $GITHUB_OUTPUT
echo "Semver version (main): $VERSION"
fi
- name: Install uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v7
with:
version: "latest"
enable-cache: true
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: "3.11"
- name: Update version.py with PEP 440 version (v1 only)
if: steps.version.outputs.is_v1 == 'true'
env:
PEP440_VERSION: ${{ steps.version.outputs.pep440 }}
run: |
sed -i "s/^__version__ = .*/__version__ = \"${PEP440_VERSION}\"/" src/google/adk/version.py
echo "Updated version.py to ${PEP440_VERSION}"
grep __version__ src/google/adk/version.py
- name: Build package
run: uv build
@@ -51,10 +97,12 @@ jobs:
- name: Create merge-back PR
env:
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }}
SEMVER_VERSION: ${{ steps.version.outputs.semver }}
PEP440_VERSION: ${{ steps.version.outputs.pep440 }}
BASE_BRANCH: ${{ steps.version.outputs.base_branch }}
run: |
gh pr create \
--base main \
--base "$BASE_BRANCH" \
--head "${GITHUB_REF_NAME}" \
--title "chore: merge release v${STEPS_VERSION_OUTPUTS_VERSION} to main" \
--body "Syncs version bump and CHANGELOG from release v${STEPS_VERSION_OUTPUTS_VERSION} to main."
--title "chore: merge release v${PEP440_VERSION} to $BASE_BRANCH" \
--body "Syncs version bump and CHANGELOG from release v${SEMVER_VERSION} to $BASE_BRANCH."