name: Generate Client
on:
schedule:
- cron: '0 2 * * *'
workflow_dispatch:
concurrency:
group: generate-client
cancel-in-progress: true
permissions:
contents: write
pull-requests: write
jobs:
generate:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Create GitHub App token
id: app-token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ secrets.BOT_APP_ID }}
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
- name: Install uv
uses: astral-sh/setup-uv@v8.1.0
with:
enable-cache: false
python-version: '3.12.4'
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y curl ripgrep
- name: Generate client
env:
OPENAPI_GENERATOR_LOG_LEVEL: error
run: |
# The script handles everything internally:
# - Downloads latest Stainless OpenAPI spec
# - Runs 2-layer patching pipeline
# - Generates using Docker
# - Applies post-generation patches
chmod +x ./scripts/generate.sh
./scripts/generate.sh
- name: Verify generation succeeded
run: |
# Ensure critical files exist
test -f src/lib.rs || (echo "Error: src/lib.rs not found after generation" && exit 1)
test -f Cargo.toml || (echo "Error: Cargo.toml not found after generation" && exit 1)
# Verify the code compiles and passes checks
echo "๐จ Building..."
cargo build --all-features
echo "๐ Running clippy..."
cargo clippy --all-features --all-targets -- -D warnings
echo "๐งช Running tests..."
cargo test --all-features
- name: Cleanup transient files
run: |
rm -rf scripts/__pycache__ || true
- name: Check for changes
id: changes
run: |
# Only consider generated outputs from the pipeline
ALLOWED_PATHS=(
src
docs
README.md
Cargo.toml
Cargo.lock
stainless.yaml
.openapi-generator
.openapi-generator-ignore
)
if [ -n "$(git status --porcelain -- "${ALLOWED_PATHS[@]}")" ]; then
echo "changes=true" >> $GITHUB_OUTPUT
else
echo "changes=false" >> $GITHUB_OUTPUT
fi
- name: Create Pull Request
id: cpr
if: steps.changes.outputs.changes == 'true'
uses: peter-evans/create-pull-request@v8
with:
token: ${{ steps.app-token.outputs.token }}
commit-message: "chore: update generated client from latest OpenAPI spec"
title: "chore: update generated client"
body: |
## ๐ค Automated Client Update
This PR updates the generated client based on the latest Stainless OpenAPI specification.
### Changes
- Downloaded latest spec from Stainless API
- Applied Layer 1 patches (model field fixes)
- Applied Layer 2 patches (Rust compatibility)
- Generated new client code
- Applied post-generation patches
### Verification
โ
Code compiles successfully
โ
All tests pass
โ
Clippy checks pass
Please review the changes and merge if everything looks correct.
branch: auto-update/client-generation
delete-branch: true
labels: |
automated
dependencies
automerge
add-paths: |
src/**
docs/**
README.md
Cargo.toml
Cargo.lock
stainless.yaml
.openapi-generator-ignore
- name: Enable auto-merge for generated PR
if: steps.changes.outputs.changes == 'true' && steps.cpr.outputs.pull-request-number != ''
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
gh pr merge "${{ steps.cpr.outputs.pull-request-number }}" --auto --rebase --delete-branch
- name: Assert App token present
run: |
if [ -z "${{ steps.app-token.outputs.token }}" ]; then
echo "GitHub App token missing. Ensure BOT_APP_ID and BOT_PRIVATE_KEY are configured." >&2
exit 1
fi
keepalive:
runs-on: ubuntu-latest
steps:
- name: Keepalive Workflow
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const workflow_id = 'generate-client.yml';
const workflow = await github.rest.actions.getWorkflow({ owner, repo, workflow_id });
console.log(`Workflow ${workflow.data.name} is ${workflow.data.state}`);
if (workflow.data.state === 'disabled_inactivity') {
await github.rest.actions.enableWorkflow({ owner, repo, workflow_id });
console.log('Workflow re-enabled');
}
const runs = await github.rest.actions.listWorkflowRuns({ owner, repo, workflow_id, per_page: 1 });
if (runs.data.workflow_runs.length > 0) {
const lastRun = runs.data.workflow_runs[0];
console.log(`Last run: ${lastRun.created_at} - Status: ${lastRun.status}`);
}