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:
app-id: ${{ secrets.BOT_APP_ID }}
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
- name: Generate client
env:
UPDATE_SPEC: true
run: |
# The script handles everything internally:
# - Checks for Docker availability
# - Downloads latest OpenAPI spec (only if changed)
# - Runs generation using Docker
# - Preserves custom files
# - Formats the code
chmod +x ./scripts/generate-openapi-client.sh
./scripts/generate-openapi-client.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)
echo "✅ Generation finished. CI will validate build/test on the PR."
- name: Check for changes
id: check_changes
run: |
# Clean any build artifacts that shouldn't be committed
rm -rf target/
# Check if there are any changes to commit
if git diff --quiet && git diff --staged --quiet; then
echo "No changes detected"
echo "has_changes=false" >> $GITHUB_OUTPUT
else
echo "Changes detected"
echo "has_changes=true" >> $GITHUB_OUTPUT
# Show what changed for the PR description
git diff --stat
fi
- name: Create or Update Pull Request
id: cpr
if: steps.check_changes.outputs.has_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 from latest OpenAPI spec'
base: main
add-paths: |
src/
Cargo.toml
docs/
openapi.yml
body: |
## 🤖 Auto-generated Update
This PR updates the generated client based on the latest OpenAPI specification from Langfuse.
### Changes
- Updated OpenAPI specification (if changed)
- Updated generated code in `src/` from latest OpenAPI spec
- Applied automatic formatting
### Validation
CI checks will run on this PR (build, tests, clippy, fmt).
To verify locally:
- cargo build --all-features
- cargo clippy --all-features --all-targets -- -D warnings
- cargo test --all
---
*This PR was automatically created by the nightly generation workflow.*
branch: automated/generate-client
delete-branch: true
labels: |
dependencies
automated
automerge
- name: Enable auto-merge for generated PR
if: steps.check_changes.outputs.has_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
- name: Update existing PR branch from main
uses: actions/github-script@v8
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const { owner, repo } = context.repo;
const head = 'automated/generate-client';
const prs = await github.paginate(github.rest.pulls.list, { owner, repo, state: 'open', base: 'main', head: `${owner}:${head}` });
if (!prs.length) {
core.info('No open PR for automated/generate-client.');
return;
}
const number = prs[0].number;
core.info(`Updating PR #${number} from base main...`);
try {
await github.rest.pulls.updateBranch({ owner, repo, pull_number: number });
core.info('Branch update triggered.');
} catch (e) {
core.warning(`Update not needed or failed: ${e.message}`);
}
keepalive:
runs-on: ubuntu-latest
steps:
- name: Keepalive Workflow
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// This step ensures the workflow stays active even during periods of inactivity
// GitHub disables scheduled workflows after 60 days of repository inactivity
const owner = context.repo.owner;
const repo = context.repo.repo;
const workflow_id = 'generate-client.yml';
// Get the workflow
const workflow = await github.rest.actions.getWorkflow({
owner,
repo,
workflow_id
});
console.log(`Workflow ${workflow.data.name} is ${workflow.data.state}`);
// If the workflow is disabled, re-enable it
if (workflow.data.state === 'disabled_inactivity') {
await github.rest.actions.enableWorkflow({
owner,
repo,
workflow_id
});
console.log('Workflow re-enabled');
}
// Log the last run to show activity
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}`);
}