@komatikai/trailhead 4.1.0 → 4.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -22
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/run-doctor.d.ts +1 -0
- package/dist/run-doctor.js +83 -0
- package/dist/run-doctor.js.map +1 -0
- package/dist/shared/ci-core.d.ts +18 -0
- package/dist/shared/ci-core.js +210 -0
- package/dist/shared/ci-core.js.map +1 -0
- package/dist/shared/ci-manifest.d.ts +82 -0
- package/dist/shared/ci-manifest.js +51 -0
- package/dist/shared/ci-manifest.js.map +1 -0
- package/dist/shared/config-core.d.ts +5 -0
- package/dist/shared/config-core.js +127 -0
- package/dist/shared/config-core.js.map +1 -0
- package/dist/shared/doctor.d.ts +48 -0
- package/dist/shared/doctor.js +308 -0
- package/dist/shared/doctor.js.map +1 -0
- package/dist/shared/release-ready.d.ts +27 -0
- package/dist/shared/release-ready.js +97 -0
- package/dist/shared/release-ready.js.map +1 -0
- package/dist/shared/types.d.ts +1813 -0
- package/dist/shared/types.js +338 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +6 -3
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Job outcome as emitted by path-filtered workflows (E15). */
|
|
3
|
+
export declare const CiManifestJobOutcome: z.ZodEnum<["ran", "passed", "skipped", "failed", "pending", "cancelled"]>;
|
|
4
|
+
export type CiManifestJobOutcome = z.infer<typeof CiManifestJobOutcome>;
|
|
5
|
+
/** Why a job did not run — `paths-filter` is the primary v4.2 use case. */
|
|
6
|
+
export declare const CiManifestSkipReason: z.ZodEnum<["paths-filter", "paths-ignore", "manual", "condition", "concurrency", "workflow_dispatch", "other"]>;
|
|
7
|
+
export type CiManifestSkipReason = z.infer<typeof CiManifestSkipReason>;
|
|
8
|
+
export declare const CiManifestJob: z.ZodObject<{
|
|
9
|
+
name: z.ZodString;
|
|
10
|
+
outcome: z.ZodEnum<["ran", "passed", "skipped", "failed", "pending", "cancelled"]>;
|
|
11
|
+
reason: z.ZodOptional<z.ZodEnum<["paths-filter", "paths-ignore", "manual", "condition", "concurrency", "workflow_dispatch", "other"]>>;
|
|
12
|
+
check_run_id: z.ZodOptional<z.ZodNumber>;
|
|
13
|
+
details_url: z.ZodOptional<z.ZodString>;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
name: string;
|
|
16
|
+
outcome: "ran" | "passed" | "skipped" | "failed" | "pending" | "cancelled";
|
|
17
|
+
reason?: "paths-filter" | "paths-ignore" | "manual" | "condition" | "concurrency" | "workflow_dispatch" | "other" | undefined;
|
|
18
|
+
check_run_id?: number | undefined;
|
|
19
|
+
details_url?: string | undefined;
|
|
20
|
+
}, {
|
|
21
|
+
name: string;
|
|
22
|
+
outcome: "ran" | "passed" | "skipped" | "failed" | "pending" | "cancelled";
|
|
23
|
+
reason?: "paths-filter" | "paths-ignore" | "manual" | "condition" | "concurrency" | "workflow_dispatch" | "other" | undefined;
|
|
24
|
+
check_run_id?: number | undefined;
|
|
25
|
+
details_url?: string | undefined;
|
|
26
|
+
}>;
|
|
27
|
+
export type CiManifestJob = z.infer<typeof CiManifestJob>;
|
|
28
|
+
export declare const CiManifest: z.ZodObject<{
|
|
29
|
+
schema_version: z.ZodLiteral<1>;
|
|
30
|
+
generated_at: z.ZodOptional<z.ZodString>;
|
|
31
|
+
commit_sha: z.ZodOptional<z.ZodString>;
|
|
32
|
+
workflow: z.ZodOptional<z.ZodString>;
|
|
33
|
+
run_id: z.ZodOptional<z.ZodNumber>;
|
|
34
|
+
jobs: z.ZodArray<z.ZodObject<{
|
|
35
|
+
name: z.ZodString;
|
|
36
|
+
outcome: z.ZodEnum<["ran", "passed", "skipped", "failed", "pending", "cancelled"]>;
|
|
37
|
+
reason: z.ZodOptional<z.ZodEnum<["paths-filter", "paths-ignore", "manual", "condition", "concurrency", "workflow_dispatch", "other"]>>;
|
|
38
|
+
check_run_id: z.ZodOptional<z.ZodNumber>;
|
|
39
|
+
details_url: z.ZodOptional<z.ZodString>;
|
|
40
|
+
}, "strip", z.ZodTypeAny, {
|
|
41
|
+
name: string;
|
|
42
|
+
outcome: "ran" | "passed" | "skipped" | "failed" | "pending" | "cancelled";
|
|
43
|
+
reason?: "paths-filter" | "paths-ignore" | "manual" | "condition" | "concurrency" | "workflow_dispatch" | "other" | undefined;
|
|
44
|
+
check_run_id?: number | undefined;
|
|
45
|
+
details_url?: string | undefined;
|
|
46
|
+
}, {
|
|
47
|
+
name: string;
|
|
48
|
+
outcome: "ran" | "passed" | "skipped" | "failed" | "pending" | "cancelled";
|
|
49
|
+
reason?: "paths-filter" | "paths-ignore" | "manual" | "condition" | "concurrency" | "workflow_dispatch" | "other" | undefined;
|
|
50
|
+
check_run_id?: number | undefined;
|
|
51
|
+
details_url?: string | undefined;
|
|
52
|
+
}>, "many">;
|
|
53
|
+
}, "strip", z.ZodTypeAny, {
|
|
54
|
+
schema_version: 1;
|
|
55
|
+
jobs: {
|
|
56
|
+
name: string;
|
|
57
|
+
outcome: "ran" | "passed" | "skipped" | "failed" | "pending" | "cancelled";
|
|
58
|
+
reason?: "paths-filter" | "paths-ignore" | "manual" | "condition" | "concurrency" | "workflow_dispatch" | "other" | undefined;
|
|
59
|
+
check_run_id?: number | undefined;
|
|
60
|
+
details_url?: string | undefined;
|
|
61
|
+
}[];
|
|
62
|
+
generated_at?: string | undefined;
|
|
63
|
+
commit_sha?: string | undefined;
|
|
64
|
+
workflow?: string | undefined;
|
|
65
|
+
run_id?: number | undefined;
|
|
66
|
+
}, {
|
|
67
|
+
schema_version: 1;
|
|
68
|
+
jobs: {
|
|
69
|
+
name: string;
|
|
70
|
+
outcome: "ran" | "passed" | "skipped" | "failed" | "pending" | "cancelled";
|
|
71
|
+
reason?: "paths-filter" | "paths-ignore" | "manual" | "condition" | "concurrency" | "workflow_dispatch" | "other" | undefined;
|
|
72
|
+
check_run_id?: number | undefined;
|
|
73
|
+
details_url?: string | undefined;
|
|
74
|
+
}[];
|
|
75
|
+
generated_at?: string | undefined;
|
|
76
|
+
commit_sha?: string | undefined;
|
|
77
|
+
workflow?: string | undefined;
|
|
78
|
+
run_id?: number | undefined;
|
|
79
|
+
}>;
|
|
80
|
+
export type CiManifest = z.infer<typeof CiManifest>;
|
|
81
|
+
export declare function parseCiManifest(raw: unknown): CiManifest;
|
|
82
|
+
export declare function readCiManifestFile(filePath: string): CiManifest | null;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
/** Job outcome as emitted by path-filtered workflows (E15). */
|
|
5
|
+
export const CiManifestJobOutcome = z.enum([
|
|
6
|
+
"ran",
|
|
7
|
+
"passed",
|
|
8
|
+
"skipped",
|
|
9
|
+
"failed",
|
|
10
|
+
"pending",
|
|
11
|
+
"cancelled",
|
|
12
|
+
]);
|
|
13
|
+
/** Why a job did not run — `paths-filter` is the primary v4.2 use case. */
|
|
14
|
+
export const CiManifestSkipReason = z.enum([
|
|
15
|
+
"paths-filter",
|
|
16
|
+
"paths-ignore",
|
|
17
|
+
"manual",
|
|
18
|
+
"condition",
|
|
19
|
+
"concurrency",
|
|
20
|
+
"workflow_dispatch",
|
|
21
|
+
"other",
|
|
22
|
+
]);
|
|
23
|
+
export const CiManifestJob = z.object({
|
|
24
|
+
name: z.string().min(1),
|
|
25
|
+
outcome: CiManifestJobOutcome,
|
|
26
|
+
reason: CiManifestSkipReason.optional(),
|
|
27
|
+
check_run_id: z.number().int().positive().optional(),
|
|
28
|
+
details_url: z.string().url().optional(),
|
|
29
|
+
});
|
|
30
|
+
export const CiManifest = z.object({
|
|
31
|
+
schema_version: z.literal(1),
|
|
32
|
+
generated_at: z.string().optional(),
|
|
33
|
+
commit_sha: z.string().optional(),
|
|
34
|
+
workflow: z.string().optional(),
|
|
35
|
+
run_id: z.number().int().positive().optional(),
|
|
36
|
+
jobs: z.array(CiManifestJob),
|
|
37
|
+
});
|
|
38
|
+
export function parseCiManifest(raw) {
|
|
39
|
+
return CiManifest.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
export function readCiManifestFile(filePath) {
|
|
42
|
+
try {
|
|
43
|
+
const resolved = path.resolve(filePath);
|
|
44
|
+
const contents = fs.readFileSync(resolved, "utf8");
|
|
45
|
+
return parseCiManifest(JSON.parse(contents));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=ci-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-manifest.js","sourceRoot":"","sources":["../../src/shared/ci-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,+DAA+D;AAC/D,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,IAAI,CAAC;IACzC,KAAK;IACL,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,SAAS;IACT,WAAW;CACZ,CAAC,CAAC;AAGH,2EAA2E;AAC3E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,IAAI,CAAC;IACzC,cAAc;IACd,cAAc;IACd,QAAQ;IACR,WAAW;IACX,aAAa;IACb,mBAAmB;IACnB,OAAO;CACR,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,OAAO,EAAE,oBAAoB;IAC7B,MAAM,EAAE,oBAAoB,CAAC,QAAQ,EAAE;IACvC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACpD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;CACzC,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC9C,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC;CAC7B,CAAC,CAAC;AAGH,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACnD,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RepoConfig as RepoConfigType } from "./types.js";
|
|
2
|
+
export declare const SUPPORTED_CONFIG_SCHEMA_VERSIONS: Set<number>;
|
|
3
|
+
export declare const CURRENT_CONFIG_SCHEMA_VERSION = 2;
|
|
4
|
+
export declare function parseYaml(input: string): unknown;
|
|
5
|
+
export declare function parseRepoConfigContent(content: string): RepoConfigType | null;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { RepoConfig } from "./types.js";
|
|
2
|
+
export const SUPPORTED_CONFIG_SCHEMA_VERSIONS = new Set([1, 2]);
|
|
3
|
+
export const CURRENT_CONFIG_SCHEMA_VERSION = 2;
|
|
4
|
+
export function parseYaml(input) {
|
|
5
|
+
const lines = input
|
|
6
|
+
.split("\n")
|
|
7
|
+
.map((line) => line.replace(/\r$/, ""))
|
|
8
|
+
.filter((line) => line.trim() !== "" && !line.trim().startsWith("#"));
|
|
9
|
+
const root = {};
|
|
10
|
+
const stack = [{ indent: -1, value: root }];
|
|
11
|
+
const parseScalar = (value) => {
|
|
12
|
+
const v = value.trim();
|
|
13
|
+
if ((v.startsWith('"') && v.endsWith('"')) ||
|
|
14
|
+
(v.startsWith("'") && v.endsWith("'"))) {
|
|
15
|
+
return v.slice(1, -1);
|
|
16
|
+
}
|
|
17
|
+
if (v === "true")
|
|
18
|
+
return true;
|
|
19
|
+
if (v === "false")
|
|
20
|
+
return false;
|
|
21
|
+
if (v === "null")
|
|
22
|
+
return null;
|
|
23
|
+
const n = Number(v);
|
|
24
|
+
if (!Number.isNaN(n) && v !== "")
|
|
25
|
+
return n;
|
|
26
|
+
return v;
|
|
27
|
+
};
|
|
28
|
+
const findNextSignificantLine = (fromIndex) => {
|
|
29
|
+
for (let i = fromIndex + 1; i < lines.length; i += 1) {
|
|
30
|
+
const candidate = lines[i];
|
|
31
|
+
if (candidate.trim() !== "" && !candidate.trim().startsWith("#")) {
|
|
32
|
+
return candidate;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
};
|
|
37
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
38
|
+
const line = lines[i];
|
|
39
|
+
const indent = line.match(/^ */)?.[0].length ?? 0;
|
|
40
|
+
const trimmed = line.trim();
|
|
41
|
+
while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
|
|
42
|
+
stack.pop();
|
|
43
|
+
}
|
|
44
|
+
const container = stack[stack.length - 1].value;
|
|
45
|
+
if (trimmed.startsWith("- ")) {
|
|
46
|
+
if (!Array.isArray(container))
|
|
47
|
+
continue;
|
|
48
|
+
const itemRaw = trimmed.slice(2).trim();
|
|
49
|
+
if (itemRaw === "") {
|
|
50
|
+
const child = {};
|
|
51
|
+
container.push(child);
|
|
52
|
+
stack.push({ indent, value: child });
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const itemKeyMatch = itemRaw.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
56
|
+
if (itemKeyMatch) {
|
|
57
|
+
const child = {};
|
|
58
|
+
const [, itemKey, itemVal] = itemKeyMatch;
|
|
59
|
+
if (itemVal === "") {
|
|
60
|
+
const nextLine = findNextSignificantLine(i);
|
|
61
|
+
const nextIndent = nextLine?.match(/^ */)?.[0].length ?? -1;
|
|
62
|
+
const nextTrimmed = nextLine?.trim() ?? "";
|
|
63
|
+
const useArray = nextLine !== null && nextIndent > indent && nextTrimmed.startsWith("- ");
|
|
64
|
+
child[itemKey] = useArray ? [] : {};
|
|
65
|
+
if (!useArray &&
|
|
66
|
+
typeof child[itemKey] === "object" &&
|
|
67
|
+
child[itemKey] !== null) {
|
|
68
|
+
stack.push({ indent, value: child[itemKey] });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const trimmedVal = itemVal.trim();
|
|
73
|
+
if (trimmedVal.startsWith("[") && trimmedVal.endsWith("]")) {
|
|
74
|
+
const inner = trimmedVal.slice(1, -1).trim();
|
|
75
|
+
child[itemKey] =
|
|
76
|
+
inner === ""
|
|
77
|
+
? []
|
|
78
|
+
: inner.split(",").map((item) => parseScalar(item.trim()));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
child[itemKey] = parseScalar(itemVal);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
container.push(child);
|
|
85
|
+
stack.push({ indent, value: child });
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
container.push(parseScalar(itemRaw));
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const keyMatch = trimmed.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
92
|
+
if (!keyMatch ||
|
|
93
|
+
typeof container !== "object" ||
|
|
94
|
+
container === null ||
|
|
95
|
+
Array.isArray(container)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const [, key, rawVal] = keyMatch;
|
|
99
|
+
if (rawVal !== "") {
|
|
100
|
+
const trimmedVal = rawVal.trim();
|
|
101
|
+
if (trimmedVal.startsWith("[") && trimmedVal.endsWith("]")) {
|
|
102
|
+
const inner = trimmedVal.slice(1, -1).trim();
|
|
103
|
+
container[key] =
|
|
104
|
+
inner === "" ? [] : inner.split(",").map((item) => parseScalar(item.trim()));
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
container[key] = parseScalar(rawVal);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const nextLine = findNextSignificantLine(i);
|
|
111
|
+
const nextIndent = nextLine?.match(/^ */)?.[0].length ?? -1;
|
|
112
|
+
const nextTrimmed = nextLine?.trim() ?? "";
|
|
113
|
+
const useArray = nextLine !== null && nextIndent > indent && nextTrimmed.startsWith("- ");
|
|
114
|
+
const child = useArray ? [] : {};
|
|
115
|
+
container[key] = child;
|
|
116
|
+
stack.push({ indent, value: child });
|
|
117
|
+
}
|
|
118
|
+
return root;
|
|
119
|
+
}
|
|
120
|
+
export function parseRepoConfigContent(content) {
|
|
121
|
+
const raw = parseYaml(content);
|
|
122
|
+
const parsed = RepoConfig.safeParse(raw);
|
|
123
|
+
if (!parsed.success)
|
|
124
|
+
return null;
|
|
125
|
+
return parsed.data;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=config-core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-core.js","sourceRoot":"","sources":["../../src/shared/config-core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { GateMode, RepoConfig } from "./types.js";
|
|
2
|
+
export type DoctorSeverity = "error" | "warn" | "info";
|
|
3
|
+
export interface DoctorFinding {
|
|
4
|
+
severity: DoctorSeverity;
|
|
5
|
+
code: string;
|
|
6
|
+
message: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DoctorReport {
|
|
9
|
+
configPath: string | null;
|
|
10
|
+
configValid: boolean;
|
|
11
|
+
gateMode: GateMode;
|
|
12
|
+
expectedCheckName: string;
|
|
13
|
+
configuredChecks: string[];
|
|
14
|
+
observedChecks: string[];
|
|
15
|
+
findings: DoctorFinding[];
|
|
16
|
+
ok: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface RunDoctorOptions {
|
|
19
|
+
cwd?: string;
|
|
20
|
+
offline?: boolean;
|
|
21
|
+
githubToken?: string;
|
|
22
|
+
repo?: string;
|
|
23
|
+
ref?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function findConfigPath(cwd: string): string | null;
|
|
26
|
+
export declare function loadRepoConfig(cwd: string): {
|
|
27
|
+
configPath: string | null;
|
|
28
|
+
config: RepoConfig | null;
|
|
29
|
+
error?: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function collectConfiguredChecks(config: RepoConfig): string[];
|
|
32
|
+
export declare function validateConfigStructure(config: RepoConfig, configPath: string): DoctorFinding[];
|
|
33
|
+
export declare function compareConfiguredChecks(configuredChecks: string[], observedChecks: string[]): DoctorFinding[];
|
|
34
|
+
export interface GitHubRepoRef {
|
|
35
|
+
owner: string;
|
|
36
|
+
repo: string;
|
|
37
|
+
}
|
|
38
|
+
export declare function parseRepoRef(input: string): GitHubRepoRef | null;
|
|
39
|
+
export declare function fetchObservedCheckNames(options: {
|
|
40
|
+
token: string;
|
|
41
|
+
repo: GitHubRepoRef;
|
|
42
|
+
ref?: string;
|
|
43
|
+
}): Promise<{
|
|
44
|
+
checks: string[];
|
|
45
|
+
error?: string;
|
|
46
|
+
}>;
|
|
47
|
+
export declare function runDoctor(options?: RunDoctorOptions): Promise<DoctorReport>;
|
|
48
|
+
export declare function formatDoctorReport(report: DoctorReport): string;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { checkNameMatches } from "./ci-core.js";
|
|
4
|
+
import { parseRepoConfigContent } from "./config-core.js";
|
|
5
|
+
import { resolveCheckName } from "./release-ready.js";
|
|
6
|
+
const CONFIG_CANDIDATES = [".trailhead.yml", ".deployguard.yml"];
|
|
7
|
+
export function findConfigPath(cwd) {
|
|
8
|
+
for (const name of CONFIG_CANDIDATES) {
|
|
9
|
+
const candidate = path.join(cwd, name);
|
|
10
|
+
if (fs.existsSync(candidate))
|
|
11
|
+
return candidate;
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
export function loadRepoConfig(cwd) {
|
|
16
|
+
const configPath = findConfigPath(cwd);
|
|
17
|
+
if (!configPath) {
|
|
18
|
+
return {
|
|
19
|
+
configPath: null,
|
|
20
|
+
config: null,
|
|
21
|
+
error: "No .trailhead.yml or .deployguard.yml found",
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const content = fs.readFileSync(configPath, "utf8");
|
|
25
|
+
const config = parseRepoConfigContent(content);
|
|
26
|
+
if (!config) {
|
|
27
|
+
return {
|
|
28
|
+
configPath,
|
|
29
|
+
config: null,
|
|
30
|
+
error: `Failed to parse ${path.basename(configPath)} — check YAML structure and schema fields`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return { configPath, config };
|
|
34
|
+
}
|
|
35
|
+
export function collectConfiguredChecks(config) {
|
|
36
|
+
const names = new Set();
|
|
37
|
+
for (const context of config.contexts) {
|
|
38
|
+
for (const name of context.ci?.required_checks ?? []) {
|
|
39
|
+
names.add(name);
|
|
40
|
+
}
|
|
41
|
+
for (const name of context.ci?.optional_checks ?? []) {
|
|
42
|
+
names.add(name);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return [...names];
|
|
46
|
+
}
|
|
47
|
+
export function validateConfigStructure(config, configPath) {
|
|
48
|
+
const findings = [];
|
|
49
|
+
const fileName = path.basename(configPath);
|
|
50
|
+
const gateMode = config.gate.mode;
|
|
51
|
+
if (gateMode !== "risk-only" && config.schema_version < 2) {
|
|
52
|
+
findings.push({
|
|
53
|
+
severity: "warn",
|
|
54
|
+
code: "schema_version",
|
|
55
|
+
message: `${fileName} uses schema_version ${config.schema_version} with gate.mode=${gateMode} — consider schema_version: 2 and contexts[]`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const risk = config.thresholds.risk;
|
|
59
|
+
const warn = config.thresholds.warn;
|
|
60
|
+
if (risk !== undefined && warn !== undefined && warn >= risk) {
|
|
61
|
+
findings.push({
|
|
62
|
+
severity: "warn",
|
|
63
|
+
code: "threshold_order",
|
|
64
|
+
message: `warn threshold (${warn}) should be lower than risk threshold (${risk})`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (gateMode !== "risk-only") {
|
|
68
|
+
if (config.contexts.length === 0) {
|
|
69
|
+
findings.push({
|
|
70
|
+
severity: "error",
|
|
71
|
+
code: "missing_contexts",
|
|
72
|
+
message: "release-ready/advisory mode requires at least one contexts[] entry with ci.required_checks",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
for (const context of config.contexts) {
|
|
76
|
+
const required = context.ci?.required_checks ?? [];
|
|
77
|
+
if (required.length === 0) {
|
|
78
|
+
findings.push({
|
|
79
|
+
severity: "warn",
|
|
80
|
+
code: "empty_required_checks",
|
|
81
|
+
message: `Context "${context.name}" has no ci.required_checks — release-ready may not wait for CI`,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const gateCheck = resolveCheckName(gateMode, config.gate.check_name);
|
|
87
|
+
if (gateMode !== "risk-only" && !gateCheck.includes("Release Ready")) {
|
|
88
|
+
findings.push({
|
|
89
|
+
severity: "info",
|
|
90
|
+
code: "custom_check_name",
|
|
91
|
+
message: `Branch protection should require check name "${gateCheck}"`,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return findings;
|
|
95
|
+
}
|
|
96
|
+
export function compareConfiguredChecks(configuredChecks, observedChecks) {
|
|
97
|
+
if (observedChecks.length === 0) {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
const findings = [];
|
|
101
|
+
const observed = [...new Set(observedChecks)];
|
|
102
|
+
for (const configured of configuredChecks) {
|
|
103
|
+
const matched = observed.some((actual) => checkNameMatches(configured, actual));
|
|
104
|
+
if (!matched) {
|
|
105
|
+
findings.push({
|
|
106
|
+
severity: "warn",
|
|
107
|
+
code: "unknown_check_name",
|
|
108
|
+
message: `Configured check "${configured}" did not match any recent GitHub check run`,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const selfChecks = new Set([
|
|
113
|
+
"Trailhead",
|
|
114
|
+
"Trailhead — Release Ready",
|
|
115
|
+
resolveCheckName("release-ready"),
|
|
116
|
+
resolveCheckName("risk-only"),
|
|
117
|
+
]);
|
|
118
|
+
for (const actual of observed) {
|
|
119
|
+
if (selfChecks.has(actual))
|
|
120
|
+
continue;
|
|
121
|
+
const referenced = configuredChecks.some((configured) => checkNameMatches(configured, actual));
|
|
122
|
+
if (!referenced) {
|
|
123
|
+
findings.push({
|
|
124
|
+
severity: "info",
|
|
125
|
+
code: "unconfigured_check",
|
|
126
|
+
message: `GitHub check "${actual}" is not referenced in contexts[].ci (optional unless required)`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return findings;
|
|
131
|
+
}
|
|
132
|
+
export function parseRepoRef(input) {
|
|
133
|
+
const match = input.match(/^([^/]+)\/([^/]+)$/);
|
|
134
|
+
if (!match)
|
|
135
|
+
return null;
|
|
136
|
+
return { owner: match[1], repo: match[2] };
|
|
137
|
+
}
|
|
138
|
+
async function githubRequest(token, url) {
|
|
139
|
+
const response = await fetch(url, {
|
|
140
|
+
headers: {
|
|
141
|
+
Accept: "application/vnd.github+json",
|
|
142
|
+
Authorization: `Bearer ${token}`,
|
|
143
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
const body = await response.text().catch(() => "");
|
|
148
|
+
return {
|
|
149
|
+
ok: false,
|
|
150
|
+
status: response.status,
|
|
151
|
+
message: body || response.statusText,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return { ok: true, data: (await response.json()) };
|
|
155
|
+
}
|
|
156
|
+
async function resolveCommitSha(token, repoRef, ref) {
|
|
157
|
+
if (ref)
|
|
158
|
+
return ref;
|
|
159
|
+
const pulls = await githubRequest(token, `https://api.github.com/repos/${repoRef.owner}/${repoRef.repo}/pulls?state=open&per_page=1`);
|
|
160
|
+
if (pulls.ok && pulls.data[0]?.head.sha) {
|
|
161
|
+
return pulls.data[0].head.sha;
|
|
162
|
+
}
|
|
163
|
+
const repo = await githubRequest(token, `https://api.github.com/repos/${repoRef.owner}/${repoRef.repo}`);
|
|
164
|
+
if (!repo.ok)
|
|
165
|
+
return null;
|
|
166
|
+
const branch = await githubRequest(token, `https://api.github.com/repos/${repoRef.owner}/${repoRef.repo}/branches/${encodeURIComponent(repo.data.default_branch)}`);
|
|
167
|
+
return branch.ok ? branch.data.commit.sha : null;
|
|
168
|
+
}
|
|
169
|
+
export async function fetchObservedCheckNames(options) {
|
|
170
|
+
const sha = await resolveCommitSha(options.token, options.repo, options.ref);
|
|
171
|
+
if (!sha) {
|
|
172
|
+
return { checks: [], error: "Could not resolve a commit SHA for check lookup" };
|
|
173
|
+
}
|
|
174
|
+
const names = new Set();
|
|
175
|
+
let page = 1;
|
|
176
|
+
while (true) {
|
|
177
|
+
const result = await githubRequest(options.token, `https://api.github.com/repos/${options.repo.owner}/${options.repo.repo}/commits/${sha}/check-runs?per_page=100&page=${page}`);
|
|
178
|
+
if (!result.ok) {
|
|
179
|
+
return {
|
|
180
|
+
checks: [],
|
|
181
|
+
error: `GitHub Checks API failed (HTTP ${result.status})`,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
for (const run of result.data.check_runs) {
|
|
185
|
+
names.add(run.name);
|
|
186
|
+
}
|
|
187
|
+
if (result.data.check_runs.length < 100)
|
|
188
|
+
break;
|
|
189
|
+
page += 1;
|
|
190
|
+
}
|
|
191
|
+
return { checks: [...names].sort() };
|
|
192
|
+
}
|
|
193
|
+
export async function runDoctor(options = {}) {
|
|
194
|
+
const cwd = options.cwd ?? process.cwd();
|
|
195
|
+
const loaded = loadRepoConfig(cwd);
|
|
196
|
+
const findings = [];
|
|
197
|
+
if (!loaded.configPath || !loaded.config) {
|
|
198
|
+
findings.push({
|
|
199
|
+
severity: "error",
|
|
200
|
+
code: "config_missing",
|
|
201
|
+
message: loaded.error ?? "Configuration file not found",
|
|
202
|
+
});
|
|
203
|
+
return {
|
|
204
|
+
configPath: loaded.configPath,
|
|
205
|
+
configValid: false,
|
|
206
|
+
gateMode: "risk-only",
|
|
207
|
+
expectedCheckName: "Trailhead",
|
|
208
|
+
configuredChecks: [],
|
|
209
|
+
observedChecks: [],
|
|
210
|
+
findings,
|
|
211
|
+
ok: false,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
const config = loaded.config;
|
|
215
|
+
const gateMode = config.gate.mode;
|
|
216
|
+
const expectedCheckName = resolveCheckName(gateMode, config.gate.check_name);
|
|
217
|
+
const configuredChecks = collectConfiguredChecks(config);
|
|
218
|
+
findings.push(...validateConfigStructure(config, loaded.configPath));
|
|
219
|
+
let observedChecks = [];
|
|
220
|
+
if (!options.offline) {
|
|
221
|
+
const token = options.githubToken ?? process.env.GITHUB_TOKEN ?? "";
|
|
222
|
+
const repoInput = options.repo ?? process.env.GITHUB_REPOSITORY ?? "";
|
|
223
|
+
const repoRef = parseRepoRef(repoInput);
|
|
224
|
+
if (!token) {
|
|
225
|
+
findings.push({
|
|
226
|
+
severity: "info",
|
|
227
|
+
code: "offline_checks",
|
|
228
|
+
message: "Set GITHUB_TOKEN (or pass --token) to compare configured checks against GitHub",
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
else if (!repoRef) {
|
|
232
|
+
findings.push({
|
|
233
|
+
severity: "info",
|
|
234
|
+
code: "offline_checks",
|
|
235
|
+
message: "Set GITHUB_REPOSITORY (or pass --repo owner/name) to compare checks against GitHub",
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
const observed = await fetchObservedCheckNames({
|
|
240
|
+
token,
|
|
241
|
+
repo: repoRef,
|
|
242
|
+
ref: options.ref,
|
|
243
|
+
});
|
|
244
|
+
observedChecks = observed.checks;
|
|
245
|
+
if (observed.error) {
|
|
246
|
+
findings.push({
|
|
247
|
+
severity: "warn",
|
|
248
|
+
code: "github_checks",
|
|
249
|
+
message: observed.error,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
else if (configuredChecks.length > 0) {
|
|
253
|
+
findings.push(...compareConfiguredChecks(configuredChecks, observedChecks));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const ok = !findings.some((finding) => finding.severity === "error");
|
|
258
|
+
return {
|
|
259
|
+
configPath: loaded.configPath,
|
|
260
|
+
configValid: true,
|
|
261
|
+
gateMode,
|
|
262
|
+
expectedCheckName,
|
|
263
|
+
configuredChecks,
|
|
264
|
+
observedChecks,
|
|
265
|
+
findings,
|
|
266
|
+
ok,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
export function formatDoctorReport(report) {
|
|
270
|
+
const lines = [];
|
|
271
|
+
lines.push("Trailhead Doctor");
|
|
272
|
+
lines.push("================");
|
|
273
|
+
lines.push("");
|
|
274
|
+
if (report.configPath) {
|
|
275
|
+
lines.push(`Config: ${report.configPath}`);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
lines.push("Config: (not found)");
|
|
279
|
+
}
|
|
280
|
+
lines.push(`Gate mode: ${report.gateMode}`);
|
|
281
|
+
lines.push(`Expected branch protection check: ${report.expectedCheckName}`);
|
|
282
|
+
if (report.configuredChecks.length > 0) {
|
|
283
|
+
lines.push(`Configured CI checks: ${report.configuredChecks.join(", ")}`);
|
|
284
|
+
}
|
|
285
|
+
if (report.observedChecks.length > 0) {
|
|
286
|
+
lines.push(`Observed GitHub checks: ${report.observedChecks.join(", ")}`);
|
|
287
|
+
}
|
|
288
|
+
if (report.findings.length === 0) {
|
|
289
|
+
lines.push("");
|
|
290
|
+
lines.push("No issues found.");
|
|
291
|
+
return lines.join("\n");
|
|
292
|
+
}
|
|
293
|
+
lines.push("");
|
|
294
|
+
for (const finding of report.findings) {
|
|
295
|
+
const label = finding.severity === "error"
|
|
296
|
+
? "ERROR"
|
|
297
|
+
: finding.severity === "warn"
|
|
298
|
+
? "WARN"
|
|
299
|
+
: "INFO";
|
|
300
|
+
lines.push(`${label} [${finding.code}] ${finding.message}`);
|
|
301
|
+
}
|
|
302
|
+
lines.push("");
|
|
303
|
+
lines.push(report.ok
|
|
304
|
+
? "Result: OK (review warnings before release-ready rollout)"
|
|
305
|
+
: "Result: FAILED");
|
|
306
|
+
return lines.join("\n");
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/shared/doctor.ts"],"names":[],"mappings":"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"}
|