fix(coding-agent): dispatch compatibility checks from PR gate

This commit is contained in:
Christian Klotz
2026-09-21 09:16:28 +02:00
parent 851072dc06
commit d8569a496f
2 changed files with 50 additions and 149 deletions
@@ -1,148 +0,0 @@
name: pi.dev model catalog compatibility
on:
pull_request_target:
branches: [main]
types: [opened, reopened, synchronize, ready_for_review]
permissions: {}
concurrency:
group: pi-dev-model-catalog-compatibility-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
authorize:
name: Authorize contributor
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
approved: ${{ steps.authorize.outputs.approved }}
steps:
- name: Check contributor approval
id: authorize
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const trustedBots = new Set(['dependabot[bot]', 'sentry[bot]', 'claude[bot]']);
const author = context.payload.pull_request.user.login;
let approved = trustedBots.has(author);
if (!approved && !author.endsWith('[bot]')) {
try {
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
owner: context.repo.owner,
repo: context.repo.repo,
username: author,
});
approved = ['admin', 'maintain', 'write'].includes(data.permission);
} catch {
approved = false;
}
}
if (!approved && !author.endsWith('[bot]')) {
const { data } = await github.rest.repos.getContent({
owner: context.repo.owner,
repo: context.repo.repo,
path: '.github/APPROVED_CONTRIBUTORS',
ref: context.payload.repository.default_branch,
});
if (!('content' in data) || typeof data.content !== 'string') {
throw new Error('Expected .github/APPROVED_CONTRIBUTORS to be a file');
}
const capabilities = new Map();
for (const rawLine of Buffer.from(data.content, 'base64').toString('utf8').split('\n')) {
const line = rawLine.trim();
if (!line || line.startsWith('#')) continue;
const parts = line.split(/\s+/);
if (parts.length !== 2) continue;
const [username, capability] = parts;
const normalizedCapability = capability.toLowerCase();
if (normalizedCapability === 'issue' || normalizedCapability === 'pr') {
capabilities.set(username.toLowerCase(), normalizedCapability);
}
}
approved = capabilities.get(author.toLowerCase()) === 'pr';
}
core.setOutput('approved', String(approved));
dispatch:
if: ${{ needs.authorize.outputs.approved == 'true' && !github.event.pull_request.draft }}
name: Dispatch private compatibility tests
needs: authorize
runs-on: ubuntu-latest
permissions:
statuses: write
steps:
- name: Set pending status
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PI_SHA: ${{ github.event.pull_request.head.sha }}
SOURCE_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
with:
github-token: ${{ github.token }}
script: |
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: process.env.PI_SHA,
state: 'pending',
context: 'pi.dev / model catalog compatibility',
description: 'Waiting for private pi.dev compatibility tests',
target_url: process.env.SOURCE_RUN_URL,
});
- name: Dispatch tests
id: dispatch
continue-on-error: true
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PI_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}
PI_SHA: ${{ github.event.pull_request.head.sha }}
PI_PR_NUMBER: ${{ github.event.pull_request.number }}
with:
github-token: ${{ secrets.PI_DEV_PAT }}
script: |
const sha = process.env.PI_SHA;
const [headOwner, headRepo, ...extraParts] = process.env.PI_REPOSITORY.split('/');
if (!headOwner || !headRepo || extraParts.length > 0 || !/^[0-9a-f]{40}$/.test(sha)) {
core.setFailed('Invalid pull request repository or commit SHA');
return;
}
await github.rest.repos.createDispatchEvent({
owner: 'earendil-works',
repo: 'pi.dev',
event_type: 'pi-model-catalog-compatibility',
client_payload: {
repository: `${headOwner}/${headRepo}`,
sha,
pr_number: Number(process.env.PI_PR_NUMBER),
},
});
- name: Report dispatch failure
if: steps.dispatch.outcome == 'failure'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PI_SHA: ${{ github.event.pull_request.head.sha }}
SOURCE_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
with:
github-token: ${{ github.token }}
script: |
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: process.env.PI_SHA,
state: 'error',
context: 'pi.dev / model catalog compatibility',
description: 'Could not dispatch pi.dev compatibility tests',
target_url: process.env.SOURCE_RUN_URL,
});
core.setFailed('Could not dispatch pi.dev compatibility tests');
+50 -1
View File
@@ -2,17 +2,24 @@ name: PR Gate
on:
pull_request_target:
types: [opened]
types: [opened, reopened, synchronize, ready_for_review]
concurrency:
group: pr-gate-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
check-contributor:
runs-on: ubuntu-latest
outputs:
approved: ${{ steps.check.outputs.approved }}
permissions:
contents: read
issues: write
pull-requests: write
steps:
- name: Check if contributor is approved
id: check
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
@@ -22,9 +29,11 @@ jobs:
const prAuthor = context.payload.pull_request.user.login;
const defaultBranch = context.payload.repository.default_branch;
const isBotAuthor = prAuthor.endsWith('[bot]');
core.setOutput('approved', 'false');
if (TRUSTED_BOT_AUTHORS.has(prAuthor)) {
console.log(`Skipping trusted bot: ${prAuthor}`);
core.setOutput('approved', 'true');
return;
}
@@ -101,6 +110,7 @@ jobs:
const permission = await getPermission(prAuthor);
if (!isBotAuthor && ['admin', 'maintain', 'write'].includes(permission)) {
console.log(`${prAuthor} is a collaborator with ${permission} access`);
core.setOutput('approved', 'true');
return;
}
@@ -110,6 +120,7 @@ jobs:
if (!isBotAuthor && capability === 'pr') {
console.log(`${prAuthor} is approved for PRs`);
core.setOutput('approved', 'true');
return;
}
@@ -126,3 +137,41 @@ jobs:
].join('\n');
await closePullRequest(message);
dispatch-pi-dev:
if: ${{ needs.check-contributor.outputs.approved == 'true' && !github.event.pull_request.draft }}
name: Dispatch pi.dev compatibility tests
needs: check-contributor
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Dispatch tests
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PI_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}
PI_SHA: ${{ github.event.pull_request.head.sha }}
PI_PR_NUMBER: ${{ github.event.pull_request.number }}
with:
github-token: ${{ secrets.PI_DEV_PAT }}
script: |
const [owner, repo, ...extraParts] = process.env.PI_REPOSITORY.split('/');
const sha = process.env.PI_SHA;
const prNumber = Number(process.env.PI_PR_NUMBER);
if (!owner || !repo || extraParts.length > 0 || !/^[0-9a-f]{40}$/.test(sha)) {
throw new Error('Invalid pull request repository or commit SHA');
}
if (!Number.isInteger(prNumber)) {
throw new Error('Invalid pull request number');
}
await github.rest.repos.createDispatchEvent({
owner: 'earendil-works',
repo: 'pi.dev',
event_type: 'pi-model-catalog-compatibility',
client_payload: {
repository: `${owner}/${repo}`,
sha,
pr_number: prNumber,
},
});