@catladder/cli 0.0.0-next-71c49b28 → 0.0.0-next-5dc1f907
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/dist/bundles/catenv/index.js +75 -12
- package/dist/bundles/cli/index.js +847 -51
- package/dist/cli/src/apps/cli/commands/project/doctor/DoctorReport.d.ts +22 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/DoctorReport.js +75 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/DoctorReport.js.map +1 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/GcloudProjectInspector.d.ts +15 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/GcloudProjectInspector.js +59 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/GcloudProjectInspector.js.map +1 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/checkCloudRun.d.ts +13 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/checkCloudRun.js +120 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/checkCloudRun.js.map +1 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/checkGithub.d.ts +10 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/checkGithub.js +100 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/checkGithub.js.map +1 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/checkGitlab.d.ts +13 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/checkGitlab.js +79 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/checkGitlab.js.map +1 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/checkStore.d.ts +8 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/checkStore.js +44 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/checkStore.js.map +1 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/index.d.ts +14 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/index.js +48 -0
- package/dist/cli/src/apps/cli/commands/project/doctor/index.js.map +1 -0
- package/dist/cli/src/apps/cli/commands/project/setup/setupAccessTokens.js +4 -1
- package/dist/cli/src/apps/cli/commands/project/setup/setupAccessTokens.js.map +1 -1
- package/dist/cli/src/apps/cli/commands/project/setup/setupCloudRun.d.ts +21 -0
- package/dist/cli/src/apps/cli/commands/project/setup/setupCloudRun.js +31 -11
- package/dist/cli/src/apps/cli/commands/project/setup/setupCloudRun.js.map +1 -1
- package/dist/cli/src/commands/index.d.ts +1 -0
- package/dist/cli/src/commands/index.js +8 -1
- package/dist/cli/src/commands/index.js.map +1 -1
- package/dist/cli/src/commands/project/commandDoctor.d.ts +8 -0
- package/dist/cli/src/commands/project/commandDoctor.js +25 -0
- package/dist/cli/src/commands/project/commandDoctor.js.map +1 -0
- package/dist/cli/src/gcloud/artifactsRegistry.d.ts +1 -0
- package/dist/cli/src/gcloud/artifactsRegistry.js +3 -2
- package/dist/cli/src/gcloud/artifactsRegistry.js.map +1 -1
- package/dist/cli/src/gcloud/serviceAccountNames.d.ts +28 -0
- package/dist/cli/src/gcloud/serviceAccountNames.js +51 -0
- package/dist/cli/src/gcloud/serviceAccountNames.js.map +1 -0
- package/dist/cli/src/gcloud/serviceAccounts.js +6 -27
- package/dist/cli/src/gcloud/serviceAccounts.js.map +1 -1
- package/dist/cli/src/utils/concurrency.d.ts +7 -0
- package/dist/cli/src/utils/concurrency.js +27 -0
- package/dist/cli/src/utils/concurrency.js.map +1 -0
- package/dist/cli/src/utils/github.d.ts +10 -0
- package/dist/cli/src/utils/github.js +26 -1
- package/dist/cli/src/utils/github.js.map +1 -1
- package/dist/cli/src/utils/gitlab.d.ts +6 -0
- package/dist/cli/src/utils/gitlab.js +19 -1
- package/dist/cli/src/utils/gitlab.js.map +1 -1
- package/dist/pipeline/src/backends/github/createGithubJobs.js +24 -2
- package/dist/pipeline/src/backends/github/createGithubJobs.js.map +1 -1
- package/dist/pipeline/src/backends/gitlab/createGitlabJobs.js +8 -7
- package/dist/pipeline/src/backends/gitlab/createGitlabJobs.js.map +1 -1
- package/dist/pipeline/src/build/cache/createJobCache.js +8 -0
- package/dist/pipeline/src/build/cache/createJobCache.js.map +1 -1
- package/dist/pipeline/src/build/node/cache.js +15 -1
- package/dist/pipeline/src/build/node/cache.js.map +1 -1
- package/dist/pipeline/src/build/types.d.ts +19 -0
- package/dist/pipeline/src/build/types.js.map +1 -1
- package/dist/pipeline/src/constants.js +1 -1
- package/dist/pipeline/src/types/jobs.d.ts +6 -0
- package/dist/pipeline/src/types/jobs.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/apps/cli/commands/project/doctor/DoctorReport.ts +72 -0
- package/src/apps/cli/commands/project/doctor/GcloudProjectInspector.ts +77 -0
- package/src/apps/cli/commands/project/doctor/checkCloudRun.ts +148 -0
- package/src/apps/cli/commands/project/doctor/checkGithub.ts +156 -0
- package/src/apps/cli/commands/project/doctor/checkGitlab.ts +133 -0
- package/src/apps/cli/commands/project/doctor/checkStore.ts +43 -0
- package/src/apps/cli/commands/project/doctor/index.ts +59 -0
- package/src/apps/cli/commands/project/setup/setupAccessTokens.ts +9 -2
- package/src/apps/cli/commands/project/setup/setupCloudRun.ts +42 -21
- package/src/commands/index.ts +1 -0
- package/src/commands/project/commandDoctor.ts +21 -0
- package/src/gcloud/__tests__/serviceAccountNames.test.ts +63 -0
- package/src/gcloud/artifactsRegistry.ts +3 -1
- package/src/gcloud/serviceAccountNames.ts +65 -0
- package/src/gcloud/serviceAccounts.ts +6 -38
- package/src/utils/concurrency.ts +25 -0
- package/src/utils/github.ts +48 -0
- package/src/utils/gitlab.ts +24 -0
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { IO } from "../../../../../core/types";
|
|
2
|
+
/**
|
|
3
|
+
* collects the results of all doctor checks: each check logs
|
|
4
|
+
* immediately (progressive output like `project setup`) and problems
|
|
5
|
+
* are repeated in the final summary together with the command that
|
|
6
|
+
* heals them. The report itself never mutates anything — doctor
|
|
7
|
+
* diagnoses, setup heals.
|
|
8
|
+
*/
|
|
9
|
+
export declare class DoctorReport {
|
|
10
|
+
private readonly io;
|
|
11
|
+
private passed;
|
|
12
|
+
private readonly warnings;
|
|
13
|
+
private readonly problems;
|
|
14
|
+
private currentSection;
|
|
15
|
+
constructor(io: IO);
|
|
16
|
+
section(title: string): void;
|
|
17
|
+
ok(message: string): void;
|
|
18
|
+
warn(message: string, remediation?: string): void;
|
|
19
|
+
fail(message: string, remediation?: string): void;
|
|
20
|
+
get hasProblems(): boolean;
|
|
21
|
+
summarize(): void;
|
|
22
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.DoctorReport = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* collects the results of all doctor checks: each check logs
|
|
9
|
+
* immediately (progressive output like `project setup`) and problems
|
|
10
|
+
* are repeated in the final summary together with the command that
|
|
11
|
+
* heals them. The report itself never mutates anything — doctor
|
|
12
|
+
* diagnoses, setup heals.
|
|
13
|
+
*/
|
|
14
|
+
class DoctorReport {
|
|
15
|
+
constructor(io) {
|
|
16
|
+
this.io = io;
|
|
17
|
+
this.passed = 0;
|
|
18
|
+
this.warnings = [];
|
|
19
|
+
this.problems = [];
|
|
20
|
+
this.currentSection = "";
|
|
21
|
+
}
|
|
22
|
+
section(title) {
|
|
23
|
+
this.currentSection = title;
|
|
24
|
+
this.io.log("");
|
|
25
|
+
this.io.log("🩺 " + title);
|
|
26
|
+
}
|
|
27
|
+
ok(message) {
|
|
28
|
+
this.passed++;
|
|
29
|
+
this.io.log(" ✅ " + message);
|
|
30
|
+
}
|
|
31
|
+
warn(message, remediation) {
|
|
32
|
+
this.warnings.push({
|
|
33
|
+
section: this.currentSection,
|
|
34
|
+
message,
|
|
35
|
+
remediation
|
|
36
|
+
});
|
|
37
|
+
this.io.log(" ⚠️ " + message);
|
|
38
|
+
if (remediation) this.io.log(" ↳ " + remediation);
|
|
39
|
+
}
|
|
40
|
+
fail(message, remediation) {
|
|
41
|
+
this.problems.push({
|
|
42
|
+
section: this.currentSection,
|
|
43
|
+
message,
|
|
44
|
+
remediation
|
|
45
|
+
});
|
|
46
|
+
this.io.log(" ❌ " + message);
|
|
47
|
+
if (remediation) this.io.log(" ↳ " + remediation);
|
|
48
|
+
}
|
|
49
|
+
get hasProblems() {
|
|
50
|
+
return this.problems.length > 0;
|
|
51
|
+
}
|
|
52
|
+
summarize() {
|
|
53
|
+
const {
|
|
54
|
+
io
|
|
55
|
+
} = this;
|
|
56
|
+
io.log("");
|
|
57
|
+
io.log("==================================================================================");
|
|
58
|
+
io.log(`🩺 doctor summary: ${this.passed} checks passed, ` + `${this.warnings.length} warnings, ${this.problems.length} problems`);
|
|
59
|
+
if (!this.hasProblems) {
|
|
60
|
+
io.log("");
|
|
61
|
+
io.log("everything looks healthy 😻");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
io.log("");
|
|
65
|
+
for (const {
|
|
66
|
+
section,
|
|
67
|
+
message,
|
|
68
|
+
remediation
|
|
69
|
+
} of this.problems) {
|
|
70
|
+
io.log(`❌ [${section}] ${message}`);
|
|
71
|
+
if (remediation) io.log(` ↳ ${remediation}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.DoctorReport = DoctorReport;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DoctorReport.js","sourceRoot":"","sources":["../../../../../../../../src/apps/cli/commands/project/doctor/DoctorReport.ts"],"names":[],"mappings":";;;AAQA;;;;;;GAMG;AACH,MAAa,YAAY;IAMvB,YAA6B,EAAM;QAAN,OAAE,GAAF,EAAE,CAAI;QAL3B,WAAM,GAAG,CAAC,CAAC;QACF,aAAQ,GAAc,EAAE,CAAC;QACzB,aAAQ,GAAc,EAAE,CAAC;QAClC,mBAAc,GAAG,EAAE,CAAC;IAEU,CAAC;IAEvC,OAAO,CAAC,KAAa;QACnB,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;QAC5B,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,EAAE,CAAC,OAAe;QAChB,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,WAAoB;QACxC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;QAC3E,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC;QAC/B,IAAI,WAAW;YAAE,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,SAAS,GAAG,WAAW,CAAC,CAAC;IACxD,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,WAAoB;QACxC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;QAC3E,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;QAC9B,IAAI,WAAW;YAAE,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,SAAS,GAAG,WAAW,CAAC,CAAC;IACxD,CAAC;IAED,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,SAAS;QACP,MAAM,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC;QACpB,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACX,EAAE,CAAC,GAAG,CACJ,oFAAoF,CACrF,CAAC;QACF,EAAE,CAAC,GAAG,CACJ,sBAAsB,IAAI,CAAC,MAAM,kBAAkB;YACjD,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,cAAc,IAAI,CAAC,QAAQ,CAAC,MAAM,WAAW,CACvE,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACX,EAAE,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;YACtC,OAAO;QACT,CAAC;QACD,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACX,KAAK,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9D,EAAE,CAAC,GAAG,CAAC,MAAM,OAAO,KAAK,OAAO,EAAE,CAAC,CAAC;YACpC,IAAI,WAAW;gBAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,WAAW,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;CACF;AAxDD,oCAwDC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* read-only gcloud queries for the doctor, cached per project: checking
|
|
3
|
+
* many component/env contexts of the same gcloud project costs one
|
|
4
|
+
* `get-iam-policy` / `services list` call, not one per context
|
|
5
|
+
*/
|
|
6
|
+
export declare class GcloudProjectInspector {
|
|
7
|
+
private readonly iamBindings;
|
|
8
|
+
private readonly enabledServices;
|
|
9
|
+
isAuthenticated(): Promise<boolean>;
|
|
10
|
+
private getIamBindings;
|
|
11
|
+
/** all roles bound to the given member (e.g. "serviceAccount:<email>") */
|
|
12
|
+
getRolesOfMember(projectId: string, member: string): Promise<string[]>;
|
|
13
|
+
getEnabledServices(projectId: string): Promise<Set<string>>;
|
|
14
|
+
hasArtifactsRegistry(projectId: string, region: string): Promise<boolean>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.GcloudProjectInspector = void 0;
|
|
7
|
+
const child_process_promise_1 = require("child-process-promise");
|
|
8
|
+
const artifactsRegistry_1 = require("../../../../../gcloud/artifactsRegistry");
|
|
9
|
+
/**
|
|
10
|
+
* read-only gcloud queries for the doctor, cached per project: checking
|
|
11
|
+
* many component/env contexts of the same gcloud project costs one
|
|
12
|
+
* `get-iam-policy` / `services list` call, not one per context
|
|
13
|
+
*/
|
|
14
|
+
class GcloudProjectInspector {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.iamBindings = new Map();
|
|
17
|
+
this.enabledServices = new Map();
|
|
18
|
+
}
|
|
19
|
+
async isAuthenticated() {
|
|
20
|
+
try {
|
|
21
|
+
await (0, child_process_promise_1.exec)("gcloud auth print-access-token");
|
|
22
|
+
return true;
|
|
23
|
+
} catch (_a) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
getIamBindings(projectId) {
|
|
28
|
+
if (!this.iamBindings.has(projectId)) {
|
|
29
|
+
this.iamBindings.set(projectId, (0, child_process_promise_1.exec)(`gcloud projects get-iam-policy ${projectId} --format=json`).then(result => {
|
|
30
|
+
var _a;
|
|
31
|
+
return (_a = JSON.parse(result.stdout).bindings) !== null && _a !== void 0 ? _a : [];
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
return this.iamBindings.get(projectId);
|
|
35
|
+
}
|
|
36
|
+
/** all roles bound to the given member (e.g. "serviceAccount:<email>") */
|
|
37
|
+
async getRolesOfMember(projectId, member) {
|
|
38
|
+
const bindings = await this.getIamBindings(projectId);
|
|
39
|
+
return bindings.filter(binding => {
|
|
40
|
+
var _a;
|
|
41
|
+
return (_a = binding.members) === null || _a === void 0 ? void 0 : _a.includes(member);
|
|
42
|
+
}).map(binding => binding.role);
|
|
43
|
+
}
|
|
44
|
+
getEnabledServices(projectId) {
|
|
45
|
+
if (!this.enabledServices.has(projectId)) {
|
|
46
|
+
this.enabledServices.set(projectId, (0, child_process_promise_1.exec)(`gcloud services list --enabled --project=${projectId} --format="value(config.name)"`).then(result => new Set(result.stdout.split("\n").map(line => line.trim()).filter(Boolean))));
|
|
47
|
+
}
|
|
48
|
+
return this.enabledServices.get(projectId);
|
|
49
|
+
}
|
|
50
|
+
async hasArtifactsRegistry(projectId, region) {
|
|
51
|
+
try {
|
|
52
|
+
await (0, child_process_promise_1.exec)(`gcloud artifacts repositories describe ${artifactsRegistry_1.CATLADDER_REGISTRY_NAME} --project="${projectId}" --location=${region}`);
|
|
53
|
+
return true;
|
|
54
|
+
} catch (_a) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.GcloudProjectInspector = GcloudProjectInspector;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GcloudProjectInspector.js","sourceRoot":"","sources":["../../../../../../../../src/apps/cli/commands/project/doctor/GcloudProjectInspector.ts"],"names":[],"mappings":";;;AAAA,iEAA6C;AAC7C,+EAAkF;AAIlF;;;;GAIG;AACH,MAAa,sBAAsB;IAAnC;QACmB,gBAAW,GAAG,IAAI,GAAG,EAAiC,CAAC;QACvD,oBAAe,GAAG,IAAI,GAAG,EAAgC,CAAC;IAgE7E,CAAC;IA9DC,KAAK,CAAC,eAAe;QACnB,IAAI,CAAC;YACH,MAAM,IAAA,4BAAI,EAAC,gCAAgC,CAAC,CAAC;YAC7C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAEO,cAAc,CAAC,SAAiB;QACtC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,WAAW,CAAC,GAAG,CAClB,SAAS,EACT,IAAA,4BAAI,EAAC,kCAAkC,SAAS,gBAAgB,CAAC,CAAC,IAAI,CACpE,CAAC,MAAM,EAAE,EAAE,WAAC,OAAA,MAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,mCAAI,EAAE,CAAA,EAAA,CACrD,CACF,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC;IAC1C,CAAC;IAED,0EAA0E;IAC1E,KAAK,CAAC,gBAAgB,CAAC,SAAiB,EAAE,MAAc;QACtD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,QAAQ;aACZ,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,WAAC,OAAA,MAAA,OAAO,CAAC,OAAO,0CAAE,QAAQ,CAAC,MAAM,CAAC,CAAA,EAAA,CAAC;aACtD,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,kBAAkB,CAAC,SAAiB;QAClC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,eAAe,CAAC,GAAG,CACtB,SAAS,EACT,IAAA,4BAAI,EACF,4CAA4C,SAAS,gCAAgC,CACtF,CAAC,IAAI,CACJ,CAAC,MAAM,EAAE,EAAE,CACT,IAAI,GAAG,CACL,MAAM,CAAC,MAAM;iBACV,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;iBAC1B,MAAM,CAAC,OAAO,CAAC,CACnB,CACJ,CACF,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,oBAAoB,CACxB,SAAiB,EACjB,MAAc;QAEd,IAAI,CAAC;YACH,MAAM,IAAA,4BAAI,EACR,0CAA0C,2CAAuB,eAAe,SAAS,gBAAgB,MAAM,EAAE,CAClH,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF;AAlED,wDAkEC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ComponentContext } from "../../../../../../../pipeline/src/index.js";
|
|
2
|
+
import type { DoctorReport } from "./DoctorReport";
|
|
3
|
+
/**
|
|
4
|
+
* compares the gcloud state `setupCloudRun` would provision for the
|
|
5
|
+
* CURRENT config against what is actually there: enabled services and
|
|
6
|
+
* artifacts registry per project, deploy service account existence and
|
|
7
|
+
* IAM roles per component/env.
|
|
8
|
+
*
|
|
9
|
+
* The motivating case (#70): a component gets `cloudSql` after its
|
|
10
|
+
* setup ran — deploys keep working and only the review teardown hits
|
|
11
|
+
* the missing `roles/cloudsql.admin` as a 403 in ci.
|
|
12
|
+
*/
|
|
13
|
+
export declare const checkCloudRun: (report: DoctorReport, contexts: ComponentContext[]) => Promise<void>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.checkCloudRun = void 0;
|
|
7
|
+
const pipeline_1 = require("../../../../../../../pipeline/src/index.js");
|
|
8
|
+
const artifactsRegistry_1 = require("../../../../../gcloud/artifactsRegistry");
|
|
9
|
+
const serviceAccountNames_1 = require("../../../../../gcloud/serviceAccountNames");
|
|
10
|
+
const serviceAccounts_1 = require("../../../../../gcloud/serviceAccounts");
|
|
11
|
+
const concurrency_1 = require("../../../../../utils/concurrency");
|
|
12
|
+
const setupCloudRun_1 = require("../setup/setupCloudRun");
|
|
13
|
+
const GcloudProjectInspector_1 = require("./GcloudProjectInspector");
|
|
14
|
+
const getCloudRunConfig = context => {
|
|
15
|
+
var _a;
|
|
16
|
+
const config = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
|
|
17
|
+
if (!(0, pipeline_1.isOfDeployType)(config, "google-cloudrun")) {
|
|
18
|
+
throw new Error("not a cloud run context");
|
|
19
|
+
}
|
|
20
|
+
return config;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* compares the gcloud state `setupCloudRun` would provision for the
|
|
24
|
+
* CURRENT config against what is actually there: enabled services and
|
|
25
|
+
* artifacts registry per project, deploy service account existence and
|
|
26
|
+
* IAM roles per component/env.
|
|
27
|
+
*
|
|
28
|
+
* The motivating case (#70): a component gets `cloudSql` after its
|
|
29
|
+
* setup ran — deploys keep working and only the review teardown hits
|
|
30
|
+
* the missing `roles/cloudsql.admin` as a 403 in ci.
|
|
31
|
+
*/
|
|
32
|
+
const checkCloudRun = async (report, contexts) => {
|
|
33
|
+
var _a;
|
|
34
|
+
const cloudRunContexts = contexts.filter(context => {
|
|
35
|
+
var _a;
|
|
36
|
+
return (0, pipeline_1.isOfDeployType)((_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config, "google-cloudrun");
|
|
37
|
+
});
|
|
38
|
+
if (cloudRunContexts.length === 0) return;
|
|
39
|
+
report.section("google cloud run infrastructure");
|
|
40
|
+
const inspector = new GcloudProjectInspector_1.GcloudProjectInspector();
|
|
41
|
+
if (!(await inspector.isAuthenticated())) {
|
|
42
|
+
report.fail("gcloud is not authenticated — all google cloud checks skipped", "run: gcloud auth login");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// project-level: enabled services + artifacts registry. The required
|
|
46
|
+
// services are the union over all components deploying to the project
|
|
47
|
+
// (cloudSql on any of them requires the sql services).
|
|
48
|
+
const byProject = new Map();
|
|
49
|
+
for (const context of cloudRunContexts) {
|
|
50
|
+
const {
|
|
51
|
+
projectId
|
|
52
|
+
} = getCloudRunConfig(context);
|
|
53
|
+
byProject.set(projectId, [...((_a = byProject.get(projectId)) !== null && _a !== void 0 ? _a : []), context]);
|
|
54
|
+
}
|
|
55
|
+
for (const [projectId, projectContexts] of byProject) {
|
|
56
|
+
const requiredServices = new Set(projectContexts.flatMap(context => (0, setupCloudRun_1.getCloudRunRequiredServices)(getCloudRunConfig(context))));
|
|
57
|
+
try {
|
|
58
|
+
const enabled = await inspector.getEnabledServices(projectId);
|
|
59
|
+
const missing = [...requiredServices].filter(service => !enabled.has(service));
|
|
60
|
+
if (missing.length === 0) {
|
|
61
|
+
report.ok(`${projectId}: all ${requiredServices.size} required services enabled`);
|
|
62
|
+
} else {
|
|
63
|
+
report.fail(`${projectId}: services not enabled: ${missing.join(", ")}`, "run: catladder project setup");
|
|
64
|
+
}
|
|
65
|
+
} catch (e) {
|
|
66
|
+
report.warn(`${projectId}: could not list enabled services (${e.message})`);
|
|
67
|
+
}
|
|
68
|
+
const regions = new Set(projectContexts.map(context => getCloudRunConfig(context).region));
|
|
69
|
+
for (const region of regions) {
|
|
70
|
+
if (await inspector.hasArtifactsRegistry(projectId, region)) {
|
|
71
|
+
report.ok(`${projectId}: artifacts registry '${artifactsRegistry_1.CATLADDER_REGISTRY_NAME}' exists in ${region}`);
|
|
72
|
+
} else {
|
|
73
|
+
report.fail(`${projectId}: artifacts registry '${artifactsRegistry_1.CATLADDER_REGISTRY_NAME}' missing in ${region}`, "run: catladder project setup");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// per component/env: deploy service account exists and carries every
|
|
78
|
+
// role the current config implies
|
|
79
|
+
const results = await (0, concurrency_1.mapWithConcurrency)(cloudRunContexts, 5, async context => {
|
|
80
|
+
const config = getCloudRunConfig(context);
|
|
81
|
+
const {
|
|
82
|
+
fullIdentifier
|
|
83
|
+
} = (0, serviceAccountNames_1.getGcloudServiceAccountNames)(context, {
|
|
84
|
+
name: setupCloudRun_1.CLOUD_RUN_DEPLOY_SA_NAME,
|
|
85
|
+
projectId: config.projectId
|
|
86
|
+
});
|
|
87
|
+
if (!(await (0, serviceAccounts_1.accountExists)(fullIdentifier))) {
|
|
88
|
+
return {
|
|
89
|
+
context,
|
|
90
|
+
fullIdentifier,
|
|
91
|
+
exists: false,
|
|
92
|
+
missingRoles: []
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const boundRoles = await inspector.getRolesOfMember(config.projectId, `serviceAccount:${fullIdentifier}`);
|
|
96
|
+
const missingRoles = (0, setupCloudRun_1.getCloudRunDeployRoles)(config).filter(role => !boundRoles.includes(role));
|
|
97
|
+
return {
|
|
98
|
+
context,
|
|
99
|
+
fullIdentifier,
|
|
100
|
+
exists: true,
|
|
101
|
+
missingRoles
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
for (const {
|
|
105
|
+
context,
|
|
106
|
+
fullIdentifier,
|
|
107
|
+
exists,
|
|
108
|
+
missingRoles
|
|
109
|
+
} of results) {
|
|
110
|
+
const label = `${context.env}:${context.name}`;
|
|
111
|
+
if (!exists) {
|
|
112
|
+
report.fail(`${label}: deploy service account ${fullIdentifier} does not exist`, `run: catladder project setup ${context.name}`);
|
|
113
|
+
} else if (missingRoles.length > 0) {
|
|
114
|
+
report.fail(`${label}: service account is missing roles: ${missingRoles.join(", ")}`, `run: catladder project setup ${context.name}`);
|
|
115
|
+
} else {
|
|
116
|
+
report.ok(`${label}: service account exists with all config-implied roles`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
exports.checkCloudRun = checkCloudRun;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkCloudRun.js","sourceRoot":"","sources":["../../../../../../../../src/apps/cli/commands/project/doctor/checkCloudRun.ts"],"names":[],"mappings":";;;AACA,kDAAqD;AACrD,+EAAkF;AAClF,mFAAyF;AACzF,2EAAsE;AACtE,kEAAsE;AACtE,0DAIgC;AAEhC,qEAAkE;AAElE,MAAM,iBAAiB,GAAG,CAAC,OAAyB,EAAE,EAAE;;IACtD,MAAM,MAAM,GAAG,MAAA,OAAO,CAAC,MAAM,0CAAE,MAAM,CAAC;IACtC,IAAI,CAAC,IAAA,yBAAc,EAAC,MAAM,EAAE,iBAAiB,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACI,MAAM,aAAa,GAAG,KAAK,EAChC,MAAoB,EACpB,QAA4B,EAC5B,EAAE;;IACF,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,WACnD,OAAA,IAAA,yBAAc,EAAC,MAAA,OAAO,CAAC,MAAM,0CAAE,MAAM,EAAE,iBAAiB,CAAC,CAAA,EAAA,CAC1D,CAAC;IACF,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAE1C,MAAM,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,IAAI,+CAAsB,EAAE,CAAC;IAC/C,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,eAAe,EAAE,CAAC,EAAE,CAAC;QACzC,MAAM,CAAC,IAAI,CACT,+DAA+D,EAC/D,wBAAwB,CACzB,CAAC;QACF,OAAO;IACT,CAAC;IAED,qEAAqE;IACrE,sEAAsE;IACtE,uDAAuD;IACvD,MAAM,SAAS,GAAG,IAAI,GAAG,EAA8B,CAAC;IACxD,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;QACvC,MAAM,EAAE,SAAS,EAAE,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACjD,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,MAAA,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,mCAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,KAAK,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,IAAI,SAAS,EAAE,CAAC;QACrD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAC9B,eAAe,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAClC,IAAA,2CAA2B,EAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CACxD,CACF,CAAC;QACF,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;YAC9D,MAAM,OAAO,GAAG,CAAC,GAAG,gBAAgB,CAAC,CAAC,MAAM,CAC1C,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CACnC,CAAC;YACF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,MAAM,CAAC,EAAE,CACP,GAAG,SAAS,SAAS,gBAAgB,CAAC,IAAI,4BAA4B,CACvE,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CACT,GAAG,SAAS,2BAA2B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAC3D,8BAA8B,CAC/B,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CACT,GAAG,SAAS,sCAAsC,CAAC,CAAC,OAAO,GAAG,CAC/D,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,eAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CACpE,CAAC;QACF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,SAAS,CAAC,oBAAoB,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC;gBAC5D,MAAM,CAAC,EAAE,CACP,GAAG,SAAS,yBAAyB,2CAAuB,eAAe,MAAM,EAAE,CACpF,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CACT,GAAG,SAAS,yBAAyB,2CAAuB,gBAAgB,MAAM,EAAE,EACpF,8BAA8B,CAC/B,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,qEAAqE;IACrE,kCAAkC;IAClC,MAAM,OAAO,GAAG,MAAM,IAAA,gCAAkB,EACtC,gBAAgB,EAChB,CAAC,EACD,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,MAAM,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,EAAE,cAAc,EAAE,GAAG,IAAA,kDAA4B,EAAC,OAAO,EAAE;YAC/D,IAAI,EAAE,wCAAwB;YAC9B,SAAS,EAAE,MAAM,CAAC,SAAS;SAC5B,CAAC,CAAC;QACH,IAAI,CAAC,CAAC,MAAM,IAAA,+BAAa,EAAC,cAAc,CAAC,CAAC,EAAE,CAAC;YAC3C,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC;QACtE,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,gBAAgB,CACjD,MAAM,CAAC,SAAS,EAChB,kBAAkB,cAAc,EAAE,CACnC,CAAC;QACF,MAAM,YAAY,GAAG,IAAA,sCAAsB,EAAC,MAAM,CAAC,CAAC,MAAM,CACxD,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CACrC,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;IACjE,CAAC,CACF,CAAC;IAEF,KAAK,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,OAAO,EAAE,CAAC;QACxE,MAAM,KAAK,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CACT,GAAG,KAAK,4BAA4B,cAAc,iBAAiB,EACnE,gCAAgC,OAAO,CAAC,IAAI,EAAE,CAC/C,CAAC;QACJ,CAAC;aAAM,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,CAAC,IAAI,CACT,GAAG,KAAK,uCAAuC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EACxE,gCAAgC,OAAO,CAAC,IAAI,EAAE,CAC/C,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,EAAE,CACP,GAAG,KAAK,wDAAwD,CACjE,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAnHW,QAAA,aAAa,iBAmHxB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Config } from "../../../../../../../pipeline/src/index.js";
|
|
2
|
+
import type { DoctorReport } from "./DoctorReport";
|
|
3
|
+
/**
|
|
4
|
+
* compares the secret/variable NAMES the generated github workflows
|
|
5
|
+
* reference against what is actually set — per github environment, the
|
|
6
|
+
* same mapping `project secrets-sync-github` writes. Values cannot be
|
|
7
|
+
* compared: github secrets are write-only by design (the vault stays
|
|
8
|
+
* the source of truth).
|
|
9
|
+
*/
|
|
10
|
+
export declare const checkGithub: (report: DoctorReport, config: Config) => Promise<void>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.checkGithub = void 0;
|
|
7
|
+
const pipeline_1 = require("../../../../../../../pipeline/src/index.js");
|
|
8
|
+
const commandSecretsSyncGithub_1 = require("../../../../../commands/project/commandSecretsSyncGithub");
|
|
9
|
+
const concurrency_1 = require("../../../../../utils/concurrency");
|
|
10
|
+
const github_1 = require("../../../../../utils/github");
|
|
11
|
+
/** marker for repo-level (environment-less) targets, see collectSecretTargets */
|
|
12
|
+
const REPO_LEVEL = "";
|
|
13
|
+
/**
|
|
14
|
+
* compares the secret/variable NAMES the generated github workflows
|
|
15
|
+
* reference against what is actually set — per github environment, the
|
|
16
|
+
* same mapping `project secrets-sync-github` writes. Values cannot be
|
|
17
|
+
* compared: github secrets are write-only by design (the vault stays
|
|
18
|
+
* the source of truth).
|
|
19
|
+
*/
|
|
20
|
+
const checkGithub = async (report, config) => {
|
|
21
|
+
if (!(0, pipeline_1.getEnabledPipelineTypes)(config).includes("github")) return;
|
|
22
|
+
report.section("github environments & secrets");
|
|
23
|
+
if (!(await (0, github_1.isGhAuthenticated)())) {
|
|
24
|
+
report.warn("github cli (gh) not authenticated — github checks skipped", "run: gh auth login");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const repo = await (0, commandSecretsSyncGithub_1.getConfiguredGithubRepo)();
|
|
28
|
+
if (!repo) {
|
|
29
|
+
report.fail(`no github repository found on the '${(0, pipeline_1.getPipelineGitRemote)(config, "github")}' remote`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
let targets;
|
|
33
|
+
try {
|
|
34
|
+
targets = await (0, commandSecretsSyncGithub_1.collectSecretTargets)(config);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
report.warn(`could not generate the github workflows to derive secret references (${e.message})`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const referencedNamesOf = environment => [...targets.entries()].filter(([, envs]) => envs.has(environment)).map(([name]) => name);
|
|
40
|
+
const environmentsNeeded = [...new Set([...targets.values()].flatMap(envs => [...envs]))].filter(environment => environment !== REPO_LEVEL).sort();
|
|
41
|
+
let existingEnvironments;
|
|
42
|
+
try {
|
|
43
|
+
existingEnvironments = new Set(await (0, github_1.listGithubEnvironments)(repo));
|
|
44
|
+
} catch (e) {
|
|
45
|
+
report.warn(`could not list environments of ${repo} (${e.message})`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const perEnvironment = await (0, concurrency_1.mapWithConcurrency)(environmentsNeeded, 8, async environment => {
|
|
49
|
+
const referenced = referencedNamesOf(environment);
|
|
50
|
+
if (!existingEnvironments.has(environment)) {
|
|
51
|
+
return {
|
|
52
|
+
environment,
|
|
53
|
+
referenced,
|
|
54
|
+
exists: false
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const [secrets, variables] = await Promise.all([(0, github_1.listGithubSecretNames)(repo, environment), (0, github_1.listGithubVariableNames)(repo, environment)]);
|
|
58
|
+
// github secret names are case-insensitive and the api returns
|
|
59
|
+
// them uppercased — compare accordingly
|
|
60
|
+
const present = new Set([...secrets, ...variables].map(name => name.toUpperCase()));
|
|
61
|
+
const referencedUpper = new Set(referenced.map(name => name.toUpperCase()));
|
|
62
|
+
return {
|
|
63
|
+
environment,
|
|
64
|
+
referenced,
|
|
65
|
+
exists: true,
|
|
66
|
+
missing: referenced.filter(name => !present.has(name.toUpperCase())),
|
|
67
|
+
stale: [...present].filter(name => !referencedUpper.has(name))
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
for (const result of perEnvironment) {
|
|
71
|
+
const {
|
|
72
|
+
environment,
|
|
73
|
+
referenced
|
|
74
|
+
} = result;
|
|
75
|
+
if (!result.exists) {
|
|
76
|
+
report.fail(`environment '${environment}' does not exist (${referenced.length} referenced secrets have nowhere to live)`, "run: catladder project secrets-sync-github");
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (result.missing.length > 0) {
|
|
80
|
+
report.fail(`${environment}: missing ${result.missing.length}/${referenced.length} referenced secrets: ${result.missing.join(", ")}`, "run: catladder project secrets-sync-github");
|
|
81
|
+
} else {
|
|
82
|
+
report.ok(`${environment}: all ${referenced.length} referenced secrets/variables present`);
|
|
83
|
+
}
|
|
84
|
+
if (result.stale.length > 0) {
|
|
85
|
+
report.warn(`${environment}: ${result.stale.length} set but not referenced by any workflow (stale?): ${result.stale.join(", ")}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const repoLevelReferenced = referencedNamesOf(REPO_LEVEL);
|
|
89
|
+
if (repoLevelReferenced.length > 0) {
|
|
90
|
+
const [secrets, variables] = await Promise.all([(0, github_1.listGithubSecretNames)(repo), (0, github_1.listGithubVariableNames)(repo)]);
|
|
91
|
+
const present = new Set([...secrets, ...variables].map(name => name.toUpperCase()));
|
|
92
|
+
const missing = repoLevelReferenced.filter(name => !present.has(name.toUpperCase()));
|
|
93
|
+
if (missing.length > 0) {
|
|
94
|
+
report.fail(`repo level: missing ${missing.length}/${repoLevelReferenced.length} referenced secrets: ${missing.join(", ")}`, "run: catladder project secrets-sync-github");
|
|
95
|
+
} else {
|
|
96
|
+
report.ok(`repo level: all ${repoLevelReferenced.length} referenced secrets/variables present`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
exports.checkGithub = checkGithub;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkGithub.js","sourceRoot":"","sources":["../../../../../../../../src/apps/cli/commands/project/doctor/checkGithub.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ComponentContext } from "../../../../../../../pipeline/src/index.js";
|
|
2
|
+
import type { IO } from "../../../../../core/types";
|
|
3
|
+
import type { DoctorReport } from "./DoctorReport";
|
|
4
|
+
/**
|
|
5
|
+
* the gitlab-side outputs of `project setup`: the semantic-release
|
|
6
|
+
* access token (+ its GL_TOKEN ci variable), the catladder topic and —
|
|
7
|
+
* for kubernetes deploys — the registry deploy token.
|
|
8
|
+
*
|
|
9
|
+
* Kubernetes namespace/rbac checks are deliberately skipped: they would
|
|
10
|
+
* require connecting kubectl to every cluster, which switches the local
|
|
11
|
+
* kubeconfig context — doctor must not change anything.
|
|
12
|
+
*/
|
|
13
|
+
export declare const checkGitlab: (instance: IO, report: DoctorReport, contexts: ComponentContext[]) => Promise<void>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.checkGitlab = void 0;
|
|
7
|
+
const pipeline_1 = require("../../../../../../../pipeline/src/index.js");
|
|
8
|
+
const date_fns_1 = require("date-fns");
|
|
9
|
+
const gitlab_1 = require("../../../../../utils/gitlab");
|
|
10
|
+
const EXPIRY_WARNING_DAYS = 30;
|
|
11
|
+
/**
|
|
12
|
+
* the gitlab-side outputs of `project setup`: the semantic-release
|
|
13
|
+
* access token (+ its GL_TOKEN ci variable), the catladder topic and —
|
|
14
|
+
* for kubernetes deploys — the registry deploy token.
|
|
15
|
+
*
|
|
16
|
+
* Kubernetes namespace/rbac checks are deliberately skipped: they would
|
|
17
|
+
* require connecting kubectl to every cluster, which switches the local
|
|
18
|
+
* kubeconfig context — doctor must not change anything.
|
|
19
|
+
*/
|
|
20
|
+
const checkGitlab = async (instance, report, contexts) => {
|
|
21
|
+
report.section("gitlab project setup");
|
|
22
|
+
let projectId;
|
|
23
|
+
try {
|
|
24
|
+
projectId = (await (0, gitlab_1.getProjectInfo)(instance)).id;
|
|
25
|
+
} catch (e) {
|
|
26
|
+
report.warn(`could not reach the gitlab api — gitlab checks skipped (${e.message})`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const tokens = await (0, gitlab_1.doGitlabRequestAllPages)(instance, `projects/${projectId}/access_tokens`);
|
|
31
|
+
const activeTokens = tokens.filter(t => t.name === "semantic-release" && t.active === true);
|
|
32
|
+
// the GL_TOKEN ci variable holds exactly one of them — judge by the
|
|
33
|
+
// one that lives longest
|
|
34
|
+
const token = activeTokens.sort((a, b) => String(b.expires_at).localeCompare(String(a.expires_at)))[0];
|
|
35
|
+
if (activeTokens.length > 1) {
|
|
36
|
+
report.warn(`${activeTokens.length} active 'semantic-release' tokens exist — duplicates from earlier setups, consider revoking the old ones`);
|
|
37
|
+
}
|
|
38
|
+
if (!token) {
|
|
39
|
+
report.fail("no active 'semantic-release' project access token", "run: catladder project setup");
|
|
40
|
+
} else {
|
|
41
|
+
const daysLeft = (0, date_fns_1.differenceInCalendarDays)(new Date(token.expires_at), new Date());
|
|
42
|
+
if (daysLeft < 0) {
|
|
43
|
+
report.fail(`'semantic-release' token expired on ${token.expires_at}`, "run: catladder project setup (rotates the token)");
|
|
44
|
+
} else if (daysLeft <= EXPIRY_WARNING_DAYS) {
|
|
45
|
+
report.warn(`'semantic-release' token expires in ${daysLeft} days (${token.expires_at})`, "run: catladder project setup (rotates the token)");
|
|
46
|
+
} else {
|
|
47
|
+
report.ok(`'semantic-release' token active (expires ${token.expires_at})`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
await (0, gitlab_1.doGitlabRequest)(instance, `projects/${projectId}/variables/GL_TOKEN`);
|
|
52
|
+
report.ok("GL_TOKEN ci variable present");
|
|
53
|
+
} catch (_a) {
|
|
54
|
+
report.fail("GL_TOKEN ci variable missing", "run: catladder project setup");
|
|
55
|
+
}
|
|
56
|
+
const {
|
|
57
|
+
topics
|
|
58
|
+
} = await (0, gitlab_1.doGitlabRequest)(instance, `projects/${projectId}`);
|
|
59
|
+
if (topics === null || topics === void 0 ? void 0 : topics.includes("catladder")) {
|
|
60
|
+
report.ok("'catladder' project topic set");
|
|
61
|
+
} else {
|
|
62
|
+
report.fail("'catladder' project topic missing", "run: catladder project setup");
|
|
63
|
+
}
|
|
64
|
+
if (contexts.some(context => {
|
|
65
|
+
var _a;
|
|
66
|
+
return (0, pipeline_1.isOfDeployType)((_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config, "kubernetes");
|
|
67
|
+
})) {
|
|
68
|
+
const deployTokens = await (0, gitlab_1.doGitlabRequestAllPages)(instance, `projects/${projectId}/deploy_tokens`);
|
|
69
|
+
if (deployTokens.find(t => t.name === "gitlab-deploy-token")) {
|
|
70
|
+
report.ok("'gitlab-deploy-token' registry deploy token present");
|
|
71
|
+
} else {
|
|
72
|
+
report.fail("'gitlab-deploy-token' registry deploy token missing (kubernetes cannot pull images)", "run: catladder project setup");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch (e) {
|
|
76
|
+
report.warn(`gitlab checks aborted (${e.message})`);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
exports.checkGitlab = checkGitlab;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkGitlab.js","sourceRoot":"","sources":["../../../../../../../../src/apps/cli/commands/project/doctor/checkGitlab.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DoctorReport } from "./DoctorReport";
|
|
2
|
+
/**
|
|
3
|
+
* every google-cloudrun project in the config needs its project number
|
|
4
|
+
* in the committed store — the deterministic cloud run urls
|
|
5
|
+
* (<service>-<projectNumber>.<region>.run.app) are computed from it at
|
|
6
|
+
* generation time. This check is fully offline.
|
|
7
|
+
*/
|
|
8
|
+
export declare const checkStore: (report: DoctorReport) => Promise<void>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.checkStore = void 0;
|
|
7
|
+
const pipeline_1 = require("../../../../../../../pipeline/src/index.js");
|
|
8
|
+
const getProjectConfig_1 = require("../../../../../config/getProjectConfig");
|
|
9
|
+
const projects_1 = require("../../../../../utils/projects");
|
|
10
|
+
/**
|
|
11
|
+
* every google-cloudrun project in the config needs its project number
|
|
12
|
+
* in the committed store — the deterministic cloud run urls
|
|
13
|
+
* (<service>-<projectNumber>.<region>.run.app) are computed from it at
|
|
14
|
+
* generation time. This check is fully offline.
|
|
15
|
+
*/
|
|
16
|
+
const checkStore = async report => {
|
|
17
|
+
var _a, _b, _c;
|
|
18
|
+
const config = await (0, getProjectConfig_1.getProjectConfig)();
|
|
19
|
+
const projectIds = new Set();
|
|
20
|
+
for (const {
|
|
21
|
+
componentName,
|
|
22
|
+
env
|
|
23
|
+
} of await (0, getProjectConfig_1.getAllComponentsWithAllEnvsFlat)()) {
|
|
24
|
+
const {
|
|
25
|
+
deploy
|
|
26
|
+
} = (0, pipeline_1.getEnvConfig)(config, componentName, env);
|
|
27
|
+
if (deploy && deploy.type === "google-cloudrun") {
|
|
28
|
+
projectIds.add(deploy.projectId);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (projectIds.size === 0) return;
|
|
32
|
+
report.section("catladder store (.catladder-store/store.yml)");
|
|
33
|
+
const root = (_a = await (0, projects_1.getGitRoot)()) !== null && _a !== void 0 ? _a : process.cwd();
|
|
34
|
+
const store = (0, pipeline_1.readCatladderStore)(root);
|
|
35
|
+
for (const projectId of projectIds) {
|
|
36
|
+
const projectNumber = (_c = (_b = store.gcloudProjects) === null || _b === void 0 ? void 0 : _b[projectId]) === null || _c === void 0 ? void 0 : _c.projectNumber;
|
|
37
|
+
if (projectNumber) {
|
|
38
|
+
report.ok(`project number for ${projectId}: ${projectNumber}`);
|
|
39
|
+
} else {
|
|
40
|
+
report.fail(`missing project number for ${projectId}`, "run: catladder project setup");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
exports.checkStore = checkStore;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkStore.js","sourceRoot":"","sources":["../../../../../../../../src/apps/cli/commands/project/doctor/checkStore.ts"],"names":[],"mappings":";;;AAAA,kDAAuE;AACvE,6EAGgD;AAChD,4DAA2D;AAG3D;;;;;GAKG;AACI,MAAM,UAAU,GAAG,KAAK,EAAE,MAAoB,EAAE,EAAE;;IACvD,MAAM,MAAM,GAAG,MAAM,IAAA,mCAAgB,GAAE,CAAC;IACxC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,KAAK,MAAM,EACT,aAAa,EACb,GAAG,GACJ,IAAI,MAAM,IAAA,kDAA+B,GAAE,EAAE,CAAC;QAC7C,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,uBAAY,EAAC,MAAM,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;QAC5D,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAChD,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IACD,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO;IAElC,MAAM,CAAC,OAAO,CAAC,8CAA8C,CAAC,CAAC;IAC/D,MAAM,IAAI,GAAG,MAAA,CAAC,MAAM,IAAA,qBAAU,GAAE,CAAC,mCAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACnD,MAAM,KAAK,GAAG,IAAA,6BAAkB,EAAC,IAAI,CAAC,CAAC;IACvC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,aAAa,GAAG,MAAA,MAAA,KAAK,CAAC,cAAc,0CAAG,SAAS,CAAC,0CAAE,aAAa,CAAC;QACvE,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,CAAC,EAAE,CAAC,sBAAsB,SAAS,KAAK,aAAa,EAAE,CAAC,CAAC;QACjE,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CACT,8BAA8B,SAAS,EAAE,EACzC,8BAA8B,CAC/B,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AA5BW,QAAA,UAAU,cA4BrB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IO } from "../../../../../core/types";
|
|
2
|
+
/**
|
|
3
|
+
* read-only health check: compares the state `project setup` (and
|
|
4
|
+
* `project secrets-sync-github`) would provision for the CURRENT config
|
|
5
|
+
* against what is actually there, and reports drift together with the
|
|
6
|
+
* command that heals it. `project setup` is a point-in-time
|
|
7
|
+
* provisioner, so config edits that change required infrastructure
|
|
8
|
+
* (IAM roles, services, secrets) drift silently until something breaks
|
|
9
|
+
* in ci — the doctor surfaces that before it does.
|
|
10
|
+
*
|
|
11
|
+
* The component filter only narrows the per-component checks; the
|
|
12
|
+
* cheap project-global checks (store, gitlab, github) always run.
|
|
13
|
+
*/
|
|
14
|
+
export declare const doctorProject: (instance: IO, onlyComponents?: string | string[]) => Promise<void>;
|