chore: import upstream snapshot with attribution
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run

This commit is contained in:
wehub-resource-sync
2026-07-13 13:25:13 +08:00
commit ec2b666284
2231 changed files with 491535 additions and 0 deletions
@@ -0,0 +1,108 @@
# Copyright 2025 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.
name: Analyze New Release for ADK Docs Updates
on:
# Runs on every new release.
release:
types: [published]
# Manual trigger for testing and retrying.
workflow_dispatch:
inputs:
resume:
description: 'Resume from the last failed/interrupted run'
required: false
type: boolean
default: false
start_tag:
description: 'Older release tag (base), e.g. v1.26.0'
required: false
type: string
end_tag:
description: 'Newer release tag (head), e.g. v1.27.0'
required: false
type: string
jobs:
analyze-new-release-for-adk-docs-updates:
if: github.repository == 'google/adk-python'
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Load adk-bot SSH Private Key
uses: webfactory/ssh-agent@v0.9.1
with:
ssh-private-key: ${{ secrets.ADK_BOT_SSH_PRIVATE_KEY }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests "google-adk[db]"
- name: Restore session DB from cache
if: ${{ github.event.inputs.resume == 'true' }}
uses: actions/cache/restore@v4
with:
path: contributing/samples/adk_team/adk_documentation/adk_release_analyzer/sessions.db
key: analyzer-session-db-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
analyzer-session-db-
- name: Run Analyzing Script
env:
GITHUB_TOKEN: ${{ secrets.ADK_TRIAGE_AGENT }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY_FOR_DOCS_AGENTS }}
GOOGLE_GENAI_USE_VERTEXAI: 0
DOC_OWNER: 'google'
CODE_OWNER: 'google'
DOC_REPO: 'adk-docs'
CODE_REPO: 'adk-python'
INTERACTIVE: 0
PYTHONPATH: contributing/samples/adk_team
ANALYZER_RESUME: ${{ github.event.inputs.resume }}
ANALYZER_START_TAG: ${{ github.event.inputs.start_tag }}
ANALYZER_END_TAG: ${{ github.event.inputs.end_tag }}
shell: bash
run: |
set -euo pipefail
args=()
if [[ "${ANALYZER_RESUME:-false}" == "true" ]]; then
args+=(--resume)
fi
if [[ -n "${ANALYZER_START_TAG:-}" ]]; then
args+=(--start-tag "$ANALYZER_START_TAG")
fi
if [[ -n "${ANALYZER_END_TAG:-}" ]]; then
args+=(--end-tag "$ANALYZER_END_TAG")
fi
python -m adk_documentation.adk_release_analyzer.main "${args[@]}"
- name: Save session DB to cache
if: always()
uses: actions/cache/save@v4
with:
path: contributing/samples/adk_team/adk_documentation/adk_release_analyzer/sessions.db
key: analyzer-session-db-${{ github.run_id }}-${{ github.run_attempt }}
+31
View File
@@ -0,0 +1,31 @@
# 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.
name: Do Not Merge on GitHub
on:
pull_request:
branches: [main]
types: [opened, reopened, synchronize]
jobs:
block-merge:
if: github.repository == 'google/adk-python'
name: maintainers will submit via Copybara
runs-on: ubuntu-latest
steps:
- name: Explain why merging is blocked
run: |
echo "::error title=GitHub merge is disabled::Do NOT merge this pull request on GitHub. A maintainer will land the change internally, and Copybara will sync it back to this repository automatically."
exit 1
@@ -0,0 +1,194 @@
# 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.
name: Continuous Integration
on:
push:
branches: [main, v1]
paths:
- '**.py'
- '.pre-commit-config.yaml'
- 'pyproject.toml'
- 'tests/**'
pull_request:
branches: [main, v1]
paths:
- '**.py'
- '.pre-commit-config.yaml'
- 'pyproject.toml'
- 'tests/**'
permissions:
contents: read
jobs:
# 1. Code format and linting (Linter)
lint:
name: Pre-commit Linter
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Run pre-commit checks
uses: pre-commit/action@v3.0.1
# 2. Static type analysis (Mypy Check with Matrix)
# Compares new changes against the target base branch dynamically to support v1.
type-check:
name: Mypy Check (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13']
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Generate Baseline
env:
TARGET_BRANCH: ${{ github.base_ref || github.ref_name }}
run: |
# Switch to target base branch to generate baseline
git checkout origin/$TARGET_BRANCH
git checkout ${{ github.sha }} -- pyproject.toml
# Install dependencies for target branch
uv venv .venv
source .venv/bin/activate
uv sync --all-extras
# Run mypy, filter for errors only, remove line numbers, and sort
# We ignore exit code (|| true) because we expect errors on baseline
uv run mypy . | grep "error:" | sed 's/:\([0-9]\+\):/::/g' | sort > baseline_errors.txt || true
echo "Found $(wc -l < baseline_errors.txt) errors on $TARGET_BRANCH."
- name: Check PR Branch
run: |
# Switch back to the PR commit
git checkout ${{ github.sha }}
# Re-sync dependencies in case the PR changed them
source .venv/bin/activate
uv sync --all-extras
# Run mypy on PR code, apply same processing
uv run mypy . | grep "error:" | sed 's/:\([0-9]\+\):/::/g' | sort > pr_errors.txt || true
echo "Found $(wc -l < pr_errors.txt) errors on PR branch."
- name: Compare and Fail on New Errors
run: |
# 'comm -13' suppresses unique lines in file1 (baseline) and common lines,
# leaving only lines unique to file2 (PR) -> The new errors.
comm -13 baseline_errors.txt pr_errors.txt > new_errors.txt
if [ -s new_errors.txt ]; then
echo "::error::The following NEW mypy errors were introduced:"
cat new_errors.txt
exit 1
else
echo "Great job! No new mypy errors introduced."
fi
# 3a. Unit testing (Unit Tests with Matrix)
unit-test:
name: Unit Tests (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Install dependencies
run: |
uv venv .venv
source .venv/bin/activate
uv sync --extra test
- name: Run unit tests with pytest
run: |
source .venv/bin/activate
pytest tests/unittests \
-n auto \
--ignore=tests/unittests/artifacts/test_artifact_service.py \
--ignore=tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py
# 3b. Dual-version A2A coverage: This job pins a2a-sdk to 0.3.x and
# runs only the A2A-related tests, so the a2a/_compat.py shim stays
# verified against both SDK majors.
# TODO: Remove this 0.3.x re-run once a2a-sdk 0.3.x support is dropped.
unit-test-a2a-v0-3:
name: A2A v0.3 Tests (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Install dependencies
run: |
uv venv .venv
source .venv/bin/activate
uv sync --extra test
- name: Run A2A tests against a2a-sdk v0.3
run: |
source .venv/bin/activate
uv pip install --reinstall-package a2a-sdk 'a2a-sdk>=0.3.4,<0.4'
pytest tests/unittests/a2a \
tests/unittests/agents/test_remote_a2a_agent.py \
tests/unittests/integrations/agent_registry/test_agent_registry.py
+163
View File
@@ -0,0 +1,163 @@
# Copyright 2025 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.
name: Copybara PR Handler
on:
push:
branches:
- main
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to close (for testing)'
required: true
type: string
commit_sha:
description: 'Commit SHA reference (optional, for testing)'
required: false
type: string
jobs:
close-imported-pr:
if: github.repository == 'google/adk-python'
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: write
contents: read
steps:
- name: Check for Copybara commits and close PRs
uses: actions/github-script@v8
with:
github-token: ${{ secrets.ADK_TRIAGE_AGENT }}
script: |
// Check if this is a manual test run
const isManualRun = context.eventName === 'workflow_dispatch';
let prsToClose = [];
if (isManualRun) {
// Manual testing mode
const prNumber = parseInt(context.payload.inputs.pr_number);
const commitSha = context.payload.inputs.commit_sha || context.sha.substring(0, 7);
console.log('=== MANUAL TEST MODE ===');
console.log(`Testing with PR #${prNumber}, commit ${commitSha}`);
prsToClose.push({ prNumber, commitSha });
} else {
// Normal mode: process commits from push event
const commits = context.payload.commits || [];
console.log(`Found ${commits.length} commit(s) in this push`);
// Process each commit
for (const commit of commits) {
const sha = commit.id;
const committer = commit.committer.name;
const message = commit.message;
console.log(`\n--- Processing commit ${sha.substring(0, 7)} ---`);
console.log(`Committer: ${committer}`);
// Check if this is a Copybara commit or has a pull request reference
const prRegex = /Merges?:?\s+(?:https:\/\/github\.com\/google\/adk-python\/pull\/|#)(\d+)/i;
const isCopybara = committer === 'Copybara-Service' ||
commit.author?.email === 'genai-sdk-bot@google.com' ||
message.includes('GitOrigin-RevId:') ||
message.includes('PiperOrigin-RevId:') ||
prRegex.test(message);
if (!isCopybara) {
console.log('Not a Copybara commit, skipping');
continue;
}
// Extract PR number from commit message
const prMatch = message.match(prRegex);
if (!prMatch) {
console.log('No PR number found in Copybara commit message');
continue;
}
const prNumber = parseInt(prMatch[1]);
const commitSha = sha.substring(0, 7);
prsToClose.push({ prNumber, commitSha });
}
}
// Process PRs to close
for (const { prNumber, commitSha } of prsToClose) {
console.log(`\n--- Processing PR #${prNumber} ---`);
// Get PR details to check if it's open
let pr;
try {
pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
} catch (error) {
console.log(`PR #${prNumber} not found or inaccessible:`, error.message);
continue;
}
// Only close if PR is still open
if (pr.data.state !== 'open') {
console.log(`PR #${prNumber} is already ${pr.data.state}, skipping`);
continue;
}
const author = pr.data.user.login;
try {
// Add comment with commit reference
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: `Thank you @${author} for your contribution! 🎉\n\nYour changes have been successfully imported and merged via Copybara in commit ${commitSha}.\n\nClosing this PR as the changes are now in the main branch.`
});
// Add 'merged' label to the PR
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: ['merged']
});
// Close the PR
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
state: 'closed'
});
console.log(`Successfully closed PR #${prNumber}`);
} catch (error) {
console.log(`Error closing PR #${prNumber}:`, error.message);
}
}
if (isManualRun) {
console.log('\n=== TEST COMPLETED ===');
} else {
console.log('\n--- Finished processing all commits ---');
}
@@ -0,0 +1,71 @@
# Copyright 2025 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.
name: ADK Answering Agent for Discussions
on:
discussion:
types: [created]
discussion_comment:
types: [created]
permissions:
contents: read
jobs:
agent-answer-questions:
if: >-
github.repository == 'google/adk-python' && (
(github.event_name == 'discussion' && github.event.discussion.category.name == 'Q&A') ||
(github.event_name == 'discussion_comment' && contains(github.event.comment.body, '@adk-bot') && github.event.sender.login != 'adk-bot')
)
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Authenticate to Google Cloud
id: auth
uses: 'google-github-actions/auth@v3'
with:
credentials_json: '${{ secrets.ADK_GCP_SA_KEY }}'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install google-adk google-cloud-discoveryengine
- name: Run Answering Script
env:
GITHUB_TOKEN: ${{ secrets.ADK_TRIAGE_AGENT }}
ADK_GCP_SA_KEY: ${{ secrets.ADK_GCP_SA_KEY }}
GOOGLE_CLOUD_PROJECT: ${{ secrets.GOOGLE_CLOUD_PROJECT }}
GOOGLE_CLOUD_LOCATION: ${{ secrets.GOOGLE_CLOUD_LOCATION }}
VERTEXAI_DATASTORE_ID: ${{ secrets.VERTEXAI_DATASTORE_ID }}
GEMINI_API_DATASTORE_ID: ${{ secrets.GEMINI_API_DATASTORE_ID }}
GOOGLE_GENAI_USE_VERTEXAI: 1
OWNER: 'google'
REPO: 'adk-python'
INTERACTIVE: 0
PYTHONPATH: contributing/samples/adk_team
DISCUSSION_JSON: ${{ toJson(github.event.discussion) }}
run: |
python -c "import os; open('/tmp/discussion.json', 'w').write(os.environ['DISCUSSION_JSON'])"
python -m adk_answering_agent.main --discussion-file /tmp/discussion.json
+100
View File
@@ -0,0 +1,100 @@
# 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.
name: "ADK Issue Tracker Maintenance"
on:
# Daily background cleanup at 6:00 AM UTC (10 PM PST)
schedule:
- cron: '0 6 * * *'
# Allows manual triggering from the Actions console
workflow_dispatch:
inputs:
run_spam_monitor:
description: 'Run Issue Monitoring Agent (Spam Sweep)'
type: boolean
default: true
full_scan:
description: 'For Issue Monitoring: Run an Initial Full Scan of ALL open issues'
type: boolean
default: false
run_stale_auditor:
description: 'Run Stale Issue Auditor Agent'
type: boolean
default: true
permissions:
issues: write
contents: read
env:
GITHUB_TOKEN: ${{ secrets.ADK_TRIAGE_AGENT }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
OWNER: ${{ github.repository_owner }}
REPO: adk-python
CONCURRENCY_LIMIT: 3
LLM_MODEL_NAME: "gemini-3.5-flash"
PYTHONPATH: contributing/samples/adk_team
jobs:
# 1. Sweep for spam, advertising, and invalid issues
sweep-spam:
if: |
github.repository == 'google/adk-python' &&
(github.event_name == 'schedule' || github.event.inputs.run_spam_monitor == 'true')
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests google-adk python-dotenv
- name: Run Issue Monitoring Agent (Spam Sweep)
env:
INITIAL_FULL_SCAN: ${{ github.event.inputs.full_scan == 'true' }}
run: python -m adk_issue_monitoring_agent.main
# 2. Audit inactive issues and nudge/close them
audit-stale:
if: |
github.repository == 'google/adk-python' &&
(github.event_name == 'schedule' || github.event.inputs.run_stale_auditor == 'true')
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests google-adk python-dateutil
- name: Run Stale Auditor Agent
run: python -m adk_stale_agent.main
+78
View File
@@ -0,0 +1,78 @@
# Copyright 2025 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.
name: ADK Pull Request Triaging Agent
on:
# React within seconds of a PR opening/updating so the owner is assigned
# promptly. pull_request_target (not pull_request) is required so the run has
# the base-repo token needed to assign on community fork PRs; this workflow
# only reads PR metadata via the API and never checks out untrusted PR code.
pull_request_target:
types: [opened, reopened, synchronize, ready_for_review]
schedule:
# Backfill every 6 hours in case an event was missed.
- cron: '0 */6 * * *'
workflow_dispatch:
inputs:
pr_number:
description: 'The Pull Request number to triage (leave empty for batch mode)'
required: false
type: 'string'
pr_count:
description: 'Number of PRs to process in batch mode (default: 10)'
required: false
default: '10'
type: 'string'
# Never let two runs triage the same PR at once (e.g. rapid pushes); the
# scheduled backfill still runs under its own group.
concurrency:
group: pr-triage-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: false
jobs:
agent-triage-pull-request:
if: github.repository == 'google/adk-python'
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests google-adk
- name: Run Triaging Script
env:
GITHUB_TOKEN: ${{ secrets.ADK_TRIAGE_AGENT }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GOOGLE_GENAI_USE_VERTEXAI: 0
OWNER: 'google'
REPO: 'adk-python'
PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number || '' }}
PR_COUNT_TO_PROCESS: ${{ github.event.inputs.pr_count || '10' }}
INTERACTIVE: ${{ vars.PR_TRIAGE_INTERACTIVE }}
PYTHONPATH: contributing/samples/adk_team
run: python -m adk_pr_triaging_agent.main
+74
View File
@@ -0,0 +1,74 @@
# 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
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: ${{ steps.config.outputs.candidate_branch }}
fetch-depth: 0
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Cherry-pick commit
run: |
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: |
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 }}'."
+165
View File
@@ -0,0 +1,165 @@
# 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: 'Optional Commit SHA (only used for "cut" action; overrides branch latest)'
required: false
type: string
permissions:
contents: write
pull-requests: write
jobs:
cut-or-regenerate:
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 "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 candidate branch (Cut)
if: inputs.action == 'cut'
run: |
CANDIDATE_BRANCH="${{ steps.config.outputs.candidate_branch }}"
git checkout -b "$CANDIDATE_BRANCH"
git push origin "$CANDIDATE_BRANCH"
echo "Created and pushed branch: $CANDIDATE_BRANCH"
# 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: clean up and (for large releases) fold the
# release-please output, draft a Highlights section on top, commit it back
# to the release PR branch, and sync the PR description to match. The
# script falls back to an empty Highlights template if drafting fails, so
# this step never blocks the release. Guarded on `pr` (not `prs_created`)
# so it also runs when release-please updates an existing PR (regenerate).
- name: Set up Python
if: steps.release_please.outputs.pr != ''
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install changelog curation dependencies
if: steps.release_please.outputs.pr != ''
run: pip install --upgrade google-genai
- name: Curate changelog Highlights
if: steps.release_please.outputs.pr != ''
# 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:
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: |
set -euo pipefail
PR_BRANCH=$(echo "$RELEASE_PR" | jq -r '.headBranchName')
PR_NUMBER=$(echo "$RELEASE_PR" | jq -r '.number')
echo "Curating changelog on release PR #$PR_NUMBER (branch: $PR_BRANCH)"
git fetch origin "$PR_BRANCH"
git checkout -B "$PR_BRANCH" FETCH_HEAD
python scripts/curate_changelog.py --changelog CHANGELOG.md \
--section-out /tmp/pr_body.md
# Mirror the curated notes into the PR description so reviewers read
# the same thing that ships in CHANGELOG.md. Done regardless of whether
# the file changed, since the body is regenerated by release-please.
if [ -s /tmp/pr_body.md ]; then
gh pr edit "$PR_NUMBER" --body-file /tmp/pr_body.md
fi
if git diff --quiet -- CHANGELOG.md; then
echo "No changelog file 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"
+122
View File
@@ -0,0 +1,122 @@
# 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:
pull_request:
types: [closed]
branches:
- release/candidate
- release/v1-candidate
permissions:
contents: write
pull-requests: write
jobs:
finalize:
if: github.event.pull_request.merged == true && github.repository == 'google/adk-python'
runs-on: ubuntu-latest
steps:
- name: Check for release-please PR
id: check
env:
LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }}
run: |
if echo "$LABELS" | grep -q "autorelease: pending"; then
echo "is_release_pr=true" >> $GITHUB_OUTPUT
else
echo "Not a release-please PR, skipping"
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: ${{ github.event.pull_request.base.ref }}
token: ${{ secrets.RELEASE_PAT }}
fetch-depth: 0
- name: Extract version from manifest
if: steps.check.outputs.is_release_pr == 'true'
id: version
run: |
VERSION=$(jq -r '.["."]' "${{ steps.config.outputs.manifest_file }}")
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Extracted version: $VERSION"
- name: Configure git identity from RELEASE_PAT
if: steps.check.outputs.is_release_pr == 'true'
env:
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
run: |
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"
- name: Record last-release-sha for release-please
if: steps.check.outputs.is_release_pr == 'true'
run: |
BASE_BRANCH="${{ steps.config.outputs.base_branch }}"
CONFIG_FILE="${{ steps.config.outputs.config_file }}"
CANDIDATE_BRANCH="${{ github.event.pull_request.base.ref }}"
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}"
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 }}
- name: Update PR label to tagged
if: steps.check.outputs.is_release_pr == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
gh pr edit ${{ github.event.pull_request.number }} \
--remove-label "autorelease: pending" \
--add-label "autorelease: tagged"
echo "Updated PR label to autorelease: tagged"
+108
View File
@@ -0,0 +1,108 @@
# 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:
workflow_dispatch:
permissions:
contents: write
pull-requests: write
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 or release/v1.35.0-alpha.1)"
exit 1
fi
- 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@v7
with:
version: "latest"
enable-cache: true
- name: Set up Python
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
- name: Publish to PyPI
env:
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
run: uv publish
- name: Create merge-back PR
env:
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
SEMVER_VERSION: ${{ steps.version.outputs.semver }}
PEP440_VERSION: ${{ steps.version.outputs.pep440 }}
BASE_BRANCH: ${{ steps.version.outputs.base_branch }}
run: |
gh pr create \
--base "$BASE_BRANCH" \
--head "${GITHUB_REF_NAME}" \
--title "chore: merge release v${PEP440_VERSION} to $BASE_BRANCH" \
--body "Syncs version bump and CHANGELOG from release v${SEMVER_VERSION} to $BASE_BRANCH."
@@ -0,0 +1,92 @@
# 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.
name: "Release: Update ADk Web"
on:
workflow_dispatch:
inputs:
adk_web_repo:
description: 'Source adk-web repository'
required: true
default: 'google/adk-web' # Default source repo
adk_web_tag:
description: 'Tag of the release to download (e.g. v1.0.0).'
required: false
default: ''
jobs:
update-frontend:
if: github.repository == 'google/adk-python'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Fetch and unzip frontend assets
run: |
TARGET_DIR="src/google/adk/cli/browser"
REPO="${{ github.event.inputs.adk_web_repo }}"
TAG="${{ github.event.inputs.adk_web_tag }}"
# Clean target directory
rm -rf "$TARGET_DIR"/*
mkdir -p "$TARGET_DIR"
if [ -z "$TAG" ]; then
echo "Fetching latest release metadata for $REPO..."
RELEASE_JSON=$(curl -s "https://api.github.com/repos/$REPO/releases/latest")
else
echo "Fetching release metadata for $REPO tag $TAG..."
RELEASE_JSON=$(curl -s "https://api.github.com/repos/$REPO/releases/tags/$TAG")
fi
# Extract download URL for adk-web-browser.zip
DOWNLOAD_URL=$(echo "$RELEASE_JSON" | grep -o -E '"browser_download_url": "[^"]+"' | grep -o -E 'https://[^"]+' | grep 'adk-web-browser.zip' | head -n 1)
if [ -z "$DOWNLOAD_URL" ]; then
echo "Error: Could not find adk-web-browser.zip asset in the release."
exit 1
fi
echo "Downloading assets from: $DOWNLOAD_URL"
curl -L -o frontend.zip "$DOWNLOAD_URL"
echo "Extracting assets to $TARGET_DIR..."
unzip -o frontend.zip -d "$TARGET_DIR"
rm frontend.zip
echo "Assets extracted successfully."
- name: Extract Bot Identity
id: bot-identity
env:
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
run: |
USER_JSON=$(gh api user)
echo "name=$(echo "$USER_JSON" | jq -r '.login')" >> $GITHUB_OUTPUT
echo "email=$(echo "$USER_JSON" | jq -r '.id')+$(echo "$USER_JSON" | jq -r '.login')@users.noreply.github.com" >> $GITHUB_OUTPUT
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.RELEASE_PAT }}
commit-message: "Update compiled adk web files from ${{ github.event.inputs.adk_web_repo }}@${{ github.event.inputs.adk_web_tag || 'latest' }}"
branch: update-frontend-assets
delete-branch: true
title: "chore: update compiled adk web assets"
committer: "${{ steps.bot-identity.outputs.name }} <${{ steps.bot-identity.outputs.email }}>"
author: "${{ steps.bot-identity.outputs.name }} <${{ steps.bot-identity.outputs.email }}>"
body: |
This PR automatically updates the compiled adk web files in `src/google/adk/cli/browser/` using the assets from `${{ github.event.inputs.adk_web_repo }}@${{ github.event.inputs.adk_web_tag || 'latest' }}`.
Please review the diff before merging.
@@ -0,0 +1,69 @@
# Copyright 2025 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.
name: Upload ADK Docs to Vertex AI Search
on:
# Runs once per day at 16:00 UTC
schedule:
- cron: '00 16 * * *'
# Manual trigger for testing and fixing
workflow_dispatch:
permissions:
contents: read
jobs:
upload-adk-docs-to-vertex-ai-search:
if: github.repository == 'google/adk-python'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Clone adk-docs repository
run: git clone https://github.com/google/adk-docs.git /tmp/adk-docs
- name: Clone adk-python repository
run: git clone https://github.com/google/adk-python.git /tmp/adk-python
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Authenticate to Google Cloud
id: auth
uses: 'google-github-actions/auth@v3'
with:
credentials_json: '${{ secrets.ADK_GCP_SA_KEY }}'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install google-adk markdown google-cloud-storage google-cloud-discoveryengine
- name: Run Answering Script
env:
GITHUB_TOKEN: ${{ secrets.ADK_TRIAGE_AGENT }}
GOOGLE_CLOUD_PROJECT: ${{ secrets.GOOGLE_CLOUD_PROJECT }}
GOOGLE_CLOUD_LOCATION: ${{ secrets.GOOGLE_CLOUD_LOCATION }}
VERTEXAI_DATASTORE_ID: ${{ secrets.VERTEXAI_DATASTORE_ID }}
GOOGLE_GENAI_USE_VERTEXAI: 1
GCS_BUCKET_NAME: ${{ secrets.GCS_BUCKET_NAME }}
ADK_DOCS_ROOT_PATH: /tmp/adk-docs
ADK_PYTHON_ROOT_PATH: /tmp/adk-python
PYTHONPATH: ${{ github.workspace }}/contributing/samples/adk_team
run: python -m adk_answering_agent.upload_docs_to_vertex_ai_search