// Which phase of a job a CI step belongs to, read from the marker emoji its // name starts with. scripts/ci-gantt.ts splits each job bar by phase with this, // and tasks/ci-workflow.test.ts uses it to find the work steps a bound is // required on. The vocabulary lives in docs/development/CI_PERFORMANCE.md // ("Step phase markers") and is mirrored in PHASE_MARKERS below. export type Phase = "setup" | "work" | "shutdown" | "other"; // Marker emoji -> phase. Each emoji maps to exactly one phase; when a step's // natural emoji would land it in the wrong phase, the step name is changed to a // marker that fits (see docs/development/CI_PERFORMANCE.md). Matching ignores a // trailing variation selector, so the base emoji covers both the plain and the // selector-suffixed form of a glyph. export const PHASE_MARKERS: [string, Phase][] = [ // setup: fetch code, install tools and dependencies, restore caches, // authenticate, and bring test servers and devices up before the real work. ["๐Ÿ“ฅ", "setup"], // checkout / download inputs ["๐Ÿฆ•", "setup"], // set up Deno ["๐Ÿ”", "setup"], // verify lock file & install, resolve refs ["๐Ÿ“ฆ", "setup"], // install packages, cache dependencies ["โ™ป๏ธ", "setup"], // restore/save build caches ["๐Ÿ›ก๏ธ", "setup"], // relax sandbox for browser tests ["๐Ÿ”ง", "setup"], // enable devices ["โš™๏ธ", "setup"], // set up external SDKs ["๐Ÿ”‘", "setup"], // authenticate to a cloud ["๐Ÿ”Œ", "setup"], // start a local server for tests ["โณ", "setup"], // wait for a service to be ready ["๐Ÿ’พ", "setup"], // restore/save caches ["๐Ÿ—ƒ๏ธ", "setup"], // restore a cached native library ["๐Ÿงฎ", "setup"], // compute a cache identity // work: the job's actual purpose. ["๐Ÿ”Ž", "work"], // checks (format, type, patterns, attestations) ["๐Ÿšง", "work"], // guard that fails the build on a banned pattern ["๐Ÿฉน", "work"], // check for unresolved merge-conflict markers ["โœ…", "work"], // validate an artifact a previous step produced ["๐Ÿ—บ๏ธ", "work"], // plan a run's lanes, or what one lane runs ["๐Ÿงช", "work"], // run tests ["๐Ÿงฉ", "work"], // run integration tests ["๐Ÿ”", "work"], // replay captured fixtures under today's source ["๐Ÿงน", "work"], // lint ["๐Ÿงญ", "work"], // check skill facts ["๐Ÿ“„", "work"], // type-check docs ["๐Ÿ—๏ธ", "work"], // build binaries/assets ["๐Ÿ‹๏ธ", "work"], // run benchmarks ["๐Ÿ“Š", "work"], // produce performance metrics / status reports ["๐Ÿงฌ", "work"], // combine coverage ["๐Ÿ“", "work"], // generate attestations ["๐Ÿ”", "work"], // sign binaries ["๐Ÿš€", "work"], // deploy ["๐Ÿ’ฌ", "work"], // post a PR comment // shutdown: post-work reports, artifact uploads, log capture, teardown. ["๐Ÿงพ", "shutdown"], // write coverage report ["๐Ÿ“ค", "shutdown"], // upload artifacts ["๐Ÿ“‹", "shutdown"], // capture logs on failure ]; const stripVS = (s: string) => s.replace(/\uFE0F/g, ""); export function phaseOf(stepName: string): Phase { const name = stepName.trim(); // A leading marker wins, so a step named "๐Ÿ’ฌ Post โ€ฆ" is classified by its // marker rather than the "Post " rule below. const norm = stripVS(name); for (const [emoji, phase] of PHASE_MARKERS) { if (norm.startsWith(stripVS(emoji))) return phase; } // Injected steps carry no marker; their wording is not ours to set. Current // jobs use the "job" pair and the "Post โ€ฆ" steps. Retained records can also // contain the "runner" pair. if (name.startsWith("Post ")) return "shutdown"; if (name === "Set up job" || name === "Set up runner") return "setup"; if (name === "Complete job" || name === "Complete runner") return "shutdown"; return "other"; }