mirror of
https://github.com/earendil-works/pi.git
synced 2026-09-28 05:54:43 +08:00
fix(coding-agent): dispatch compatibility checks from PR gate
This commit is contained in:
@@ -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');
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user