Files
2026-07-13 13:12:00 +08:00

145 lines
6.3 KiB
YAML

name: Sync OpenAPI to site
# Keeps the public API reference on the omnigent website in sync with
# the spec generated here. When openapi.json changes on main, copy it
# into omnigent-site/public/openapi.json and open (or update) a PR there.
#
# Like doc-sync, this stages onto the per-minor docs branch `X.Y-docs`
# (derived from omnigent/version.py) rather than site `main`: the spec on
# main describes the NEXT unreleased version, so the API reference is held
# back until release, when publish-changelog merges `X.Y-docs → main`.
#
# Cross-repo writes can't use the workflow's own GITHUB_TOKEN (it's
# scoped to this repo), so we mint a short-lived token from the
# omnigent-ci GitHub App — the same App used by oss-regen-on-comment.yml
# — scoped to omnigent-site. The App must be installed on omnigent-site
# with contents + pull-requests write.
on:
push:
branches: [main]
paths: [openapi.json]
# Manual trigger for backfills / re-syncs after editing this workflow.
workflow_dispatch:
# One sync at a time; a newer spec supersedes an in-flight run.
concurrency:
group: sync-openapi-to-site
cancel-in-progress: true
permissions:
contents: read
jobs:
sync:
name: Open sync PR on omnigent-site
runs-on: ubuntu-latest
# Skip cleanly on forks / installs where the App isn't configured,
# rather than failing the token step with a confusing error.
if: ${{ vars.OMNIGENT_BOT_APP_ID != '' }}
env:
SYNC_BRANCH: auto/openapi-sync
TARGET_REPO: ${{ github.repository_owner }}/omnigent-site
steps:
- name: Checkout omnigent (spec source)
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
path: omnigent
# Derive the per-minor docs staging branch from the runtime version
# (0.5.0.dev0 → "0.5-docs"), matching doc-sync so both stage together.
- name: Resolve docs branch
id: docsbranch
run: |
set -euo pipefail
minor="$(python3 - <<'PYEOF'
import pathlib, re
text = pathlib.Path("omnigent/omnigent/version.py").read_text()
m = re.search(r'VERSION\s*=\s*["\']([0-9]+)\.([0-9]+)', text)
if not m:
raise SystemExit("could not parse X.Y from omnigent/omnigent/version.py")
print(f"{m.group(1)}.{m.group(2)}")
PYEOF
)"
echo "branch=${minor}-docs" >> "$GITHUB_OUTPUT"
echo "::notice::OpenAPI ref stages on branch ${minor}-docs"
- name: Mint App token for omnigent-site
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ vars.OMNIGENT_BOT_APP_ID }}
private-key: ${{ secrets.OMNIGENT_BOT_APP_KEY }}
owner: ${{ github.repository_owner }}
repositories: omnigent-site
- name: Checkout omnigent-site (sync target)
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ env.TARGET_REPO }}
token: ${{ steps.app-token.outputs.token }}
path: site
# Base the sync on the docs branch, not main. Create it off the default
# branch's tip if this is the cycle's first stage (idempotent — a concurrent
# doc-sync run may have created it already).
- name: Switch site checkout to docs branch
working-directory: site
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
DOCS_BRANCH: ${{ steps.docsbranch.outputs.branch }}
run: |
set -euo pipefail
git config user.name "omnigent-ci[bot]"
git config user.email "294685417+omnigent-ci[bot]@users.noreply.github.com"
if git ls-remote --exit-code --heads origin "$DOCS_BRANCH" >/dev/null 2>&1; then
git fetch origin "$DOCS_BRANCH"
git switch -C "$DOCS_BRANCH" FETCH_HEAD
else
git switch -C "$DOCS_BRANCH"
git push origin "$DOCS_BRANCH" \
|| echo "::notice::${DOCS_BRANCH} already created by a concurrent run — reusing it."
fi
- name: Copy spec into the site
run: cp omnigent/openapi.json site/public/openapi.json
# Commit + push to a fixed branch and open a PR if one isn't
# already open. If a PR exists, the force-push updates it in place
# — so repeated spec changes collapse into a single rolling PR.
- name: Open or update sync PR
working-directory: site
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
DOCS_BRANCH: ${{ steps.docsbranch.outputs.branch }}
run: |
if [ -z "$(git status --porcelain -- public/openapi.json)" ]; then
echo "openapi.json already in sync — nothing to do."
exit 0
fi
# user.name/email already set by the branch-switch step.
git switch -C "$SYNC_BRANCH"
git add public/openapi.json
git commit -m "chore(api): sync openapi.json from omnigent@${GITHUB_SHA:0:7}"
git push --force origin "$SYNC_BRANCH"
# auto/openapi-sync is a rolling branch reused across cycles, but its PR
# base tracks the current docs branch — so retarget an already-open PR if
# the cycle rolled over (e.g. 0.5-docs → 0.6-docs after a release).
existing="$(gh pr list --head "$SYNC_BRANCH" --state open --json number --jq '.[0].number // empty')"
if [ -n "$existing" ]; then
gh pr edit "$existing" --base "$DOCS_BRANCH" >/dev/null 2>&1 || true
echo "PR #$existing already open for $SYNC_BRANCH (base $DOCS_BRANCH) — the force-push updated it."
exit 0
fi
# Build the body with printf so YAML block indentation never
# leaks leading spaces into the Markdown.
short="${GITHUB_SHA:0:7}"
body="$(printf 'Automated sync of `public/openapi.json` from [omnigent@`%s`](https://github.com/%s/commit/%s).\n\nStaged on `%s` (the per-minor docs branch); publishes the updated API reference at `/reference` when that branch merges to main at release.' "$short" "$GITHUB_REPOSITORY" "$GITHUB_SHA" "$DOCS_BRANCH")"
gh pr create \
--base "$DOCS_BRANCH" \
--head "$SYNC_BRANCH" \
--title "chore(api): sync OpenAPI reference from omnigent" \
--body "$body"