From 8c7c93894853fd7ebaa7e75cfdc025a503bf8630 Mon Sep 17 00:00:00 2001 From: eliebak Date: Fri, 21 Aug 2026 23:51:08 +0200 Subject: [PATCH 1/2] ci: refresh the model catalog daily via scheduled workflow Adds a scheduled workflow (05:17 UTC daily, plus workflow_dispatch) that runs `npm run generate-models` in packages/ai and opens a PR on bot/update-model-catalog when the committed snapshot drifted from the live catalogs. Reruns force-push the same branch, so at most one refresh PR is open at a time. Because generate-models exits 0 when a source fetch fails and most sources fall back to an empty list rather than the snapshot, the job refuses to open a PR when the regenerated catalog lost more than 10% of its models versus the committed file. Bot-authored PRs already pass the contribution gate (bots are allowed automatically) and skip the changelog-fragment check. Known caveat: PRs opened with the default GITHUB_TOKEN do not trigger CI; push to the branch or close/reopen the PR, or swap in a PAT/App token. Co-Authored-By: Claude Fable 5 --- .github/workflows/update-model-catalog.yml | 105 +++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 .github/workflows/update-model-catalog.yml diff --git a/.github/workflows/update-model-catalog.yml b/.github/workflows/update-model-catalog.yml new file mode 100644 index 0000000000..aec4bbfed3 --- /dev/null +++ b/.github/workflows/update-model-catalog.yml @@ -0,0 +1,105 @@ +name: Update model catalog + +on: + schedule: + # Daily at 05:17 UTC; off the top of the hour to dodge the cron rush. + - cron: '17 5 * * *' + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +concurrency: + group: update-model-catalog + cancel-in-progress: false + +env: + UPDATE_BRANCH: bot/update-model-catalog + CATALOG_FILE: packages/ai/src/models.generated.ts + +jobs: + regenerate: + name: Regenerate and open PR + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + # Credentials stay persisted (checkout default): the push step below + # needs them. The job only ever pushes to $UPDATE_BRANCH. + + - name: Setup Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 22 + cache: npm + + # npm ci runs every workspace's install scripts, and some need the same + # native libraries CI installs (node-canvas builds against cairo/pango). + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev + + - name: Install dependencies + run: npm ci + + - name: Regenerate model catalog + working-directory: packages/ai + run: npm run generate-models + + - name: Guard against gutted providers + # generate-models exits 0 even when a source fetch fails, and most + # failed sources fall back to an empty list rather than the committed + # snapshot. A large drop in total models means a source silently + # failed, so refuse to open a PR from that state. + run: | + old=$(git show "HEAD:$CATALOG_FILE" | grep -c 'satisfies Model') + new=$(grep -c 'satisfies Model' "$CATALOG_FILE") + echo "Committed snapshot: $old models; regenerated: $new models" + if [ "$new" -lt $(( old * 9 / 10 )) ]; then + echo "::error::Regenerated catalog dropped from $old to $new models; a provider fetch likely failed." + exit 1 + fi + + - name: Detect changes + id: diff + run: | + if git diff --quiet -- "$CATALOG_FILE"; then + echo "Catalog unchanged; nothing to do." + echo "changed=false" >> "$GITHUB_OUTPUT" + else + git diff --stat -- "$CATALOG_FILE" + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Create or update pull request + if: steps.diff.outputs.changed == 'true' + env: + GH_TOKEN: ${{ github.token }} + # Note: PRs opened with the default GITHUB_TOKEN do not trigger + # pull_request workflows (CI). Push to the branch or close/reopen the + # PR to run CI, or swap github.token for a PAT/App token here. + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + old=$(git show "HEAD:$CATALOG_FILE" | grep -c 'satisfies Model') + new=$(grep -c 'satisfies Model' "$CATALOG_FILE") + + git switch -c "$UPDATE_BRANCH" + git add "$CATALOG_FILE" + git commit -m "chore(ai): refresh the generated model catalog from live provider catalogs" + git push -f origin "$UPDATE_BRANCH" + + open_prs=$(gh pr list --head "$UPDATE_BRANCH" --base main --state open --json number --jq length) + if [ "$open_prs" = "0" ]; then + gh pr create \ + --base main \ + --head "$UPDATE_BRANCH" \ + --title "chore(ai): refresh the generated model catalog from live provider catalogs" \ + --body "$(printf 'Automated daily refresh of `%s` from the live provider catalogs (models.dev, OpenRouter, Vercel AI Gateway, Prime Inference).\n\nModel count: %s -> %s. Review the diff for pricing changes, renames, and removals before merging.\n\nCI does not run automatically on PRs opened by the workflow token; push to the branch or close/reopen to trigger it.' "$CATALOG_FILE" "$old" "$new")" + else + echo "Open PR for $UPDATE_BRANCH already exists; force-pushed the refreshed catalog to it." + fi From 6365b2417969fbf0fb353cb24c18a35efaeabfe7 Mon Sep 17 00:00:00 2001 From: Seth Date: Fri, 21 Aug 2026 16:18:10 -0700 Subject: [PATCH 2/2] fix(ci): keep model catalog refresh PRs current --- .github/workflows/update-model-catalog.yml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/update-model-catalog.yml b/.github/workflows/update-model-catalog.yml index aec4bbfed3..e74212825d 100644 --- a/.github/workflows/update-model-catalog.yml +++ b/.github/workflows/update-model-catalog.yml @@ -26,8 +26,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - # Credentials stay persisted (checkout default): the push step below - # needs them. The job only ever pushes to $UPDATE_BRANCH. + with: + ref: ${{ github.event.repository.default_branch }} - name: Setup Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 @@ -78,28 +78,29 @@ jobs: if: steps.diff.outputs.changed == 'true' env: GH_TOKEN: ${{ github.token }} - # Note: PRs opened with the default GITHUB_TOKEN do not trigger - # pull_request workflows (CI). Push to the branch or close/reopen the - # PR to run CI, or swap github.token for a PAT/App token here. + # GITHUB_TOKEN-created PRs need a maintainer to close/reopen them + # before merge so pull_request CI runs. run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" old=$(git show "HEAD:$CATALOG_FILE" | grep -c 'satisfies Model') new=$(grep -c 'satisfies Model' "$CATALOG_FILE") + body=$(printf 'Automated daily refresh of `%s` from the live provider catalogs (models.dev, OpenRouter, Vercel AI Gateway, Prime Inference).\n\nModel count: %s -> %s. Review the diff for pricing changes, renames, and removals before merging.\n\nCI does not run automatically on PRs opened by the workflow token. Close and reopen this PR to trigger it.' "$CATALOG_FILE" "$old" "$new") git switch -c "$UPDATE_BRANCH" git add "$CATALOG_FILE" git commit -m "chore(ai): refresh the generated model catalog from live provider catalogs" git push -f origin "$UPDATE_BRANCH" - open_prs=$(gh pr list --head "$UPDATE_BRANCH" --base main --state open --json number --jq length) - if [ "$open_prs" = "0" ]; then + open_pr=$(gh pr list --head "$UPDATE_BRANCH" --base main --state open --json number --jq '.[0].number // empty') + if [ -z "$open_pr" ]; then gh pr create \ --base main \ --head "$UPDATE_BRANCH" \ --title "chore(ai): refresh the generated model catalog from live provider catalogs" \ - --body "$(printf 'Automated daily refresh of `%s` from the live provider catalogs (models.dev, OpenRouter, Vercel AI Gateway, Prime Inference).\n\nModel count: %s -> %s. Review the diff for pricing changes, renames, and removals before merging.\n\nCI does not run automatically on PRs opened by the workflow token; push to the branch or close/reopen to trigger it.' "$CATALOG_FILE" "$old" "$new")" + --body "$body" else - echo "Open PR for $UPDATE_BRANCH already exists; force-pushed the refreshed catalog to it." + gh pr edit "$open_pr" --body "$body" + echo "Updated pull request #$open_pr with the refreshed catalog." fi