Skip to content
78 changes: 64 additions & 14 deletions .github/workflows/run-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,32 @@ jobs:
with:
script: |
const adder = context.payload.sender.login;
const senderType = context.payload.sender.type;
const pr = context.payload.pull_request;
const installation = context.payload.installation;

// Verify label adder has write access
const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({
owner: context.repo.owner,
repo: context.repo.repo,
username: adder,
});
const trustedBotLogins = ['graphite-app[bot]'];
let isAuthorized = false;

if (!['admin', 'maintain', 'write'].includes(perm.permission)) {
core.setFailed(`${adder} does not have write access`);
return;
if (senderType === 'Bot' && installation && trustedBotLogins.includes(adder)) {
console.log(`Label added by trusted GitHub App: ${adder}`);
isAuthorized = true;
}

console.log(`Label added by ${adder} (${perm.permission})`);
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} (${perm.permission})`);
}

// Find the latest pr.yml run for this PR's head SHA
const { data: runs } = await github.rest.actions.listWorkflowRuns({
Expand All @@ -56,13 +67,52 @@ jobs:

const latestRun = matchingRuns[0];

// Check if workflow is still running - can't re-run in-progress workflows
// If workflow is still running, cancel it first then re-run
if (latestRun.status === 'in_progress' || latestRun.status === 'queued') {
core.setFailed(`Workflow is still running (status: ${latestRun.status}). Wait for it to complete or cancel it first.`);
return;
console.log(`Workflow is running (status: ${latestRun.status}). Cancelling it first...`);

await github.rest.actions.cancelWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: latestRun.id,
});

// 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})`);
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({
Expand Down
Loading