Skip to content

deps: refresh workspace dependencies and CI tooling #99

deps: refresh workspace dependencies and CI tooling

deps: refresh workspace dependencies and CI tooling #99

name: org-required-checks
on:
pull_request:
types:
- opened
- reopened
- synchronize
- edited
- ready_for_review
deployment_status:
permissions:
checks: read
contents: read
pull-requests: read
statuses: read
jobs:
verify:
name: Check
if: |
github.event_name == 'pull_request' ||
(github.event_name == 'deployment_status' && github.event.deployment_status.state == 'success')
runs-on: ubuntu-latest
steps:
- name: Wait for merge gates
uses: actions/github-script@v8
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
let pr = context.payload.pull_request;
let pull_number;
let headSha;
if (pr) {
pull_number = pr.number;
headSha = pr.head.sha;
} else {
const deploymentStatus = context.payload.deployment_status;
const deploymentSha = deploymentStatus?.deployment?.sha;
if (!deploymentSha) {
core.info("No pull request or deployment SHA context. Passing check.");
return;
}
headSha = deploymentSha;
const associatedPrs = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner,
repo,
commit_sha: headSha,
});
pr =
associatedPrs.data.find((candidate) => candidate.state === "open") ?? null;
if (!pr) {
core.info(`No open pull request associated with SHA ${headSha}. Passing check.`);
return;
}
pull_number = pr.number;
}
const files = await github.paginate(github.rest.pulls.listFiles, {
owner,
repo,
pull_number,
per_page: 100,
});
const ciIgnoredPatterns = [
/^docs\//,
/^README\.md$/,
/^AGENTS\.md$/,
/^\.codex\//,
/^\.opencode\//,
];
const hasCiRelevantChange = files.some((file) => {
return !ciIgnoredPatterns.some((pattern) => pattern.test(file.filename));
});
const isReleaseVersionBumpPr =
/^chore\/bump-version-/.test(pr.head.ref) &&
(pr.user?.login === "github-actions[bot]" || pr.user?.login === "app/github-actions");
const requiredContexts = ["Analyze (JavaScript/TypeScript)", "Vercel"];
if (hasCiRelevantChange && !isReleaseVersionBumpPr) {
requiredContexts.push("checks", "Preview validation (seed + runtime QA)");
}
core.info(
`Required contexts: ${requiredContexts.join(", ")} (ciRelevant=${hasCiRelevantChange}, releaseBump=${isReleaseVersionBumpPr})`,
);
const deadline = Date.now() + 20 * 60 * 1000;
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const normalizeStatusContext = (state) => {
if (state === "success") {
return "success";
}
if (state === "failure" || state === "error") {
return "failure";
}
return "pending";
};
const normalizeCheckRun = (checkRun) => {
if (checkRun.status !== "completed") {
return "pending";
}
if (checkRun.conclusion === "success") {
return "success";
}
if (checkRun.conclusion === "neutral") {
return "success";
}
return "failure";
};
while (Date.now() < deadline) {
const [checkRunsResp, statusesResp] = await Promise.all([
github.rest.checks.listForRef({
owner,
repo,
ref: headSha,
per_page: 100,
}),
github.rest.repos.listCommitStatusesForRef({
owner,
repo,
ref: headSha,
per_page: 100,
}),
]);
const checkRunsByName = new Map();
for (const checkRun of checkRunsResp.data.check_runs) {
if (!checkRunsByName.has(checkRun.name)) {
checkRunsByName.set(checkRun.name, checkRun);
}
}
const statusesByContext = new Map();
for (const status of statusesResp.data) {
if (!statusesByContext.has(status.context)) {
statusesByContext.set(status.context, status);
}
}
let hasPending = false;
for (const contextName of requiredContexts) {
const checkRun = checkRunsByName.get(contextName);
if (checkRun) {
const normalized = normalizeCheckRun(checkRun);
if (normalized === "failure") {
core.setFailed(
`${contextName} failed with conclusion '${checkRun.conclusion ?? "unknown"}'.`,
);
return;
}
if (normalized === "pending") {
hasPending = true;
}
continue;
}
const status = statusesByContext.get(contextName);
if (status) {
const normalized = normalizeStatusContext(status.state);
if (normalized === "failure") {
core.setFailed(`${contextName} failed with state '${status.state}'.`);
return;
}
if (normalized === "pending") {
hasPending = true;
}
continue;
}
hasPending = true;
}
if (!hasPending) {
core.info("All required contexts passed.");
return;
}
await sleep(15000);
}
core.setFailed(
`Timed out waiting for required contexts: ${requiredContexts.join(", ")}`,
);
ValidatePrTitle:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
steps:
- name: Validate PR title
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
if [ -z "${PR_TITLE:-}" ]; then
echo "PR title is empty."
exit 1
fi
if [ ${#PR_TITLE} -gt 120 ]; then
echo "PR title must be 120 characters or fewer."
exit 1
fi
echo "PR title validation passed."