#!/usr/bin/env bun /** * Does this push touch the npm-consumer-smoke consumer path? * * This list used to live in `npm-consumer-smoke.yml`'s `on.push.paths`, where * it decided whether the WORKFLOW TRIGGERED at all. That made a required status * check that often posted no status: a push touching none of these paths * created no run, so the `npm-consumer-smoke*` context branch protection * requires never reported, and the PR was blocked on a check that was never * going to run (celilo#603). * * The workflow now always triggers and always reports; this decides whether it * does the expensive Docker work. Same paths, same intent — the filter moved * from "should CI speak" to "should CI do the work". * * DEFAULT TO RUNNING. Every uncertainty here resolves to `true`. Skipping when * we should have run is a silent false green on a release-path gate; running * when we needn't costs a few minutes of builder time. */ /** Kept verbatim from the trigger's former `paths:` list. */ export const CONSUMER_PATHS = [ 'packages/e2e/**', 'packages/registry-server/**', // The smoke test IS this gate's body — the only place publishModule and // friends run in the consumer install shape (celilo#1142). 'e2e/tests/smoke.test.ts', // The publish driver (moved from the dead apps/celilo/scripts/publish.ts). 'apps/celilo/src/cli/commands/publish/**', 'scripts/publish.ts', // (The derived release graph lives at packages/e2e/scripts/workspace-graph.ts, // already covered by packages/e2e/** above.) // Every workspace package.json — the actual input to the package graph. // Narrowed to workspace members (not **/package.json) so a devDependency bump // in an unrelated dir doesn't fire this heavy Docker job; the cheap sync gate // in ci.yml catches graph drift on every PR regardless. 'apps/*/package.json', 'packages/*/package.json', 'package.json', ]; interface PushCommit { added?: string[]; modified?: string[]; removed?: string[]; } export interface PushPayload { commits?: PushCommit[]; total_commits?: number; } export function touchesConsumerPath(files: string[]): boolean { return files.some((file) => CONSUMER_PATHS.some((glob) => new Bun.Glob(glob).match(file))); } export function pushIsRelevant(payload: PushPayload): boolean { const commits = payload.commits ?? []; // No commit list to reason from — a payload shape we don't understand is not // evidence that nothing changed. if (commits.length === 0) return true; // Forgejo caps `commits[]` on a large push while `total_commits` reports the // real number. The unlisted commits are exactly the ones we cannot inspect. if ((payload.total_commits ?? commits.length) > commits.length) return true; // A commit carrying none of the three arrays told us nothing about its // files. That is not the same as a commit that changed nothing — no such // commit exists — so it is an unknown, and unknowns run. if (commits.some((c) => !c.added && !c.modified && !c.removed)) return true; return touchesConsumerPath( commits.flatMap((c) => [...(c.added ?? []), ...(c.modified ?? []), ...(c.removed ?? [])]), ); } if (import.meta.main) { const eventPath = process.env.GITHUB_EVENT_PATH; // A manual dispatch (or any non-push event) means someone asked for it. const relevant = process.env.GITHUB_EVENT_NAME !== 'push' || !eventPath ? true : pushIsRelevant(await Bun.file(eventPath).json()); console.log(relevant ? 'true' : 'false'); }