Files
Cleanuparr/.github/workflows/stack-gate.yml

80 lines
2.8 KiB
YAML

name: Stack Gate
on:
workflow_call:
inputs:
paths:
description: 'Newline-separated path prefixes matching the caller''s on.pull_request.paths.'
type: string
required: true
outputs:
run:
description: 'false when the PR is below the top of a stack whose top PR also triggers this workflow.'
value: ${{ jobs.gate.outputs.run }}
jobs:
gate:
runs-on: ubuntu-latest
timeout-minutes: 2
outputs:
run: ${{ steps.stack.outputs.run }}
steps:
- name: Check stack position
id: stack
uses: actions/github-script@v9
env:
PATHS: ${{ inputs.paths }}
with:
script: |
if (context.eventName !== 'pull_request') {
core.setOutput('run', 'true');
return;
}
try {
const result = await github.graphql(`
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
stack {
size
entries(first: 50) {
nodes {
position
pullRequest { files(first: 100) { nodes { path } } }
}
}
}
stackEntry { position }
}
}
}`, {
owner: context.repo.owner,
repo: context.repo.repo,
number: context.payload.pull_request.number,
});
const pr = result.repository.pullRequest;
if (!pr.stack || !pr.stackEntry || pr.stackEntry.position === pr.stack.size) {
core.setOutput('run', 'true');
return;
}
const prefixes = process.env.PATHS.split('\n').map(p => p.trim()).filter(Boolean);
const files = pr.stack.entries.nodes.find(e => e.position === pr.stack.size)?.pullRequest?.files;
// Missing data or no match in the first 100 files fails open
const topRuns = files?.nodes.some(f => prefixes.some(p => f.path.startsWith(p))) ?? false;
if (!topRuns) {
core.info('Top PR does not trigger this workflow, running here.');
core.setOutput('run', 'true');
return;
}
core.info(`PR is at position ${pr.stackEntry.position} of ${pr.stack.size}, CI runs on the top PR only.`);
core.setOutput('run', 'false');
} catch (error) {
// Fail open so a lookup error never silently drops CI
core.warning(error.message);
core.setOutput('run', 'true');
}