name: Refresh discovery index
on:
schedule:
- cron: "17 4 * * *"
workflow_dispatch:
permissions:
contents: read
concurrency:
group: discovery-refresh
cancel-in-progress: true
jobs:
refresh:
name: refresh
runs-on: ubuntu-26.04-arm
timeout-minutes: 15
permissions:
contents: write
pull-requests: write
actions: write
steps:
- uses: actions/checkout@v7
timeout-minutes: 2
- uses: actions/setup-node@v7
timeout-minutes: 5
with:
node-version: "26"
- name: Refetch, distil, and regenerate the command tree
id: refresh
timeout-minutes: 5
run: |
node scripts/fetch-discovery.ts
# The CLI command tree, the agent skills, and the site's coverage
# table are all generated from the index, so an index change must
# carry all of them in the same PR — they can never drift apart.
node scripts/generate-commands.ts
node scripts/generate-skills.ts
node scripts/generate-coverage.ts
# `git status --porcelain` rather than `git diff --quiet`, because a
# brand-new service makes Google ship a discovery doc that has no
# tracked file yet: `git diff` sees nothing for an untracked path, so
# the one update most worth a PR would have opened none.
if [ -z "$(git status --porcelain -- src/discovery src/commands/generated.rs skills site/src/data/discovery-coverage.ts)" ]; then
echo "changed=false" >> "$GITHUB_OUTPUT"
echo "Discovery index is already current."
else
echo "changed=true" >> "$GITHUB_OUTPUT"
git --no-pager diff --stat -- src/discovery src/commands/generated.rs site/src/data/discovery-coverage.ts
fi
- uses: dtolnay/rust-toolchain@nightly
if: steps.refresh.outputs.changed == 'true'
timeout-minutes: 5
with:
components: rust-src
- uses: Swatinem/rust-cache@v2
if: steps.refresh.outputs.changed == 'true'
timeout-minutes: 5
- name: Verify the Rust side still builds against the new index
if: steps.refresh.outputs.changed == 'true'
timeout-minutes: 10
run: cargo test --lib
- name: Summarise the delta for the PR body
if: steps.refresh.outputs.changed == 'true'
id: summary
timeout-minutes: 2
run: |
TABLE=$(node -e "
const fs=require('fs');
const cur=JSON.parse(fs.readFileSync('src/discovery/manifest.json','utf8'));
const rows=Object.entries(cur.services)
.map(([n,i]) => '| '+n+' | '+i.methods+' | '+i.scopes+' | '+i.revision+' |');
console.log(rows.join('\n'));
")
{
echo "summary<<EOF"
echo "$TABLE"
echo "EOF"
} >> "$GITHUB_OUTPUT"
{
echo '### Discovery index after refresh'
echo
echo '| service | methods | scopes | revision |'
echo '|---|---|---|---|'
echo "$TABLE"
} >> "$GITHUB_STEP_SUMMARY"
- name: Open a pull request
id: pr
if: steps.refresh.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v8
timeout-minutes: 5
with:
author: "debanjanbasu <10209115+debanjanbasu@users.noreply.github.com>"
committer: "debanjanbasu <10209115+debanjanbasu@users.noreply.github.com>"
branch: chore/discovery-refresh
title: "chore(discovery): refresh Google Discovery index"
commit-message: "chore(discovery): refresh Google Discovery index"
labels: dependencies
add-paths: |
src/discovery
src/commands/generated.rs
skills
site/src/data/discovery-coverage.ts
body: |
Automated refresh of `src/discovery/` from Google's Discovery
Service, run by `.github/workflows/discovery.yml`. The
regenerated command tree (`src/commands/generated.rs`), the agent
skills (`skills/**`), and the site's coverage table
(`site/src/data/discovery-coverage.ts`) ride along, so the PR
always contains the index and everything derived from it in
lockstep.
The Rust tests ran against this index, so a method whose shape
changed incompatibly would have failed the build rather than
shipping.
| service | methods | scopes | discovery revision |
|---|---|---|---|
${{ steps.summary.outputs.summary }}
Review the diff for surprising removals — a method disappearing
usually means Google renamed it upstream.
- name: Wake the merge poller
if: steps.pr.outputs.pull-request-number != ''
timeout-minutes: 2
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: gh workflow run automation-merge.yml --ref main || echo "::warning::poller dispatch failed"