Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 234 additions & 0 deletions .github/actions/run-releasekit/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
# 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

# ══════════════════════════════════════════════════════════════════════
# Reusable composite action: Run a releasekit command
#
# Builds the CLI invocation from structured inputs, runs it with
# proper error handling, and parses outputs into GITHUB_OUTPUT.
#
# Supports all three pipeline stages: prepare, release, publish.
# All user-controlled inputs are passed via env vars to prevent
# GitHub Actions script injection.
#
# Outputs:
# has_bumps β€” "true" if prepare found version bumps
# pr_url β€” URL of the created/updated Release PR
# release_url β€” URL of the created GitHub Release
#
# Usage:
#
# - uses: ./.github/actions/run-releasekit
# id: prepare
# with:
# command: prepare
# workspace: py
# releasekit-dir: py/tools/releasekit
# group: ${{ inputs.group }}
# force: ${{ inputs.force_prepare }}
#
# ══════════════════════════════════════════════════════════════════════

name: Run ReleaseKit
description: >-
Run a releasekit command (prepare, release, or publish) with
structured inputs and parsed outputs.

inputs:
command:
description: >-
The releasekit command to run: prepare, release, or publish.
required: true
workspace:
description: >-
Workspace label (e.g. "py", "js", "go").
required: true
releasekit-dir:
description: >-
Path to the releasekit tool directory (relative to repo root).
required: false
default: py/tools/releasekit
dry-run:
description: >-
Set to "true" to pass --dry-run.
required: false
default: "false"
force:
description: >-
Set to "true" to pass --force (prepare and publish).
required: false
default: "false"
group:
description: >-
Release group to target (empty for all).
required: false
default: ""
bump-type:
description: >-
Override bump type: auto, patch, minor, major (prepare only).
required: false
default: "auto"
prerelease:
description: >-
Prerelease suffix, e.g. "rc.1" (prepare only).
required: false
default: ""
concurrency:
description: >-
Max parallel publish jobs, 0 = auto (publish only).
required: false
default: "0"
max-retries:
description: >-
Max retries for failed publishes (publish only).
required: false
default: "0"
check-url:
description: >-
Registry URL for version existence checks (publish only).
required: false
default: ""
index-url:
description: >-
Registry URL for uploading packages (publish only).
required: false
default: ""
show-plan:
description: >-
Show the execution plan before running the command.
required: false
default: "false"

outputs:
has_bumps:
description: "'true' if prepare found version bumps."
value: ${{ steps.run.outputs.has_bumps }}
pr_url:
description: URL of the Release PR (prepare only).
value: ${{ steps.run.outputs.pr_url }}
release_url:
description: URL of the GitHub Release (release only).
value: ${{ steps.run.outputs.release_url }}

runs:
using: composite
steps:
# ── Optional: show execution plan ───────────────────────────────
- name: Preview execution plan
if: inputs.show-plan == 'true'
shell: bash
env:
RK_DIR: ${{ inputs.releasekit-dir }}
RK_WORKSPACE: ${{ inputs.workspace }}
RK_DRY_RUN: ${{ inputs.dry-run }}
run: |
echo "::group::Execution Plan"
uv run --directory "$RK_DIR" \
releasekit --workspace "$RK_WORKSPACE" plan --format full 2>&1 || true
echo "::endgroup::"
if [ "$RK_DRY_RUN" = "true" ]; then
echo "::notice::DRY RUN β€” no side effects"
else
echo "::notice::LIVE RUN"
fi

# ── Build and run the command ───────────────────────────────────
- name: Run releasekit ${{ inputs.command }}
id: run
shell: bash
env:
RK_COMMAND: ${{ inputs.command }}
RK_WORKSPACE: ${{ inputs.workspace }}
RK_DIR: ${{ inputs.releasekit-dir }}
RK_DRY_RUN: ${{ inputs.dry-run }}
RK_FORCE: ${{ inputs.force }}
RK_GROUP: ${{ inputs.group }}
RK_BUMP_TYPE: ${{ inputs.bump-type }}
RK_PRERELEASE: ${{ inputs.prerelease }}
RK_CONCURRENCY: ${{ inputs.concurrency }}
RK_MAX_RETRIES: ${{ inputs.max-retries }}
RK_CHECK_URL: ${{ inputs.check-url }}
RK_INDEX_URL: ${{ inputs.index-url }}
run: |
set -euo pipefail

cmd=(uv run --directory "$RK_DIR" releasekit --workspace "$RK_WORKSPACE" "$RK_COMMAND")

# ── Common flags ──────────────────────────────────────────
if [ "$RK_DRY_RUN" = "true" ]; then
cmd+=(--dry-run)
fi
if [ "$RK_FORCE" = "true" ]; then
cmd+=(--force)
fi
if [ -n "$RK_GROUP" ]; then
cmd+=(--group "$RK_GROUP")
fi

# ── prepare-specific flags ────────────────────────────────
if [ "$RK_COMMAND" = "prepare" ]; then
if [ "$RK_BUMP_TYPE" != "auto" ] && [ -n "$RK_BUMP_TYPE" ]; then
cmd+=(--bump "$RK_BUMP_TYPE")
fi
if [ -n "$RK_PRERELEASE" ]; then
cmd+=(--prerelease "$RK_PRERELEASE")
fi
fi

# ── publish-specific flags ────────────────────────────────
if [ "$RK_COMMAND" = "publish" ]; then
if [ -n "$RK_CHECK_URL" ]; then
cmd+=(--check-url "$RK_CHECK_URL")
fi
if [ -n "$RK_INDEX_URL" ]; then
cmd+=(--index-url "$RK_INDEX_URL")
fi
if [ -n "$RK_CONCURRENCY" ] && [ "$RK_CONCURRENCY" != "0" ]; then
cmd+=(--concurrency "$RK_CONCURRENCY")
fi
if [ -n "$RK_MAX_RETRIES" ] && [ "$RK_MAX_RETRIES" != "0" ]; then
cmd+=(--max-retries "$RK_MAX_RETRIES")
fi
fi

# ── Execute ───────────────────────────────────────────────
echo "::group::Running: ${cmd[*]}"
OUTPUT=$("${cmd[@]}" 2>&1) || EXIT_CODE=$?
echo "$OUTPUT"
echo "::endgroup::"

if [ "${EXIT_CODE:-0}" -ne 0 ]; then
echo "::error::releasekit $RK_COMMAND failed with exit code $EXIT_CODE"
exit "$EXIT_CODE"
fi

# ── Parse outputs ─────────────────────────────────────────
if [ "$RK_COMMAND" = "prepare" ]; then
PR_URL=$(echo "$OUTPUT" | sed -n 's/.*Release PR: //p' | tail -1)
if [ -n "$PR_URL" ]; then
echo "has_bumps=true" >> "$GITHUB_OUTPUT"
echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT"
else
echo "has_bumps=false" >> "$GITHUB_OUTPUT"
fi
fi

if [ "$RK_COMMAND" = "release" ]; then
RELEASE_URL=$(echo "$OUTPUT" | sed -n 's/.*release_url=//p' | tail -1)
if [ -n "$RELEASE_URL" ]; then
echo "release_url=$RELEASE_URL" >> "$GITHUB_OUTPUT"
fi
fi
105 changes: 105 additions & 0 deletions .github/actions/setup-ollama/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# 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

# ══════════════════════════════════════════════════════════════════════
# Reusable composite action: Install Ollama and cache models
#
# Installs the Ollama server, starts it in the background, pulls the
# requested models, and caches them between CI runs using
# actions/cache. The cache key includes the model list so pulling
# different models in different workflows produces separate caches.
#
# Usage:
#
# - uses: ./.github/actions/setup-ollama
# with:
# models: "gemma3:4b"
#
# The Ollama model blobs live under ~/.ollama/models. The cache
# restores this directory before pulling, so only new or updated
# models trigger a download.
# ══════════════════════════════════════════════════════════════════════

name: Setup Ollama
description: Install Ollama, pull models, and cache them between runs.

inputs:
models:
description: >-
Space-separated list of Ollama model tags to pull
(e.g. "gemma3:4b").
required: true
ollama-version:
description: >-
Ollama version to install. "latest" fetches the newest release.
required: false
default: latest

runs:
using: composite
steps:
# ── 1. Restore cached models ────────────────────────────────────
- name: Restore Ollama model cache
id: cache-ollama
uses: actions/cache@v4
with:
path: ~/.ollama/models
# Key includes the model list so different model sets get
# separate caches. The runner OS is included because model
# blobs are platform-independent but the directory layout
# could theoretically differ.
key: ollama-models-${{ runner.os }}-${{ inputs.models }}

# ── 2. Install Ollama ───────────────────────────────────────────
- name: Install Ollama
shell: bash
run: |
curl -fsSL https://ollama.com/install.sh | sh
echo "Ollama version: $(ollama --version)"

# ── 3. Start Ollama server ──────────────────────────────────────
- name: Start Ollama server
shell: bash
run: |
ollama serve &
# Wait for the server to be ready (up to 30 seconds).
for i in $(seq 1 30); do
if curl -sf http://localhost:11434/api/tags >/dev/null 2>&1; then
echo "Ollama server is ready"
break
fi
sleep 1
done

# Final check to ensure the server is ready before proceeding.
if ! curl -sf http://localhost:11434/api/tags >/dev/null 2>&1; then
echo "::error::Ollama server failed to start after 30 seconds."
exit 1
fi

# ── 4. Pull models (skips if already cached) ────────────────────
- name: Pull Ollama models
shell: bash
env:
OLLAMA_MODELS: ${{ inputs.models }}
run: |
for model in $OLLAMA_MODELS; do
echo "::group::Pulling $model"
ollama pull "$model"
echo "::endgroup::"
done
echo "Available models:"
ollama list
Loading
Loading