# 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.
#
# SPDX-License-Identifier: Apache-2.0
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# ReleaseKit: Automated Release Pipeline β Python (uv)
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
#
# SAMPLE WORKFLOW β Copy to .github/workflows/releasekit-uv.yml to use.
#
# This workflow implements a release-please-style pipeline for the
# genkit Python SDK. It uses releasekit to automate:
#
# 1. PREPARE β compute version bumps, generate changelogs, open
# or update a Release PR.
# 2. RELEASE β tag the merge commit, create a GitHub Release.
# 3. PUBLISH β build and publish packages to PyPI in topological
# order with retry and ephemeral version pinning.
#
# ββ Automatic Flow ββββββββββββββββββββββββββββββββββββββββββββββββββ
#
# push to main βββΊ releasekit prepare βββΊ Release PR
# (py/** or python/**) (autorelease: pending)
# β
# merge PR
# β
# βΌ
# releasekit release βββΊ tags + GitHub Release
# β
# βΌ
# releasekit publish βββΊ PyPI
# β
# βΌ
# repository_dispatch βββΊ downstream repos
#
# ββ Manual Dispatch Flow ββββββββββββββββββββββββββββββββββββββββββββ
#
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# β workflow_dispatch UI β
# β β
# β action: [prepare βΌ] βββΊ runs PREPARE job only β
# β [release βΌ] βββΊ runs RELEASE + PUBLISH + NOTIFY β
# β β
# β target: [pypi / testpypi] β
# β dry_run: [β] simulate, no side effects β
# β force_prepare: [β] skip preflight, force PR creation β
# β group: [________] target a release group β
# β bump_type: [auto / patch / minor / major] β
# β prerelease: [________] e.g. rc.1, beta.1 β
# β skip_publish: [β] tag + release but don't publish β
# β concurrency: [0] max parallel publish (0 = auto) β
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
#
# ββ Job Dependency Graph βββββββββββββββββββββββββββββββββββββββββββ
#
# βββββββββ
# β auth β Resolve token: App β PAT β GITHUB_TOKEN
# βββββ¬ββββ
# β outputs: token, auth-method, git-user-name, git-user-email
# β
# ββββββββββββββββββββββββ
# β β
# βΌ βΌ
# βββββββββββ βββββββββββ
# β prepare β β release β
# β (push) β β (merge) β
# βββββββββββ ββββββ¬βββββ
# β needs: [auth]
# β
# βΌ
# βββββββββββ
# β publish β
# β (PyPI) β
# ββββββ¬βββββ
# β needs: [auth, release]
# β
# βΌ
# ββββββββββ
# β verify β pip install + import check
# ββββββ¬ββββ
# β needs: [publish]
# β
# βΌ
# ββββββββββ
# β notify β
# ββββββββββ
# needs: [auth, release, publish, verify]
#
# ββ Token Resolution (Sentinel Pattern) βββββββββββββββββββββββββββ
#
# The auth job resolves a token and outputs it for downstream jobs.
# Because secrets.GITHUB_TOKEN cannot cross job boundaries, the auth
# job outputs the literal string "GITHUB_TOKEN" as a sentinel when
# falling back to the built-in token.
#
# auth job downstream job
# ββββββββ ββββββββββββββ
# App token available?
# yes βββΊ output real token βββΊ use token directly
# no βββΊ PAT available?
# yes βββΊ output PAT ββΊ use token directly
# no βββΊ output βββββββββββββββββββββββββββββββ
# "GITHUB_TOKEN"β RESOLVED_TOKEN = β
# (sentinel) β auth-method == 'github- β
# β token' ? secrets.GITHUB_ β
# β TOKEN : needs.auth.token β
# βββββββββββββββββββββββββββββββ
#
# ββ Trigger Matrix ββββββββββββββββββββββββββββββββββββββββββββββββββ
#
# Event β Jobs that run
# ββββββββββββββββββββΌββββββββββββββββββββββββββββββββββ
# push to main β prepare
# PR merged β release β publish β verify β notify
# dispatch: prepare β prepare
# dispatch: release β release β publish β verify β notify
#
# ββ Inputs Reference ββββββββββββββββββββββββββββββββββββββββββββββββ
#
# Input β Type β Default β Description
# ββββββββββββββββΌββββββββββΌββββββββββΌββββββββββββββββββββββββββββββ
# action β choice β release β Pipeline stage: prepare or release
# target β choice β pypi β Registry: pypi or testpypi
# dry_run β boolean β true β Simulate without side effects
# force_prepare β boolean β false β Force PR creation (--force)
# group β string β (all) β Target a specific release group
# bump_type β choice β auto β Override semver bump detection
# prerelease β string β (none) β Prerelease suffix (e.g. rc.1)
# skip_publish β boolean β false β Tag + release, skip registry
# concurrency β string β 0 β Max parallel publish jobs
# max_retries β string β 2 β Retry failed publishes (0 = off)
# no_ai β boolean β false β Disable AI features
# model β string β (chain) β Override AI model
# codename_theme β string β (cfg) β Override codename theme
#
# ββ Required Configuration βββββββββββββββββββββββββββββββββββββββββ
#
# Repository Variables (Settings β Variables β Actions):
#
# RELEASEKIT_APP_ID β GitHub App ID (for App-based auth)
# RELEASEKIT_GIT_USER_NAME β Git committer name for CLA-signed
# identity (used with PAT/GITHUB_TOKEN)
# RELEASEKIT_GIT_USER_EMAILβ Git committer email for CLA-signed
# identity (used with PAT/GITHUB_TOKEN)
#
# Repository Secrets (Settings β Secrets β Actions):
#
# RELEASEKIT_APP_PRIVATE_KEY β GitHub App private key (PEM)
# RELEASEKIT_TOKEN β PAT fallback (if no App configured)
# GEMINI_API_KEY β Gemini API key (for AI features)
#
# If neither RELEASEKIT_APP_ID nor RELEASEKIT_TOKEN is set, the
# workflow falls back to GITHUB_TOKEN. In that case, set
# RELEASEKIT_GIT_USER_NAME and RELEASEKIT_GIT_USER_EMAIL to use
# a CLA-signed identity (otherwise PRs may fail CLA checks).
#
# ββ Idempotency & Resumability βββββββββββββββββββββββββββββββββββββ
#
# Every job is idempotent β re-running a failed workflow is safe:
#
# auth Stateless; always resolves a fresh token.
# prepare Updates the existing Release PR instead of duplicating.
# release Skips tags and GitHub Releases that already exist.
# publish Skips versions already present on the registry.
# verify Re-verifies; always safe to repeat.
# notify Dispatches repository_dispatch (downstream deduplicates).
#
# To resume after a failure, use "Re-run failed jobs" in the GitHub
# Actions UI. Only the failed jobs re-run; successful jobs keep their
# outputs. No special flags or state files are needed.
#
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
name: "ReleaseKit: Python (uv)"
on:
workflow_call:
inputs:
action:
description: 'Which pipeline stage to run'
required: false
default: release
type: string
target:
description: 'Publish target registry (release only)'
required: false
default: pypi
type: string
dry_run:
description: 'Dry run β log what would happen without creating tags or publishing'
required: false
default: true
type: boolean
force_prepare:
description: 'Force create/update the Release PR even if no new bumps are detected'
required: false
default: false
type: boolean
group:
description: 'Release group to target (leave empty for all)'
required: false
type: string
bump_type:
description: 'Override auto-detected bump type'
required: false
default: auto
type: string
prerelease:
description: 'Publish as prerelease (e.g. rc.1, beta.1)'
required: false
type: string
skip_publish:
description: 'Tag and create GitHub Release but skip publishing to registry'
required: false
default: false
type: boolean
concurrency:
description: 'Max parallel publish jobs (0 = auto)'
required: false
default: '0'
type: string
max_retries:
description: 'Max retries for failed publish attempts (0 = no retries)'
required: false
default: '2'
type: string
no_ai:
description: 'Disable all AI features (summarization, codenames)'
required: false
default: false
type: boolean
model:
description: 'Override AI model (e.g. ollama/gemma3:12b)'
required: false
default: ''
type: string
codename_theme:
description: 'Override codename theme (e.g. galaxies, animals)'
required: false
default: ''
type: string
secrets:
GITHUB_TOKEN:
required: false
PYPI_TOKEN:
required: false
TESTPYPI_TOKEN:
required: false
GEMINI_API_KEY:
required: false
RELEASEKIT_APP_PRIVATE_KEY:
description: 'GitHub App private key for CLA-passing commits'
required: false
RELEASEKIT_TOKEN:
description: 'Personal Access Token fallback'
required: false
workflow_dispatch:
inputs:
action:
description: 'Which pipeline stage to run'
required: true
default: release
type: choice
options:
- prepare
- release
target:
description: 'Publish target registry (release only)'
required: true
default: pypi
type: choice
options:
- testpypi
- pypi
dry_run:
description: 'Dry run β log what would happen without creating tags or publishing'
required: true
default: true
type: boolean
force_prepare:
description: 'Force create/update the Release PR even if no new bumps are detected'
required: false
default: false
type: boolean
group:
description: 'Release group to target (leave empty for all)'
required: false
type: string
bump_type:
description: 'Override auto-detected bump type'
required: false
default: auto
type: choice
options:
- auto
- patch
- minor
- major
prerelease:
description: 'Publish as prerelease (e.g. rc.1, beta.1)'
required: false
type: string
skip_publish:
description: 'Tag and create GitHub Release but skip publishing to registry'
required: false
default: false
type: boolean
concurrency:
description: 'Max parallel publish jobs (0 = auto)'
required: false
default: '0'
type: string
max_retries:
description: 'Max retries for failed publish attempts (0 = no retries)'
required: false
default: '2'
type: string
no_ai:
description: 'Disable all AI features (summarization, codenames)'
required: false
default: false
type: boolean
model:
description: 'Override AI model (e.g. ollama/gemma3:12b)'
required: false
type: string
codename_theme:
description: 'Override codename theme (e.g. galaxies, animals)'
required: false
type: string
auth_method:
description: 'Authentication method (auto = detect from configured secrets)'
required: false
default: auto
type: choice
options:
- auto
- app
- pat
- github-token
push:
branches: [main]
paths:
- "py/packages/**"
- "py/plugins/**"
- "python/packages/**"
- "python/plugins/**"
pull_request:
types: [closed]
branches: [main]
# Only one release pipeline runs at a time.
concurrency:
group: releasekit-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: write # Create tags, releases, and push to release branch
pull-requests: write # Open/update Release PRs, manage labels
id-token: write # Sigstore keyless signing (SLSA provenance)
env:
RELEASEKIT_DIR: py/tools/releasekit
WORKSPACE_DIR: py
# Registry URLs resolved from the target input (defaults to prod PyPI).
PUBLISH_INDEX_URL: ${{ inputs.target == 'testpypi' && 'https://test.pypi.org/legacy/' || '' }}
PUBLISH_CHECK_URL: ${{ inputs.target == 'testpypi' && 'https://test.pypi.org/simple/' || 'https://pypi.org/simple/' }}
# Dry-run logic:
# - PR merge (pull_request closed): dry_run=false (this is the one-button release flow)
# - Manual dispatch: uses the checkbox value (default: true)
# - Push to main: dry_run is not relevant (only runs prepare, which is always live)
DRY_RUN: ${{ github.event_name == 'pull_request' && 'false' || (inputs.dry_run == 'false' && 'false' || 'true') }}
jobs:
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# AUTH: Resolve token (GitHub App β PAT β GITHUB_TOKEN)
#
# Supports three auth modes in priority order:
# 1. GitHub App β set RELEASEKIT_APP_ID (variable) +
# RELEASEKIT_APP_PRIVATE_KEY (secret).
# Passes CLA, triggers CI on Release PRs.
# 2. PAT β set RELEASEKIT_TOKEN (secret).
# Passes CLA, triggers CI on Release PRs.
# 3. GITHUB_TOKEN β built-in, no setup needed.
# PRs will NOT trigger CI and may fail CLA checks.
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
auth:
name: Resolve Auth Token
runs-on: ubuntu-latest
timeout-minutes: 2
outputs:
# When the fallback is GITHUB_TOKEN, we output the literal string
# "GITHUB_TOKEN" as a sentinel because the actual secret cannot
# cross job boundaries. Downstream jobs must substitute their own
# secrets.GITHUB_TOKEN when they see this sentinel.
token: ${{ steps.resolve.outputs.token }}
auth-method: ${{ steps.resolve.outputs.auth-method }}
git-user-name: ${{ steps.resolve.outputs.git-user-name }}
git-user-email: ${{ steps.resolve.outputs.git-user-email }}
steps:
# Option 1: GitHub App (preferred β passes CLA, triggers CI).
- name: Generate GitHub App token
if: >
(inputs.auth_method == 'auto' || inputs.auth_method == 'app' || inputs.auth_method == '')
&& vars.RELEASEKIT_APP_ID != ''
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.RELEASEKIT_APP_ID }}
private-key: ${{ secrets.RELEASEKIT_APP_PRIVATE_KEY }}
- name: Get App bot user ID
if: steps.app-token.outcome == 'success'
id: app-user
run: |
if ! user_id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id 2>/dev/null); then
echo "::warning::Failed to fetch App bot user ID β using 0 as fallback"
user_id=0
fi
echo "user-id=$user_id" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
# Resolve: App > PAT > GITHUB_TOKEN (respects auth_method override).
- name: Resolve token and identity
id: resolve
run: |
AUTH="${{ inputs.auth_method || 'auto' }}"
# App token β used when auth=auto (and available) or auth=app.
if [ "$AUTH" = "app" ] || { [ "$AUTH" = "auto" ] && [ -n "$APP_TOKEN" ]; }; then
{ echo "token=$APP_TOKEN"
echo "auth-method=app"
echo "git-user-name=${{ steps.app-token.outputs.app-slug }}[bot]"
echo "git-user-email=${{ steps.app-user.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com"
} >> "$GITHUB_OUTPUT"
echo "::notice::Using GitHub App token (${{ steps.app-token.outputs.app-slug }})"
# PAT β used when auth=auto (and available) or auth=pat.
elif [ "$AUTH" = "pat" ] || { [ "$AUTH" = "auto" ] && [ -n "$PAT_TOKEN" ]; }; then
{ echo "token=$PAT_TOKEN"
echo "auth-method=pat"
echo "git-user-name=${GIT_USER_NAME:-releasekit[bot]}"
echo "git-user-email=${GIT_USER_EMAIL:-releasekit[bot]@users.noreply.github.com}"
} >> "$GITHUB_OUTPUT"
echo "::notice::Using Personal Access Token"
# GITHUB_TOKEN β fallback or explicit. Uses repo variables for
# git identity if set, so CLA can pass with a signed identity.
# NOTE: We output the sentinel "GITHUB_TOKEN" instead of the
# actual secret because secrets.GITHUB_TOKEN cannot be passed
# across job boundaries via outputs.
else
{ echo "token=GITHUB_TOKEN"
echo "auth-method=github-token"
echo "git-user-name=${GIT_USER_NAME:-github-actions[bot]}"
echo "git-user-email=${GIT_USER_EMAIL:-github-actions[bot]@users.noreply.github.com}"
} >> "$GITHUB_OUTPUT"
if [ -n "$GIT_USER_NAME" ]; then
echo "::notice::Using GITHUB_TOKEN with custom identity ($GIT_USER_NAME)"
else
echo "::warning::Using GITHUB_TOKEN β PRs will not trigger CI and may fail CLA checks. Set RELEASEKIT_GIT_USER_NAME and RELEASEKIT_GIT_USER_EMAIL repo variables to use a CLA-signed identity."
fi
fi
env:
APP_TOKEN: ${{ steps.app-token.outputs.token }}
PAT_TOKEN: ${{ secrets.RELEASEKIT_TOKEN }}
GIT_USER_NAME: ${{ vars.RELEASEKIT_GIT_USER_NAME }}
GIT_USER_EMAIL: ${{ vars.RELEASEKIT_GIT_USER_EMAIL }}
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# PREPARE: Compute bumps and open/update Release PR
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
prepare:
name: Prepare Release PR
needs: auth
if: |
(github.event_name == 'push' &&
!startsWith(github.event.head_commit.message, 'chore(release):') &&
!contains(github.event.head_commit.message, 'releasekit--release')) ||
(github.event_name == 'workflow_dispatch' && inputs.action == 'prepare')
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
has_bumps: ${{ steps.prepare.outputs.has_bumps }}
pr_url: ${{ steps.prepare.outputs.pr_url }}
env:
# Resolve the sentinel: use the job's own GITHUB_TOKEN when auth
# fell back to the built-in token (it cannot cross job boundaries).
RESOLVED_TOKEN: ${{ needs.auth.outputs.auth-method == 'github-token' && secrets.GITHUB_TOKEN || needs.auth.outputs.token }}
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-releasekit
with:
token: ${{ env.RESOLVED_TOKEN }}
releasekit-dir: ${{ env.RELEASEKIT_DIR }}
git-user-name: ${{ needs.auth.outputs.git-user-name }}
git-user-email: ${{ needs.auth.outputs.git-user-email }}
- uses: ./.github/actions/run-releasekit
id: prepare
with:
command: prepare
workspace: py
releasekit-dir: ${{ env.RELEASEKIT_DIR }}
group: ${{ inputs.group }}
bump-type: ${{ inputs.bump_type }}
prerelease: ${{ inputs.prerelease }}
force: ${{ inputs.force_prepare }}
no-ai: ${{ inputs.no_ai && 'true' || 'false' }}
model: ${{ inputs.model }}
codename-theme: ${{ inputs.codename_theme }}
env:
GH_TOKEN: ${{ env.RESOLVED_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# RELEASE: Tag merge commit and create GitHub Release
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
release:
name: Tag and Release
needs: auth
if: |
(github.event_name == 'pull_request' &&
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'autorelease: pending')) ||
(github.event_name == 'workflow_dispatch' && inputs.action == 'release')
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
release_url: ${{ steps.release.outputs.release_url }}
env:
RESOLVED_TOKEN: ${{ needs.auth.outputs.auth-method == 'github-token' && secrets.GITHUB_TOKEN || needs.auth.outputs.token }}
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-releasekit
with:
token: ${{ env.RESOLVED_TOKEN }}
releasekit-dir: ${{ env.RELEASEKIT_DIR }}
git-user-name: ${{ needs.auth.outputs.git-user-name }}
git-user-email: ${{ needs.auth.outputs.git-user-email }}
- uses: ./.github/actions/run-releasekit
id: release
with:
command: release
workspace: py
releasekit-dir: ${{ env.RELEASEKIT_DIR }}
dry-run: ${{ env.DRY_RUN }}
show-plan: "true"
no-ai: ${{ inputs.no_ai && 'true' || 'false' }}
model: ${{ inputs.model }}
codename-theme: ${{ inputs.codename_theme }}
env:
GH_TOKEN: ${{ env.RESOLVED_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# PUBLISH: Build and publish packages to PyPI
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
publish:
name: Publish to ${{ inputs.target || 'pypi' }}
needs: [auth, release]
if: inputs.skip_publish != 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
environment: ${{ inputs.target || 'pypi' }}
permissions:
id-token: write # Trusted publishing (OIDC) + Sigstore signing
attestations: write # PEP 740 attestations
env:
RESOLVED_TOKEN: ${{ needs.auth.outputs.auth-method == 'github-token' && secrets.GITHUB_TOKEN || needs.auth.outputs.token }}
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-releasekit
with:
token: ${{ env.RESOLVED_TOKEN }}
releasekit-dir: ${{ env.RELEASEKIT_DIR }}
install-system-deps: "true"
workspace-dir: ${{ env.WORKSPACE_DIR }}
enable-ollama: "false"
- uses: ./.github/actions/run-releasekit
with:
command: publish
workspace: py
releasekit-dir: ${{ env.RELEASEKIT_DIR }}
dry-run: ${{ env.DRY_RUN }}
force: "true"
group: ${{ inputs.group }}
concurrency: ${{ inputs.concurrency }}
max-retries: ${{ inputs.max_retries }}
check-url: ${{ env.PUBLISH_CHECK_URL }}
index-url: ${{ env.PUBLISH_INDEX_URL }}
show-plan: "true"
no-ai: ${{ inputs.no_ai && 'true' || 'false' }}
model: ${{ inputs.model }}
codename-theme: ${{ inputs.codename_theme }}
env:
UV_PUBLISH_TOKEN: ${{ inputs.target == 'testpypi' && secrets.TESTPYPI_TOKEN || secrets.PYPI_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
- name: Upload manifest artifact
if: success()
uses: actions/upload-artifact@v4
with:
name: release-manifest
path: ${{ env.WORKSPACE_DIR }}/release-manifest.json
retention-days: 90
- name: Upload SLSA provenance
if: success()
uses: actions/upload-artifact@v4
with:
name: slsa-provenance
path: ${{ env.WORKSPACE_DIR }}/provenance*.intoto.jsonl
retention-days: 90
if-no-files-found: warn
- name: Upload SBOMs
if: success()
uses: actions/upload-artifact@v4
with:
name: sbom
path: |
${{ env.WORKSPACE_DIR }}/sbom.cdx.json
${{ env.WORKSPACE_DIR }}/sbom.spdx.json
retention-days: 90
if-no-files-found: warn
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# VERIFY: Check published packages are installable
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
verify:
name: Verify Published Packages
needs: publish
if: >-
success() &&
(github.event_name == 'pull_request' ||
inputs.dry_run == false)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Wait for PyPI propagation
run: |
echo "Waiting 60 seconds for PyPI CDN propagation..."
sleep 60
- name: Get core version and verify installation
continue-on-error: true # Propagation delays should not fail the pipeline
run: |
VERSION=$(grep '^version' py/packages/genkit/pyproject.toml | head -1 | sed 's/.*= *"//' | sed 's/".*//')
echo "Core version: $VERSION"
pip install --upgrade pip
# Verify core package.
pip install "genkit==$VERSION"
python -c "from genkit.ai import Genkit; print('β
genkit imports successfully')"
# Verify all plugins are installable.
for pyproject in py/plugins/*/pyproject.toml; do
plugin_dir=$(dirname "$pyproject")
plugin_name=$(basename "$plugin_dir")
pkg_name="genkit-plugin-${plugin_name}"
plugin_version=$(grep '^version' "$pyproject" | head -1 | sed 's/.*= *"//' | sed 's/".*//')
echo "Verifying $pkg_name==$plugin_version..."
if pip install "$pkg_name==$plugin_version"; then
echo "β
$pkg_name installed"
else
echo "β οΈ $pkg_name==$plugin_version not found on PyPI (may not have been published)"
fi
done
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# NOTIFY: Post-release notifications
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
notify:
name: Notify Downstream
needs: [auth, release, publish, verify]
if: success()
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Dispatch release event
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ needs.auth.outputs.auth-method == 'github-token' && secrets.GITHUB_TOKEN || needs.auth.outputs.token }}
event-type: genkit-python-release
client-payload: '{"release_url": "${{ needs.release.outputs.release_url }}"}'