fix: allow gitops-only ci plan without node modules
This commit is contained in:
+144
-1
@@ -1,16 +1,159 @@
|
||||
#!/usr/bin/env node
|
||||
import { createCiPlan } from "./src/ci-plan-lib.mjs";
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
|
||||
const noDepsGitOpsOnlyPaths = Object.freeze([
|
||||
"deploy/gitops/",
|
||||
"deploy/frp/",
|
||||
"scripts/ci-plan.mjs",
|
||||
"scripts/gitops-render.mjs",
|
||||
"scripts/src/runtime-lane.ts",
|
||||
"tsconfig.gitops.json"
|
||||
]);
|
||||
|
||||
const noDepsDocsOnlyPaths = Object.freeze([
|
||||
"docs/",
|
||||
"README.md",
|
||||
"AGENTS.md"
|
||||
]);
|
||||
|
||||
if (args.help) {
|
||||
process.stdout.write(`${usage()}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const noDepsPlan = tryCreateNoDepsPlan(args);
|
||||
if (noDepsPlan) {
|
||||
process.stdout.write(args.pretty ? `${JSON.stringify(noDepsPlan, null, 2)}\n` : `${JSON.stringify(noDepsPlan)}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const { createCiPlan } = await import("./src/ci-plan-lib.mjs");
|
||||
const plan = await createCiPlan(args);
|
||||
process.stdout.write(args.pretty ? `${JSON.stringify(plan, null, 2)}\n` : `${JSON.stringify(plan)}\n`);
|
||||
|
||||
function tryCreateNoDepsPlan(options) {
|
||||
const services = Array.isArray(options.services) ? options.services.filter(Boolean) : [];
|
||||
if (services.length === 0) return null;
|
||||
const targetRef = options.targetRef ?? "HEAD";
|
||||
const baseRef = options.baseRef ?? (gitValue(["rev-parse", `${targetRef}^`]) || targetRef);
|
||||
const changedPaths = gitValue(["diff", "--name-only", baseRef, targetRef]).split("\n").map((line) => line.trim()).filter(Boolean);
|
||||
const summary = classifyNoDepsGlobalChange(changedPaths);
|
||||
if (!summary.gitopsOnly && !summary.docsOnly && !summary.testOnly && summary.summary !== "no-source-diff") return null;
|
||||
const sourceCommitId = gitValue(["rev-parse", targetRef]);
|
||||
const shortCommitId = gitValue(["rev-parse", "--short=7", targetRef]);
|
||||
return {
|
||||
planVersion: "v1",
|
||||
sourceCommitId,
|
||||
shortCommitId,
|
||||
baseRef,
|
||||
targetRef,
|
||||
compatibility: {
|
||||
deployConfig: options.deployConfigPath ?? "deploy/deploy.yaml",
|
||||
artifactCatalog: options.artifactCatalogPath ?? null,
|
||||
lane: options.lane ?? "v02",
|
||||
ciContractSource: "scripts/ci-plan.mjs",
|
||||
mode: "no-deps-global-change-fast-path",
|
||||
currentRenderCompatible: true,
|
||||
plannerMutatesDeployJson: false,
|
||||
serviceIdSource: "cli-services",
|
||||
defaultComponentLazyBuild: true,
|
||||
envReuseCodeOnlyFastLane: true,
|
||||
fullBuildFallback: false
|
||||
},
|
||||
inputs: {
|
||||
registryPrefix: options.registryPrefix ?? process.env.HWLAB_DEV_REGISTRY_PREFIX ?? process.env.HWLAB_DEV_REGISTRY ?? "127.0.0.1:5000/hwlab",
|
||||
serviceCount: services.length,
|
||||
verifyReuseRegistry: options.verifyReuseRegistry ?? booleanEnv(process.env.HWLAB_CI_PLAN_VERIFY_REUSE_REGISTRY)
|
||||
},
|
||||
changedPaths,
|
||||
changedPathSummary: summary,
|
||||
imageBuildRequired: false,
|
||||
affectedServices: [],
|
||||
rolloutServices: [],
|
||||
buildServices: [],
|
||||
reusedServices: services,
|
||||
buildSkippedCount: services.length,
|
||||
services: services.map((serviceId) => ({
|
||||
serviceId,
|
||||
affected: false,
|
||||
buildRequired: false,
|
||||
skipImageBuild: true,
|
||||
runtimeMode: "env-reuse-git-mirror-checkout",
|
||||
envReuse: true,
|
||||
envChanged: false,
|
||||
codeChanged: false,
|
||||
reason: [summary.summary === "no-source-diff" ? "no-source-diff" : `${summary.summary}-no-image-build`]
|
||||
})),
|
||||
ciCdPlan: {
|
||||
willBuild: [],
|
||||
willRollout: [],
|
||||
willReuse: services,
|
||||
willRunGitopsPromote: summary.gitopsOnly,
|
||||
noImageBuildReason: summary.summary
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function classifyNoDepsGlobalChange(changedPaths) {
|
||||
const relevant = changedPaths.filter(Boolean);
|
||||
const testOnly = relevant.length > 0 && relevant.every(isNoDepsTestOnlyPath);
|
||||
const docsOnly = relevant.length > 0 && relevant.every((item) => isNoDepsDocsOnlyPath(item) || isNoDepsTestOnlyPath(item));
|
||||
const gitopsOnly = relevant.length > 0 && relevant.every((item) => isNoDepsGitOpsOnlyPath(item) || isNoDepsDocsOnlyPath(item) || isNoDepsTestOnlyPath(item));
|
||||
return {
|
||||
count: relevant.length,
|
||||
testOnly,
|
||||
docsOnly,
|
||||
gitopsOnly,
|
||||
summary: relevant.length === 0
|
||||
? "no-source-diff"
|
||||
: testOnly
|
||||
? "test-only-change"
|
||||
: docsOnly
|
||||
? "docs-only-change"
|
||||
: gitopsOnly
|
||||
? "gitops-only-change"
|
||||
: "runtime-or-build-change"
|
||||
};
|
||||
}
|
||||
|
||||
function isNoDepsGitOpsOnlyPath(filePath) {
|
||||
return noDepsGitOpsOnlyPaths.some((pattern) => pathMatches(pattern, filePath));
|
||||
}
|
||||
|
||||
function isNoDepsDocsOnlyPath(filePath) {
|
||||
const normalized = normalizeRepoPath(filePath);
|
||||
if (normalized.startsWith("internal/agent/prompts/")) return false;
|
||||
if (normalized.startsWith("skills/")) return false;
|
||||
return noDepsDocsOnlyPaths.some((pattern) => pathMatches(pattern, normalized)) || normalized.endsWith(".md");
|
||||
}
|
||||
|
||||
function isNoDepsTestOnlyPath(filePath) {
|
||||
const normalized = normalizeRepoPath(filePath);
|
||||
return /\.test\.(mjs|ts)$/u.test(normalized) || normalized.includes("/__tests__/") || normalized.includes("/fixtures/");
|
||||
}
|
||||
|
||||
function pathMatches(pattern, filePath) {
|
||||
const normalizedPattern = normalizeRepoPath(pattern);
|
||||
const normalizedFilePath = normalizeRepoPath(filePath);
|
||||
if (!normalizedPattern || !normalizedFilePath) return false;
|
||||
if (normalizedPattern.endsWith("/")) return normalizedFilePath.startsWith(normalizedPattern);
|
||||
return normalizedFilePath === normalizedPattern;
|
||||
}
|
||||
|
||||
function normalizeRepoPath(value) {
|
||||
return String(value ?? "").replaceAll("\\", "/").replace(/^\.\//u, "").replace(/^\/+/, "").trim();
|
||||
}
|
||||
|
||||
function gitValue(args) {
|
||||
return execFileSync("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 15000 }).trim();
|
||||
}
|
||||
|
||||
function booleanEnv(value) {
|
||||
return ["1", "true", "yes", "on"].includes(String(value ?? "").trim().toLowerCase());
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const parsed = { pretty: false };
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
|
||||
Reference in New Issue
Block a user