145 lines
6.3 KiB
YAML
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"
|