Skip to content

Commit e495609

Browse files
authored
chore(release): backport unified release workflows from main (#6318)
1 parent 4caf782 commit e495609

5 files changed

Lines changed: 289 additions & 105 deletions

File tree

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,55 @@
1-
# Step 3 (optional): Cherry-picks a commit from main to the release/candidate branch.
2-
# Use between step 1 and step 4 to include bug fixes in an in-progress release.
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Step 3 (optional): Cherry-picks a commit from a base branch (main or v1) into the active release candidate branch.
16+
# Use this between step 1 and step 4 to include hotfixes in an in-progress release.
317
# Note: Does NOT auto-trigger release-please to preserve manual changelog edits.
418
name: "Release: Cherry-pick"
519

620
on:
721
workflow_dispatch:
822
inputs:
23+
branch:
24+
description: 'Branch line of the release candidate (main or v1)'
25+
required: true
26+
default: 'main'
27+
type: choice
28+
options:
29+
- main
30+
- v1
931
commit_sha:
1032
description: 'Commit SHA to cherry-pick'
1133
required: true
12-
type: string
13-
14-
permissions:
15-
contents: write
1634

1735
jobs:
1836
cherry-pick:
37+
if: github.repository == 'google/adk-python'
1938
runs-on: ubuntu-latest
2039
steps:
40+
- name: Determine Branch Configurations
41+
id: config
42+
run: |
43+
BRANCH="${{ inputs.branch }}"
44+
if [ "$BRANCH" = "v1" ]; then
45+
echo "candidate_branch=release/v1-candidate" >> $GITHUB_OUTPUT
46+
else
47+
echo "candidate_branch=release/candidate" >> $GITHUB_OUTPUT
48+
fi
49+
2150
- uses: actions/checkout@v6
2251
with:
23-
ref: release/candidate
52+
ref: ${{ steps.config.outputs.candidate_branch }}
2453
fetch-depth: 0
2554

2655
- name: Configure git
@@ -30,14 +59,16 @@ jobs:
3059
3160
- name: Cherry-pick commit
3261
run: |
33-
echo "Cherry-picking ${INPUTS_COMMIT_SHA} to release/candidate"
62+
CANDIDATE_BRANCH="${{ steps.config.outputs.candidate_branch }}"
63+
echo "Cherry-picking ${INPUTS_COMMIT_SHA} to $CANDIDATE_BRANCH"
3464
git cherry-pick ${INPUTS_COMMIT_SHA}
3565
env:
3666
INPUTS_COMMIT_SHA: ${{ inputs.commit_sha }}
3767

3868
- name: Push changes
3969
run: |
40-
git push origin release/candidate
41-
echo "Successfully cherry-picked commit to release/candidate"
42-
echo "Note: Release Please is NOT auto-triggered to preserve manual changelog edits."
43-
echo "Run release-please.yml manually if you want to regenerate the changelog."
70+
CANDIDATE_BRANCH="${{ steps.config.outputs.candidate_branch }}"
71+
git push origin "$CANDIDATE_BRANCH"
72+
echo "Successfully cherry-picked commit to $CANDIDATE_BRANCH"
73+
echo "If you want to regenerate the changelog PR, run the 'Release: Cut' workflow manually"
74+
echo "with action='regenerate' and branch='${{ inputs.branch }}'."

.github/workflows/release-cut.yml

Lines changed: 130 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,155 @@
1-
# Step 1: Starts the release process by creating a release/candidate branch.
2-
# Generates a changelog PR for review (step 2).
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Unified release manager. Supports:
16+
# 1. Cutting a new release candidate branch from main or v1.
17+
# 2. Regenerating/updating the changelog PR on an existing candidate branch.
318
name: "Release: Cut"
419

520
on:
621
workflow_dispatch:
722
inputs:
23+
action:
24+
description: 'Action to perform'
25+
required: true
26+
default: 'cut'
27+
type: choice
28+
options:
29+
- cut
30+
- regenerate
31+
branch:
32+
description: 'Branch to release from (main or v1)'
33+
required: true
34+
default: 'main'
35+
type: choice
36+
options:
37+
- main
38+
- v1
839
commit_sha:
9-
description: 'Commit SHA to cut from (leave empty for latest main)'
40+
description: 'Optional Commit SHA (only used for "cut" action; overrides branch latest)'
1041
required: false
1142
type: string
1243

1344
permissions:
1445
contents: write
15-
actions: write
46+
pull-requests: write
1647

1748
jobs:
18-
cut-release:
49+
cut-or-regenerate:
50+
if: github.repository == 'google/adk-python'
1951
runs-on: ubuntu-latest
2052
steps:
21-
- uses: actions/checkout@v6
53+
- name: Determine Branch Configurations
54+
id: config
55+
run: |
56+
BRANCH="${{ inputs.branch }}"
57+
if [ "$BRANCH" = "v1" ]; then
58+
echo "base_ref=v1" >> $GITHUB_OUTPUT
59+
echo "candidate_branch=release/v1-candidate" >> $GITHUB_OUTPUT
60+
echo "config_file=.github/release-please-config-v1.json" >> $GITHUB_OUTPUT
61+
echo "manifest_file=.github/.release-please-manifest-v1.json" >> $GITHUB_OUTPUT
62+
else
63+
echo "base_ref=main" >> $GITHUB_OUTPUT
64+
echo "candidate_branch=release/candidate" >> $GITHUB_OUTPUT
65+
echo "config_file=.github/release-please-config.json" >> $GITHUB_OUTPUT
66+
echo "manifest_file=.github/.release-please-manifest.json" >> $GITHUB_OUTPUT
67+
fi
68+
69+
# Action: CUT NEW RELEASE
70+
- name: Checkout base ref (Cut)
71+
if: inputs.action == 'cut'
72+
uses: actions/checkout@v6
2273
with:
23-
ref: ${{ inputs.commit_sha || 'main' }}
74+
ref: ${{ inputs.commit_sha || steps.config.outputs.base_ref }}
75+
token: ${{ secrets.RELEASE_PAT }}
2476

25-
- name: Check for existing release/candidate branch
26-
env:
27-
GH_TOKEN: ${{ github.token }}
77+
- name: Check for existing candidate branch (Cut)
78+
if: inputs.action == 'cut'
2879
run: |
29-
if git ls-remote --exit-code --heads origin release/candidate &>/dev/null; then
30-
echo "Error: release/candidate branch already exists"
31-
echo "Please finalize or delete the existing release candidate before starting a new one"
80+
CANDIDATE_BRANCH="${{ steps.config.outputs.candidate_branch }}"
81+
if git ls-remote --exit-code --heads origin "$CANDIDATE_BRANCH" &>/dev/null; then
82+
echo "Error: Branch $CANDIDATE_BRANCH already exists."
83+
echo "Please finalize or delete the existing release candidate before starting a new one."
3284
exit 1
3385
fi
3486
35-
- name: Create and push release/candidate branch
87+
- name: Create and push candidate branch (Cut)
88+
if: inputs.action == 'cut'
3689
run: |
37-
git checkout -b release/candidate
38-
git push origin release/candidate
39-
echo "Created branch: release/candidate"
90+
CANDIDATE_BRANCH="${{ steps.config.outputs.candidate_branch }}"
91+
git checkout -b "$CANDIDATE_BRANCH"
92+
git push origin "$CANDIDATE_BRANCH"
93+
echo "Created and pushed branch: $CANDIDATE_BRANCH"
94+
95+
# Action: REGENERATE EXISTING PR
96+
- name: Checkout existing candidate branch (Regenerate)
97+
if: inputs.action == 'regenerate'
98+
uses: actions/checkout@v6
99+
with:
100+
ref: ${{ steps.config.outputs.candidate_branch }}
101+
token: ${{ secrets.RELEASE_PAT }}
40102

41-
- name: Trigger Release Please
103+
# Run Release Please
104+
- name: Run Release Please
105+
id: release_please
106+
uses: googleapis/release-please-action@v4
107+
with:
108+
token: ${{ secrets.RELEASE_PAT }}
109+
config-file: ${{ steps.config.outputs.config_file }}
110+
manifest-file: ${{ steps.config.outputs.manifest_file }}
111+
target-branch: ${{ steps.config.outputs.candidate_branch }}
112+
113+
# Curate the changelog: draft a Highlights section on top of the
114+
# release-please output and commit it back to the release PR branch. The
115+
# script falls back to an empty Highlights template if drafting fails, so
116+
# this step never blocks the release.
117+
- name: Set up Python
118+
if: steps.release_please.outputs.prs_created == 'true'
119+
uses: actions/setup-python@v6
120+
with:
121+
python-version: '3.11'
122+
123+
- name: Install changelog curation dependencies
124+
if: steps.release_please.outputs.prs_created == 'true'
125+
run: pip install --upgrade google-genai
126+
127+
- name: Curate changelog Highlights
128+
if: steps.release_please.outputs.prs_created == 'true'
129+
# Curation is a nice-to-have layered on top of the release PR; never let
130+
# it turn the release run red (e.g. a push race or a transient error).
131+
continue-on-error: true
42132
env:
43-
GH_TOKEN: ${{ github.token }}
133+
RELEASE_PR: ${{ steps.release_please.outputs.pr }}
134+
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
135+
GOOGLE_GENAI_USE_VERTEXAI: '0'
136+
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
44137
run: |
45-
gh workflow run release-please.yml --repo ${{ github.repository }} --ref release/candidate
46-
echo "Triggered Release Please workflow"
138+
set -euo pipefail
139+
PR_BRANCH=$(echo "$RELEASE_PR" | jq -r '.headBranchName')
140+
echo "Curating changelog on release PR branch: $PR_BRANCH"
141+
git fetch origin "$PR_BRANCH"
142+
git checkout -B "$PR_BRANCH" FETCH_HEAD
143+
python scripts/curate_changelog.py --changelog CHANGELOG.md
144+
if git diff --quiet -- CHANGELOG.md; then
145+
echo "No changelog changes to commit."
146+
exit 0
147+
fi
148+
USER_JSON=$(gh api user)
149+
git config user.name "$(echo "$USER_JSON" | jq -r '.login')"
150+
git config user.email "$(echo "$USER_JSON" | jq -r '.id')+$(echo "$USER_JSON" | jq -r '.login')@users.noreply.github.com"
151+
git add CHANGELOG.md
152+
git commit -m "chore: add curated highlights to changelog"
153+
# Rebase onto any concurrent PR-branch updates so the push doesn't fail on a stale ref.
154+
git pull --rebase origin "$PR_BRANCH"
155+
git push origin "$PR_BRANCH"

.github/workflows/release-finalize.yml

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,35 @@
1-
# Step 4: Triggers when the changelog PR is merged to release/candidate.
2-
# Records last-release-sha and renames release/candidate to release/v{version}.
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Triggers automatically when the changelog PR is merged to a candidate branch.
16+
# Records the last-release-sha for Release Please and renames the branch to release/v{version}.
317
name: "Release: Finalize"
418

519
on:
620
pull_request:
721
types: [closed]
822
branches:
923
- release/candidate
24+
- release/v1-candidate
1025

1126
permissions:
1227
contents: write
1328
pull-requests: write
1429

1530
jobs:
1631
finalize:
17-
if: github.event.pull_request.merged == true
32+
if: github.event.pull_request.merged == true && github.repository == 'google/adk-python'
1833
runs-on: ubuntu-latest
1934
steps:
2035
- name: Check for release-please PR
@@ -29,18 +44,33 @@ jobs:
2944
echo "is_release_pr=false" >> $GITHUB_OUTPUT
3045
fi
3146
47+
- name: Determine Branch Configurations
48+
if: steps.check.outputs.is_release_pr == 'true'
49+
id: config
50+
run: |
51+
CANDIDATE_BRANCH="${{ github.event.pull_request.base.ref }}"
52+
if [ "$CANDIDATE_BRANCH" = "release/v1-candidate" ]; then
53+
echo "base_branch=v1" >> $GITHUB_OUTPUT
54+
echo "config_file=.github/release-please-config-v1.json" >> $GITHUB_OUTPUT
55+
echo "manifest_file=.github/.release-please-manifest-v1.json" >> $GITHUB_OUTPUT
56+
else
57+
echo "base_branch=main" >> $GITHUB_OUTPUT
58+
echo "config_file=.github/release-please-config.json" >> $GITHUB_OUTPUT
59+
echo "manifest_file=.github/.release-please-manifest.json" >> $GITHUB_OUTPUT
60+
fi
61+
3262
- uses: actions/checkout@v6
3363
if: steps.check.outputs.is_release_pr == 'true'
3464
with:
35-
ref: release/candidate
65+
ref: ${{ github.event.pull_request.base.ref }}
3666
token: ${{ secrets.RELEASE_PAT }}
3767
fetch-depth: 0
3868

3969
- name: Extract version from manifest
4070
if: steps.check.outputs.is_release_pr == 'true'
4171
id: version
4272
run: |
43-
VERSION=$(jq -r '.["."]' .github/.release-please-manifest.json)
73+
VERSION=$(jq -r '.["."]' "${{ steps.config.outputs.manifest_file }}")
4474
echo "version=$VERSION" >> $GITHUB_OUTPUT
4575
echo "Extracted version: $VERSION"
4676
@@ -56,21 +86,28 @@ jobs:
5686
- name: Record last-release-sha for release-please
5787
if: steps.check.outputs.is_release_pr == 'true'
5888
run: |
59-
git fetch origin main
60-
CUT_SHA=$(git merge-base origin/main HEAD)
61-
echo "Release was cut from main at: $CUT_SHA"
89+
BASE_BRANCH="${{ steps.config.outputs.base_branch }}"
90+
CONFIG_FILE="${{ steps.config.outputs.config_file }}"
91+
CANDIDATE_BRANCH="${{ github.event.pull_request.base.ref }}"
92+
93+
git fetch origin "$BASE_BRANCH"
94+
CUT_SHA=$(git merge-base "origin/$BASE_BRANCH" HEAD)
95+
echo "Release was cut from $BASE_BRANCH at: $CUT_SHA"
96+
6297
jq --arg sha "$CUT_SHA" '. + {"last-release-sha": $sha}' \
63-
.github/release-please-config.json > tmp.json && mv tmp.json .github/release-please-config.json
64-
git add .github/release-please-config.json
65-
git commit -m "chore: update last-release-sha for next release"
66-
git push origin release/candidate
98+
"$CONFIG_FILE" > tmp.json && mv tmp.json "$CONFIG_FILE"
99+
100+
git add "$CONFIG_FILE"
101+
git commit -m "chore: update last-release-sha for next $BASE_BRANCH release"
102+
git push origin "$CANDIDATE_BRANCH"
67103
68-
- name: Rename release/candidate to release/v{version}
104+
- name: Rename candidate to release/v{version}
69105
if: steps.check.outputs.is_release_pr == 'true'
70106
run: |
71107
VERSION="v${STEPS_VERSION_OUTPUTS_VERSION}"
72-
git push origin "release/candidate:refs/heads/release/$VERSION" ":release/candidate"
73-
echo "Renamed release/candidate to release/$VERSION"
108+
CANDIDATE_BRANCH="${{ github.event.pull_request.base.ref }}"
109+
git push origin "$CANDIDATE_BRANCH:refs/heads/release/$VERSION" ":$CANDIDATE_BRANCH"
110+
echo "Renamed $CANDIDATE_BRANCH to release/$VERSION"
74111
env:
75112
STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }}
76113

0 commit comments

Comments
 (0)