141 lines
5.2 KiB
YAML
141 lines
5.2 KiB
YAML
name: Run CI
|
|
|
|
on:
|
|
pull_request_target:
|
|
types: [labeled]
|
|
|
|
permissions:
|
|
actions: write
|
|
contents: read
|
|
|
|
jobs:
|
|
trigger:
|
|
name: Trigger CI
|
|
if: github.event.label.name == 'run-ci' || github.event.label.name == 'ready-for-e2e'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Verify and trigger CI
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const adder = context.payload.sender.login;
|
|
const senderType = context.payload.sender.type;
|
|
const pr = context.payload.pull_request;
|
|
|
|
console.log(`Sender: ${adder} (type: ${senderType})`);
|
|
|
|
const trustedBotLogins = ['graphite-app[bot]'];
|
|
let isAuthorized = false;
|
|
|
|
if (senderType === 'Bot' && trustedBotLogins.includes(adder)) {
|
|
console.log(`Authorized: trusted GitHub App`);
|
|
isAuthorized = true;
|
|
}
|
|
|
|
if (!isAuthorized) {
|
|
const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
username: adder,
|
|
});
|
|
|
|
if (!['admin', 'maintain', 'write'].includes(perm.permission)) {
|
|
core.setFailed(`${adder} does not have write access`);
|
|
return;
|
|
}
|
|
|
|
console.log(`Label added by ${adder}`);
|
|
}
|
|
|
|
// Find the latest pr.yml run for this PR's head SHA
|
|
const { data: runs } = await github.rest.actions.listWorkflowRuns({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
workflow_id: 'pr.yml',
|
|
head_sha: pr.head.sha,
|
|
per_page: 5,
|
|
});
|
|
|
|
// Filter runs to this PR (in case same SHA exists in multiple PRs)
|
|
const matchingRuns = runs.workflow_runs.filter(run =>
|
|
!run.pull_requests?.length || run.pull_requests.some(p => p.number === pr.number)
|
|
);
|
|
|
|
if (matchingRuns.length === 0) {
|
|
core.setFailed(`No PR workflow found for SHA ${pr.head.sha}`);
|
|
return;
|
|
}
|
|
|
|
const latestRun = matchingRuns[0];
|
|
|
|
// If workflow is still running, cancel it first then re-run
|
|
if (latestRun.status === 'in_progress' || latestRun.status === 'queued') {
|
|
console.log(`Workflow is running (status: ${latestRun.status}). Cancelling it first...`);
|
|
|
|
const cancelMaxRetries = 10;
|
|
const cancelRetryDelay = 3000;
|
|
for (let attempt = 1; attempt <= cancelMaxRetries; attempt++) {
|
|
try {
|
|
await github.rest.actions.cancelWorkflowRun({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
run_id: latestRun.id,
|
|
});
|
|
console.log(`Cancel request accepted on attempt ${attempt}`);
|
|
break;
|
|
} catch (err) {
|
|
if (err.status === 409 && attempt < cancelMaxRetries) {
|
|
console.log(`Cancel attempt ${attempt} got 409 (not yet cancellable), retrying in ${cancelRetryDelay / 1000}s...`);
|
|
await new Promise(resolve => setTimeout(resolve, cancelRetryDelay));
|
|
} else {
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wait for the workflow to be cancelled (poll with timeout)
|
|
const maxWaitTime = 60000; // 60 seconds
|
|
const pollInterval = 2000; // 2 seconds
|
|
const startTime = Date.now();
|
|
|
|
while (Date.now() - startTime < maxWaitTime) {
|
|
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
|
|
const { data: updatedRun } = await github.rest.actions.getWorkflowRun({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
run_id: latestRun.id,
|
|
});
|
|
|
|
if (updatedRun.status === 'completed') {
|
|
console.log(`Workflow cancelled successfully (conclusion: ${updatedRun.conclusion})`);
|
|
break;
|
|
}
|
|
|
|
console.log(`Waiting for workflow to cancel... (status: ${updatedRun.status})`);
|
|
}
|
|
|
|
// Check final status
|
|
const { data: finalRun } = await github.rest.actions.getWorkflowRun({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
run_id: latestRun.id,
|
|
});
|
|
|
|
if (finalRun.status !== 'completed') {
|
|
core.setFailed(`Timed out waiting for workflow to cancel (status: ${finalRun.status})`);
|
|
return;
|
|
}
|
|
}
|
|
|
|
console.log(`Re-running workflow ${latestRun.id} (was: ${latestRun.conclusion || latestRun.status})`);
|
|
|
|
// Re-run preserves original context (PR, SHA, etc.)
|
|
await github.rest.actions.reRunWorkflow({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
run_id: latestRun.id,
|
|
});
|
|
|
|
console.log(`Triggered: ${latestRun.html_url}`);
|