Ctrl K
ComunidadGuías…

scripts/ci-tiers.mjs

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { readFileSync, readdirSync } from "node:fs";
import { join, resolve } from "node:path";
import { pathToFileURL } from "node:url";
import { COMMIT, tierCost } from "../e2e/scripts/suite.mjs";

export const TIERS = [
  "commit",
  "main",
  "nightly",
  "weekly",
  "called",
  "scoped",
];

function triggers(workflow) {
  return workflow.on ?? workflow[true] ?? {};
}

function ignored(trigger) {
  if (trigger === null || trigger === undefined) {
    return [];
  }
  return trigger["paths-ignore"] ?? [];
}

function has(workflow, name) {
  return Object.hasOwn(triggers(workflow), name);
}

export function checkTiers(manifest, workflows, suiteCost) {
  const failures = [];
  const declared = Object.keys(manifest.workflows);
  const present = Object.keys(workflows);
  for (const name of present) {
    if (!declared.includes(name)) {
      failures.push(
        `${name} is not in .github/ci-tiers.json; declare its tier`,
      );
    }
  }
  for (const name of declared) {
    if (!present.includes(name)) {
      failures.push(
        `.github/ci-tiers.json declares ${name}, which does not exist`,
      );
    }
  }

  const wanted = [...manifest.releaseOnlyPaths, manifest.reportPath];
  let budget = 0;
  for (const [name, entry] of Object.entries(manifest.workflows)) {
    const workflow = workflows[name];
    if (workflow === undefined) {
      continue;
    }
    if (!TIERS.includes(entry.tier)) {
      failures.push(`${name} declares unknown tier ${String(entry.tier)}`);
      continue;
    }
    if (entry.tier === "commit" || entry.tier === "main") {
      budget += entry.jobMinutes;
    }
    const on = triggers(workflow);
    if (entry.tier === "commit") {
      if (!has(workflow, "push") || !has(workflow, "pull_request")) {
        failures.push(
          `${name} is on the commit tier but does not run on both push and pull_request`,
        );
      }
      for (const scope of ["push", "pull_request"]) {
        const list = ignored(on[scope]);
        const missing = wanted.filter((path) => !list.includes(path));
        if (scope === "push" && !list.includes(manifest.reportPath)) {
          failures.push(
            `${name} reruns on ${scope} for a ${manifest.reportPath} change`,
          );
          continue;
        }
        if (scope === "pull_request" && missing.length > 0) {
          failures.push(`${name} reruns on ${scope} for ${missing.join(", ")}`);
        }
      }
    }
    if (entry.tier === "main") {
      if (!has(workflow, "push")) {
        failures.push(`${name} is on the main tier but does not run on push`);
      }
      if (has(workflow, "pull_request")) {
        failures.push(
          `${name} is on the main tier but also runs on every pull request`,
        );
      }
    }
    if (entry.tier === "called" || entry.tier === "weekly") {
      for (const scope of ["push", "pull_request"]) {
        if (has(workflow, scope)) {
          failures.push(
            `${name} is on the ${entry.tier} tier but still runs on ${scope}`,
          );
        }
      }
    }
    if (entry.tier === "called" && !has(workflow, "workflow_call")) {
      failures.push(`${name} is on the called tier but is not reusable`);
    }
    if (entry.tier === "scoped") {
      if (!has(workflow, "pull_request")) {
        failures.push(
          `${name} is on the scoped tier but does not run on pull_request`,
        );
      } else if ((on.pull_request?.paths ?? []).length === 0) {
        failures.push(`${name} is on the scoped tier but names no paths`);
      }
    }
    if (entry.tier === "nightly") {
      const jobs = workflow.jobs ?? {};
      if (jobs.guard === undefined) {
        failures.push(`${name} is on the nightly tier and needs a guard job`);
      }
      for (const [job, body] of Object.entries(jobs)) {
        if (job === "guard") {
          continue;
        }
        const needs = [body.needs ?? []].flat();
        if (!needs.includes("guard")) {
          failures.push(
            `${name} job ${job} does not wait for the guard and would run on every push`,
          );
        }
      }
      if (has(workflow, "pull_request")) {
        failures.push(
          `${name} is on the nightly tier but runs on every pull request`,
        );
      }
    }
  }

  budget += suiteCost;
  if (budget > manifest.budgetMinutes) {
    failures.push(
      `the per-commit tiers cost ${String(budget)} job-minutes, budget is ` +
        `${String(manifest.budgetMinutes)}; move a workflow off the commit tier or raise the budget`,
    );
  }
  return { failures, budget };
}

function main() {
  const dir = process.argv[2];
  if (dir === undefined) {
    throw new Error("give the directory of workflows converted to JSON");
  }
  const root = resolve(import.meta.dirname, "..");
  const manifest = JSON.parse(
    readFileSync(join(root, ".github/ci-tiers.json"), "utf8"),
  );
  const workflows = {};
  for (const entry of readdirSync(dir)) {
    if (!entry.endsWith(".json")) {
      continue;
    }
    workflows[entry.replace(/\.json$/, ".yaml")] = JSON.parse(
      readFileSync(join(dir, entry), "utf8"),
    );
  }
  const suite = JSON.parse(readFileSync(join(root, "e2e/suite.json"), "utf8"));
  const { failures, budget } = checkTiers(
    manifest,
    workflows,
    tierCost(suite, COMMIT),
  );
  for (const failure of failures) {
    process.stderr.write(`${failure}\n`);
  }
  if (failures.length > 0) {
    process.exitCode = 1;
    return;
  }
  process.stdout.write(
    `checked ${String(Object.keys(manifest.workflows).length)} workflows; ` +
      `the per-commit tiers cost ${String(budget)} job-minutes of ` +
      `${String(manifest.budgetMinutes)}, ${String(manifest.budgetMinutes - budget)} to spare\n`,
  );
}

if (
  process.argv[1] !== undefined &&
  import.meta.url === pathToFileURL(process.argv[1]).href
) {
  main();
}