Skip to content

API Spec Watch

API Spec Watch #92

name: API Spec Watch
on:
workflow_dispatch:
schedule:
- cron: "15 5 * * *"
permissions:
contents: read
issues: write
jobs:
openapi-drift:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Check OpenAPI drift
id: openapi_check
env:
ISBNDB_OPENAPI_SOURCE_URL: ${{ vars.ISBNDB_OPENAPI_SOURCE_URL }}
ISBNDB_OPENAPI_SOURCE_URL_SECRET: ${{ secrets.ISBNDB_OPENAPI_SOURCE_URL }}
run: |
source_url="${ISBNDB_OPENAPI_SOURCE_URL:-${ISBNDB_OPENAPI_SOURCE_URL_SECRET:-}}"
if [ -z "${source_url}" ]; then
echo "No OpenAPI source URL configured (vars/secrets ISBNDB_OPENAPI_SOURCE_URL)."
echo "Skipping drift check."
echo "configured=false" >> "${GITHUB_OUTPUT}"
echo "drift=false" >> "${GITHUB_OUTPUT}"
exit 0
fi
echo "configured=true" >> "${GITHUB_OUTPUT}"
echo "source_url=${source_url}" >> "${GITHUB_OUTPUT}"
set +e
./scripts/check_api_spec_changes.sh \
--source-url "${source_url}" \
--write-fetched /tmp/isbndb-openapi-candidate.json \
> /tmp/isbndb-openapi-check.log 2>&1
status=$?
set -e
cat /tmp/isbndb-openapi-check.log
if [ "${status}" -eq 0 ]; then
echo "drift=false" >> "${GITHUB_OUTPUT}"
exit 0
fi
if [ "${status}" -eq 2 ]; then
echo "drift=true" >> "${GITHUB_OUTPUT}"
exit 0
fi
exit "${status}"
- name: Create or update drift issue
if: steps.openapi_check.outputs.configured == 'true' && steps.openapi_check.outputs.drift == 'true'
uses: actions/github-script@v7
env:
SOURCE_URL: ${{ steps.openapi_check.outputs.source_url }}
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const title = 'API drift detected in ISBNdb OpenAPI';
const label = 'api-drift';
const runUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`;
const body = [
'The scheduled OpenAPI drift check detected changes against the repository snapshot.',
'',
`- Source URL: ${process.env.SOURCE_URL}`,
`- Workflow run: ${runUrl}`,
'',
'Next steps:',
'1. Download the `isbndb-openapi-candidate` artifact from this run.',
'2. Compare with `api/upstream/isbndb-openapi.json`.',
'3. Update package code/tests/docs if needed, then refresh the snapshot.',
].join('\n');
try {
await github.rest.issues.createLabel({
owner,
repo,
name: label,
color: 'd73a4a',
description: 'OpenAPI drift detected by API Spec Watch',
});
} catch (error) {
if (error.status !== 422) {
throw error;
}
}
const existing = await github.rest.issues.listForRepo({
owner,
repo,
state: 'open',
labels: label,
per_page: 100,
});
const openIssue = existing.data.find((issue) => issue.title === title);
if (openIssue) {
await github.rest.issues.createComment({
owner,
repo,
issue_number: openIssue.number,
body: `Drift detected again.\n\n${body}`,
});
core.info(`Updated existing issue #${openIssue.number}`);
} else {
const created = await github.rest.issues.create({
owner,
repo,
title,
body,
labels: [label],
});
core.info(`Created issue #${created.data.number}`);
}
- name: Upload fetched OpenAPI snapshot
if: always()
uses: actions/upload-artifact@v4
with:
name: isbndb-openapi-candidate
path: |
/tmp/isbndb-openapi-candidate.json
/tmp/isbndb-openapi-check.log
if-no-files-found: ignore
- name: Fail when drift is detected
if: steps.openapi_check.outputs.drift == 'true'
run: |
echo "OpenAPI drift detected against api/upstream/isbndb-openapi.json."
exit 1