|
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. |
3 | 18 | name: "Release: Cut" |
4 | 19 |
|
5 | 20 | on: |
6 | 21 | workflow_dispatch: |
7 | 22 | 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 |
8 | 39 | 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)' |
10 | 41 | required: false |
11 | 42 | type: string |
12 | 43 |
|
13 | 44 | permissions: |
14 | 45 | contents: write |
15 | | - actions: write |
| 46 | + pull-requests: write |
16 | 47 |
|
17 | 48 | jobs: |
18 | | - cut-release: |
| 49 | + cut-or-regenerate: |
| 50 | + if: github.repository == 'google/adk-python' |
19 | 51 | runs-on: ubuntu-latest |
20 | 52 | 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 |
22 | 73 | with: |
23 | | - ref: ${{ inputs.commit_sha || 'main' }} |
| 74 | + ref: ${{ inputs.commit_sha || steps.config.outputs.base_ref }} |
| 75 | + token: ${{ secrets.RELEASE_PAT }} |
24 | 76 |
|
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' |
28 | 79 | 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." |
32 | 84 | exit 1 |
33 | 85 | fi |
34 | 86 |
|
35 | | - - name: Create and push release/candidate branch |
| 87 | + - name: Create and push candidate branch (Cut) |
| 88 | + if: inputs.action == 'cut' |
36 | 89 | 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 }} |
40 | 102 |
|
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 |
42 | 132 | 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 }} |
44 | 137 | 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" |
0 commit comments