@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
|
@@ -587,6 +587,580 @@ exports.projectConfigSecrets = projectConfigSecrets;
|
|
|
587
587
|
|
|
588
588
|
/***/ }),
|
|
589
589
|
|
|
590
|
+
/***/ 88089:
|
|
591
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
592
|
+
|
|
593
|
+
"use strict";
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
Object.defineProperty(exports, "__esModule", ({
|
|
597
|
+
value: true
|
|
598
|
+
}));
|
|
599
|
+
exports.DoctorReport = void 0;
|
|
600
|
+
/**
|
|
601
|
+
* collects the results of all doctor checks: each check logs
|
|
602
|
+
* immediately (progressive output like `project setup`) and problems
|
|
603
|
+
* are repeated in the final summary together with the command that
|
|
604
|
+
* heals them. The report itself never mutates anything — doctor
|
|
605
|
+
* diagnoses, setup heals.
|
|
606
|
+
*/
|
|
607
|
+
class DoctorReport {
|
|
608
|
+
constructor(io) {
|
|
609
|
+
this.io = io;
|
|
610
|
+
this.passed = 0;
|
|
611
|
+
this.warnings = [];
|
|
612
|
+
this.problems = [];
|
|
613
|
+
this.currentSection = "";
|
|
614
|
+
}
|
|
615
|
+
section(title) {
|
|
616
|
+
this.currentSection = title;
|
|
617
|
+
this.io.log("");
|
|
618
|
+
this.io.log("🩺 " + title);
|
|
619
|
+
}
|
|
620
|
+
ok(message) {
|
|
621
|
+
this.passed++;
|
|
622
|
+
this.io.log(" ✅ " + message);
|
|
623
|
+
}
|
|
624
|
+
warn(message, remediation) {
|
|
625
|
+
this.warnings.push({
|
|
626
|
+
section: this.currentSection,
|
|
627
|
+
message,
|
|
628
|
+
remediation
|
|
629
|
+
});
|
|
630
|
+
this.io.log(" ⚠️ " + message);
|
|
631
|
+
if (remediation) this.io.log(" ↳ " + remediation);
|
|
632
|
+
}
|
|
633
|
+
fail(message, remediation) {
|
|
634
|
+
this.problems.push({
|
|
635
|
+
section: this.currentSection,
|
|
636
|
+
message,
|
|
637
|
+
remediation
|
|
638
|
+
});
|
|
639
|
+
this.io.log(" ❌ " + message);
|
|
640
|
+
if (remediation) this.io.log(" ↳ " + remediation);
|
|
641
|
+
}
|
|
642
|
+
get hasProblems() {
|
|
643
|
+
return this.problems.length > 0;
|
|
644
|
+
}
|
|
645
|
+
summarize() {
|
|
646
|
+
const {
|
|
647
|
+
io
|
|
648
|
+
} = this;
|
|
649
|
+
io.log("");
|
|
650
|
+
io.log("==================================================================================");
|
|
651
|
+
io.log(`🩺 doctor summary: ${this.passed} checks passed, ` + `${this.warnings.length} warnings, ${this.problems.length} problems`);
|
|
652
|
+
if (!this.hasProblems) {
|
|
653
|
+
io.log("");
|
|
654
|
+
io.log("everything looks healthy 😻");
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
io.log("");
|
|
658
|
+
for (const {
|
|
659
|
+
section,
|
|
660
|
+
message,
|
|
661
|
+
remediation
|
|
662
|
+
} of this.problems) {
|
|
663
|
+
io.log(`❌ [${section}] ${message}`);
|
|
664
|
+
if (remediation) io.log(` ↳ ${remediation}`);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
exports.DoctorReport = DoctorReport;
|
|
669
|
+
|
|
670
|
+
/***/ }),
|
|
671
|
+
|
|
672
|
+
/***/ 44952:
|
|
673
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
674
|
+
|
|
675
|
+
"use strict";
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
Object.defineProperty(exports, "__esModule", ({
|
|
679
|
+
value: true
|
|
680
|
+
}));
|
|
681
|
+
exports.GcloudProjectInspector = void 0;
|
|
682
|
+
const child_process_promise_1 = __nccwpck_require__(51748);
|
|
683
|
+
const artifactsRegistry_1 = __nccwpck_require__(41624);
|
|
684
|
+
/**
|
|
685
|
+
* read-only gcloud queries for the doctor, cached per project: checking
|
|
686
|
+
* many component/env contexts of the same gcloud project costs one
|
|
687
|
+
* `get-iam-policy` / `services list` call, not one per context
|
|
688
|
+
*/
|
|
689
|
+
class GcloudProjectInspector {
|
|
690
|
+
constructor() {
|
|
691
|
+
this.iamBindings = new Map();
|
|
692
|
+
this.enabledServices = new Map();
|
|
693
|
+
}
|
|
694
|
+
async isAuthenticated() {
|
|
695
|
+
try {
|
|
696
|
+
await (0, child_process_promise_1.exec)("gcloud auth print-access-token");
|
|
697
|
+
return true;
|
|
698
|
+
} catch (_a) {
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
getIamBindings(projectId) {
|
|
703
|
+
if (!this.iamBindings.has(projectId)) {
|
|
704
|
+
this.iamBindings.set(projectId, (0, child_process_promise_1.exec)(`gcloud projects get-iam-policy ${projectId} --format=json`).then(result => {
|
|
705
|
+
var _a;
|
|
706
|
+
return (_a = JSON.parse(result.stdout).bindings) !== null && _a !== void 0 ? _a : [];
|
|
707
|
+
}));
|
|
708
|
+
}
|
|
709
|
+
return this.iamBindings.get(projectId);
|
|
710
|
+
}
|
|
711
|
+
/** all roles bound to the given member (e.g. "serviceAccount:<email>") */
|
|
712
|
+
async getRolesOfMember(projectId, member) {
|
|
713
|
+
const bindings = await this.getIamBindings(projectId);
|
|
714
|
+
return bindings.filter(binding => {
|
|
715
|
+
var _a;
|
|
716
|
+
return (_a = binding.members) === null || _a === void 0 ? void 0 : _a.includes(member);
|
|
717
|
+
}).map(binding => binding.role);
|
|
718
|
+
}
|
|
719
|
+
getEnabledServices(projectId) {
|
|
720
|
+
if (!this.enabledServices.has(projectId)) {
|
|
721
|
+
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))));
|
|
722
|
+
}
|
|
723
|
+
return this.enabledServices.get(projectId);
|
|
724
|
+
}
|
|
725
|
+
async hasArtifactsRegistry(projectId, region) {
|
|
726
|
+
try {
|
|
727
|
+
await (0, child_process_promise_1.exec)(`gcloud artifacts repositories describe ${artifactsRegistry_1.CATLADDER_REGISTRY_NAME} --project="${projectId}" --location=${region}`);
|
|
728
|
+
return true;
|
|
729
|
+
} catch (_a) {
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
exports.GcloudProjectInspector = GcloudProjectInspector;
|
|
735
|
+
|
|
736
|
+
/***/ }),
|
|
737
|
+
|
|
738
|
+
/***/ 65424:
|
|
739
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
740
|
+
|
|
741
|
+
"use strict";
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
Object.defineProperty(exports, "__esModule", ({
|
|
745
|
+
value: true
|
|
746
|
+
}));
|
|
747
|
+
exports.checkCloudRun = void 0;
|
|
748
|
+
const pipeline_1 = __nccwpck_require__(60241);
|
|
749
|
+
const artifactsRegistry_1 = __nccwpck_require__(41624);
|
|
750
|
+
const serviceAccountNames_1 = __nccwpck_require__(83798);
|
|
751
|
+
const serviceAccounts_1 = __nccwpck_require__(35611);
|
|
752
|
+
const concurrency_1 = __nccwpck_require__(5092);
|
|
753
|
+
const setupCloudRun_1 = __nccwpck_require__(51629);
|
|
754
|
+
const GcloudProjectInspector_1 = __nccwpck_require__(44952);
|
|
755
|
+
const getCloudRunConfig = context => {
|
|
756
|
+
var _a;
|
|
757
|
+
const config = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
|
|
758
|
+
if (!(0, pipeline_1.isOfDeployType)(config, "google-cloudrun")) {
|
|
759
|
+
throw new Error("not a cloud run context");
|
|
760
|
+
}
|
|
761
|
+
return config;
|
|
762
|
+
};
|
|
763
|
+
/**
|
|
764
|
+
* compares the gcloud state `setupCloudRun` would provision for the
|
|
765
|
+
* CURRENT config against what is actually there: enabled services and
|
|
766
|
+
* artifacts registry per project, deploy service account existence and
|
|
767
|
+
* IAM roles per component/env.
|
|
768
|
+
*
|
|
769
|
+
* The motivating case (#70): a component gets `cloudSql` after its
|
|
770
|
+
* setup ran — deploys keep working and only the review teardown hits
|
|
771
|
+
* the missing `roles/cloudsql.admin` as a 403 in ci.
|
|
772
|
+
*/
|
|
773
|
+
const checkCloudRun = async (report, contexts) => {
|
|
774
|
+
var _a;
|
|
775
|
+
const cloudRunContexts = contexts.filter(context => {
|
|
776
|
+
var _a;
|
|
777
|
+
return (0, pipeline_1.isOfDeployType)((_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config, "google-cloudrun");
|
|
778
|
+
});
|
|
779
|
+
if (cloudRunContexts.length === 0) return;
|
|
780
|
+
report.section("google cloud run infrastructure");
|
|
781
|
+
const inspector = new GcloudProjectInspector_1.GcloudProjectInspector();
|
|
782
|
+
if (!(await inspector.isAuthenticated())) {
|
|
783
|
+
report.fail("gcloud is not authenticated — all google cloud checks skipped", "run: gcloud auth login");
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
// project-level: enabled services + artifacts registry. The required
|
|
787
|
+
// services are the union over all components deploying to the project
|
|
788
|
+
// (cloudSql on any of them requires the sql services).
|
|
789
|
+
const byProject = new Map();
|
|
790
|
+
for (const context of cloudRunContexts) {
|
|
791
|
+
const {
|
|
792
|
+
projectId
|
|
793
|
+
} = getCloudRunConfig(context);
|
|
794
|
+
byProject.set(projectId, [...((_a = byProject.get(projectId)) !== null && _a !== void 0 ? _a : []), context]);
|
|
795
|
+
}
|
|
796
|
+
for (const [projectId, projectContexts] of byProject) {
|
|
797
|
+
const requiredServices = new Set(projectContexts.flatMap(context => (0, setupCloudRun_1.getCloudRunRequiredServices)(getCloudRunConfig(context))));
|
|
798
|
+
try {
|
|
799
|
+
const enabled = await inspector.getEnabledServices(projectId);
|
|
800
|
+
const missing = [...requiredServices].filter(service => !enabled.has(service));
|
|
801
|
+
if (missing.length === 0) {
|
|
802
|
+
report.ok(`${projectId}: all ${requiredServices.size} required services enabled`);
|
|
803
|
+
} else {
|
|
804
|
+
report.fail(`${projectId}: services not enabled: ${missing.join(", ")}`, "run: catladder project setup");
|
|
805
|
+
}
|
|
806
|
+
} catch (e) {
|
|
807
|
+
report.warn(`${projectId}: could not list enabled services (${e.message})`);
|
|
808
|
+
}
|
|
809
|
+
const regions = new Set(projectContexts.map(context => getCloudRunConfig(context).region));
|
|
810
|
+
for (const region of regions) {
|
|
811
|
+
if (await inspector.hasArtifactsRegistry(projectId, region)) {
|
|
812
|
+
report.ok(`${projectId}: artifacts registry '${artifactsRegistry_1.CATLADDER_REGISTRY_NAME}' exists in ${region}`);
|
|
813
|
+
} else {
|
|
814
|
+
report.fail(`${projectId}: artifacts registry '${artifactsRegistry_1.CATLADDER_REGISTRY_NAME}' missing in ${region}`, "run: catladder project setup");
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
// per component/env: deploy service account exists and carries every
|
|
819
|
+
// role the current config implies
|
|
820
|
+
const results = await (0, concurrency_1.mapWithConcurrency)(cloudRunContexts, 5, async context => {
|
|
821
|
+
const config = getCloudRunConfig(context);
|
|
822
|
+
const {
|
|
823
|
+
fullIdentifier
|
|
824
|
+
} = (0, serviceAccountNames_1.getGcloudServiceAccountNames)(context, {
|
|
825
|
+
name: setupCloudRun_1.CLOUD_RUN_DEPLOY_SA_NAME,
|
|
826
|
+
projectId: config.projectId
|
|
827
|
+
});
|
|
828
|
+
if (!(await (0, serviceAccounts_1.accountExists)(fullIdentifier))) {
|
|
829
|
+
return {
|
|
830
|
+
context,
|
|
831
|
+
fullIdentifier,
|
|
832
|
+
exists: false,
|
|
833
|
+
missingRoles: []
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
const boundRoles = await inspector.getRolesOfMember(config.projectId, `serviceAccount:${fullIdentifier}`);
|
|
837
|
+
const missingRoles = (0, setupCloudRun_1.getCloudRunDeployRoles)(config).filter(role => !boundRoles.includes(role));
|
|
838
|
+
return {
|
|
839
|
+
context,
|
|
840
|
+
fullIdentifier,
|
|
841
|
+
exists: true,
|
|
842
|
+
missingRoles
|
|
843
|
+
};
|
|
844
|
+
});
|
|
845
|
+
for (const {
|
|
846
|
+
context,
|
|
847
|
+
fullIdentifier,
|
|
848
|
+
exists,
|
|
849
|
+
missingRoles
|
|
850
|
+
} of results) {
|
|
851
|
+
const label = `${context.env}:${context.name}`;
|
|
852
|
+
if (!exists) {
|
|
853
|
+
report.fail(`${label}: deploy service account ${fullIdentifier} does not exist`, `run: catladder project setup ${context.name}`);
|
|
854
|
+
} else if (missingRoles.length > 0) {
|
|
855
|
+
report.fail(`${label}: service account is missing roles: ${missingRoles.join(", ")}`, `run: catladder project setup ${context.name}`);
|
|
856
|
+
} else {
|
|
857
|
+
report.ok(`${label}: service account exists with all config-implied roles`);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
exports.checkCloudRun = checkCloudRun;
|
|
862
|
+
|
|
863
|
+
/***/ }),
|
|
864
|
+
|
|
865
|
+
/***/ 93265:
|
|
866
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
867
|
+
|
|
868
|
+
"use strict";
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
Object.defineProperty(exports, "__esModule", ({
|
|
872
|
+
value: true
|
|
873
|
+
}));
|
|
874
|
+
exports.checkGithub = void 0;
|
|
875
|
+
const pipeline_1 = __nccwpck_require__(60241);
|
|
876
|
+
const commandSecretsSyncGithub_1 = __nccwpck_require__(50962);
|
|
877
|
+
const concurrency_1 = __nccwpck_require__(5092);
|
|
878
|
+
const github_1 = __nccwpck_require__(18036);
|
|
879
|
+
/** marker for repo-level (environment-less) targets, see collectSecretTargets */
|
|
880
|
+
const REPO_LEVEL = "";
|
|
881
|
+
/**
|
|
882
|
+
* compares the secret/variable NAMES the generated github workflows
|
|
883
|
+
* reference against what is actually set — per github environment, the
|
|
884
|
+
* same mapping `project secrets-sync-github` writes. Values cannot be
|
|
885
|
+
* compared: github secrets are write-only by design (the vault stays
|
|
886
|
+
* the source of truth).
|
|
887
|
+
*/
|
|
888
|
+
const checkGithub = async (report, config) => {
|
|
889
|
+
if (!(0, pipeline_1.getEnabledPipelineTypes)(config).includes("github")) return;
|
|
890
|
+
report.section("github environments & secrets");
|
|
891
|
+
if (!(await (0, github_1.isGhAuthenticated)())) {
|
|
892
|
+
report.warn("github cli (gh) not authenticated — github checks skipped", "run: gh auth login");
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
const repo = await (0, commandSecretsSyncGithub_1.getConfiguredGithubRepo)();
|
|
896
|
+
if (!repo) {
|
|
897
|
+
report.fail(`no github repository found on the '${(0, pipeline_1.getPipelineGitRemote)(config, "github")}' remote`);
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
let targets;
|
|
901
|
+
try {
|
|
902
|
+
targets = await (0, commandSecretsSyncGithub_1.collectSecretTargets)(config);
|
|
903
|
+
} catch (e) {
|
|
904
|
+
report.warn(`could not generate the github workflows to derive secret references (${e.message})`);
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
const referencedNamesOf = environment => [...targets.entries()].filter(([, envs]) => envs.has(environment)).map(([name]) => name);
|
|
908
|
+
const environmentsNeeded = [...new Set([...targets.values()].flatMap(envs => [...envs]))].filter(environment => environment !== REPO_LEVEL).sort();
|
|
909
|
+
let existingEnvironments;
|
|
910
|
+
try {
|
|
911
|
+
existingEnvironments = new Set(await (0, github_1.listGithubEnvironments)(repo));
|
|
912
|
+
} catch (e) {
|
|
913
|
+
report.warn(`could not list environments of ${repo} (${e.message})`);
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
const perEnvironment = await (0, concurrency_1.mapWithConcurrency)(environmentsNeeded, 8, async environment => {
|
|
917
|
+
const referenced = referencedNamesOf(environment);
|
|
918
|
+
if (!existingEnvironments.has(environment)) {
|
|
919
|
+
return {
|
|
920
|
+
environment,
|
|
921
|
+
referenced,
|
|
922
|
+
exists: false
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
const [secrets, variables] = await Promise.all([(0, github_1.listGithubSecretNames)(repo, environment), (0, github_1.listGithubVariableNames)(repo, environment)]);
|
|
926
|
+
// github secret names are case-insensitive and the api returns
|
|
927
|
+
// them uppercased — compare accordingly
|
|
928
|
+
const present = new Set([...secrets, ...variables].map(name => name.toUpperCase()));
|
|
929
|
+
const referencedUpper = new Set(referenced.map(name => name.toUpperCase()));
|
|
930
|
+
return {
|
|
931
|
+
environment,
|
|
932
|
+
referenced,
|
|
933
|
+
exists: true,
|
|
934
|
+
missing: referenced.filter(name => !present.has(name.toUpperCase())),
|
|
935
|
+
stale: [...present].filter(name => !referencedUpper.has(name))
|
|
936
|
+
};
|
|
937
|
+
});
|
|
938
|
+
for (const result of perEnvironment) {
|
|
939
|
+
const {
|
|
940
|
+
environment,
|
|
941
|
+
referenced
|
|
942
|
+
} = result;
|
|
943
|
+
if (!result.exists) {
|
|
944
|
+
report.fail(`environment '${environment}' does not exist (${referenced.length} referenced secrets have nowhere to live)`, "run: catladder project secrets-sync-github");
|
|
945
|
+
continue;
|
|
946
|
+
}
|
|
947
|
+
if (result.missing.length > 0) {
|
|
948
|
+
report.fail(`${environment}: missing ${result.missing.length}/${referenced.length} referenced secrets: ${result.missing.join(", ")}`, "run: catladder project secrets-sync-github");
|
|
949
|
+
} else {
|
|
950
|
+
report.ok(`${environment}: all ${referenced.length} referenced secrets/variables present`);
|
|
951
|
+
}
|
|
952
|
+
if (result.stale.length > 0) {
|
|
953
|
+
report.warn(`${environment}: ${result.stale.length} set but not referenced by any workflow (stale?): ${result.stale.join(", ")}`);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
const repoLevelReferenced = referencedNamesOf(REPO_LEVEL);
|
|
957
|
+
if (repoLevelReferenced.length > 0) {
|
|
958
|
+
const [secrets, variables] = await Promise.all([(0, github_1.listGithubSecretNames)(repo), (0, github_1.listGithubVariableNames)(repo)]);
|
|
959
|
+
const present = new Set([...secrets, ...variables].map(name => name.toUpperCase()));
|
|
960
|
+
const missing = repoLevelReferenced.filter(name => !present.has(name.toUpperCase()));
|
|
961
|
+
if (missing.length > 0) {
|
|
962
|
+
report.fail(`repo level: missing ${missing.length}/${repoLevelReferenced.length} referenced secrets: ${missing.join(", ")}`, "run: catladder project secrets-sync-github");
|
|
963
|
+
} else {
|
|
964
|
+
report.ok(`repo level: all ${repoLevelReferenced.length} referenced secrets/variables present`);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
exports.checkGithub = checkGithub;
|
|
969
|
+
|
|
970
|
+
/***/ }),
|
|
971
|
+
|
|
972
|
+
/***/ 47273:
|
|
973
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
974
|
+
|
|
975
|
+
"use strict";
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
Object.defineProperty(exports, "__esModule", ({
|
|
979
|
+
value: true
|
|
980
|
+
}));
|
|
981
|
+
exports.checkGitlab = void 0;
|
|
982
|
+
const pipeline_1 = __nccwpck_require__(60241);
|
|
983
|
+
const date_fns_1 = __nccwpck_require__(84022);
|
|
984
|
+
const gitlab_1 = __nccwpck_require__(4508);
|
|
985
|
+
const EXPIRY_WARNING_DAYS = 30;
|
|
986
|
+
/**
|
|
987
|
+
* the gitlab-side outputs of `project setup`: the semantic-release
|
|
988
|
+
* access token (+ its GL_TOKEN ci variable), the catladder topic and —
|
|
989
|
+
* for kubernetes deploys — the registry deploy token.
|
|
990
|
+
*
|
|
991
|
+
* Kubernetes namespace/rbac checks are deliberately skipped: they would
|
|
992
|
+
* require connecting kubectl to every cluster, which switches the local
|
|
993
|
+
* kubeconfig context — doctor must not change anything.
|
|
994
|
+
*/
|
|
995
|
+
const checkGitlab = async (instance, report, contexts) => {
|
|
996
|
+
report.section("gitlab project setup");
|
|
997
|
+
let projectId;
|
|
998
|
+
try {
|
|
999
|
+
projectId = (await (0, gitlab_1.getProjectInfo)(instance)).id;
|
|
1000
|
+
} catch (e) {
|
|
1001
|
+
report.warn(`could not reach the gitlab api — gitlab checks skipped (${e.message})`);
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
try {
|
|
1005
|
+
const tokens = await (0, gitlab_1.doGitlabRequestAllPages)(instance, `projects/${projectId}/access_tokens`);
|
|
1006
|
+
const activeTokens = tokens.filter(t => t.name === "semantic-release" && t.active === true);
|
|
1007
|
+
// the GL_TOKEN ci variable holds exactly one of them — judge by the
|
|
1008
|
+
// one that lives longest
|
|
1009
|
+
const token = activeTokens.sort((a, b) => String(b.expires_at).localeCompare(String(a.expires_at)))[0];
|
|
1010
|
+
if (activeTokens.length > 1) {
|
|
1011
|
+
report.warn(`${activeTokens.length} active 'semantic-release' tokens exist — duplicates from earlier setups, consider revoking the old ones`);
|
|
1012
|
+
}
|
|
1013
|
+
if (!token) {
|
|
1014
|
+
report.fail("no active 'semantic-release' project access token", "run: catladder project setup");
|
|
1015
|
+
} else {
|
|
1016
|
+
const daysLeft = (0, date_fns_1.differenceInCalendarDays)(new Date(token.expires_at), new Date());
|
|
1017
|
+
if (daysLeft < 0) {
|
|
1018
|
+
report.fail(`'semantic-release' token expired on ${token.expires_at}`, "run: catladder project setup (rotates the token)");
|
|
1019
|
+
} else if (daysLeft <= EXPIRY_WARNING_DAYS) {
|
|
1020
|
+
report.warn(`'semantic-release' token expires in ${daysLeft} days (${token.expires_at})`, "run: catladder project setup (rotates the token)");
|
|
1021
|
+
} else {
|
|
1022
|
+
report.ok(`'semantic-release' token active (expires ${token.expires_at})`);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
try {
|
|
1026
|
+
await (0, gitlab_1.doGitlabRequest)(instance, `projects/${projectId}/variables/GL_TOKEN`);
|
|
1027
|
+
report.ok("GL_TOKEN ci variable present");
|
|
1028
|
+
} catch (_a) {
|
|
1029
|
+
report.fail("GL_TOKEN ci variable missing", "run: catladder project setup");
|
|
1030
|
+
}
|
|
1031
|
+
const {
|
|
1032
|
+
topics
|
|
1033
|
+
} = await (0, gitlab_1.doGitlabRequest)(instance, `projects/${projectId}`);
|
|
1034
|
+
if (topics === null || topics === void 0 ? void 0 : topics.includes("catladder")) {
|
|
1035
|
+
report.ok("'catladder' project topic set");
|
|
1036
|
+
} else {
|
|
1037
|
+
report.fail("'catladder' project topic missing", "run: catladder project setup");
|
|
1038
|
+
}
|
|
1039
|
+
if (contexts.some(context => {
|
|
1040
|
+
var _a;
|
|
1041
|
+
return (0, pipeline_1.isOfDeployType)((_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config, "kubernetes");
|
|
1042
|
+
})) {
|
|
1043
|
+
const deployTokens = await (0, gitlab_1.doGitlabRequestAllPages)(instance, `projects/${projectId}/deploy_tokens`);
|
|
1044
|
+
if (deployTokens.find(t => t.name === "gitlab-deploy-token")) {
|
|
1045
|
+
report.ok("'gitlab-deploy-token' registry deploy token present");
|
|
1046
|
+
} else {
|
|
1047
|
+
report.fail("'gitlab-deploy-token' registry deploy token missing (kubernetes cannot pull images)", "run: catladder project setup");
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
} catch (e) {
|
|
1051
|
+
report.warn(`gitlab checks aborted (${e.message})`);
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
exports.checkGitlab = checkGitlab;
|
|
1055
|
+
|
|
1056
|
+
/***/ }),
|
|
1057
|
+
|
|
1058
|
+
/***/ 21141:
|
|
1059
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
1060
|
+
|
|
1061
|
+
"use strict";
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
Object.defineProperty(exports, "__esModule", ({
|
|
1065
|
+
value: true
|
|
1066
|
+
}));
|
|
1067
|
+
exports.checkStore = void 0;
|
|
1068
|
+
const pipeline_1 = __nccwpck_require__(60241);
|
|
1069
|
+
const getProjectConfig_1 = __nccwpck_require__(50001);
|
|
1070
|
+
const projects_1 = __nccwpck_require__(57414);
|
|
1071
|
+
/**
|
|
1072
|
+
* every google-cloudrun project in the config needs its project number
|
|
1073
|
+
* in the committed store — the deterministic cloud run urls
|
|
1074
|
+
* (<service>-<projectNumber>.<region>.run.app) are computed from it at
|
|
1075
|
+
* generation time. This check is fully offline.
|
|
1076
|
+
*/
|
|
1077
|
+
const checkStore = async report => {
|
|
1078
|
+
var _a, _b, _c;
|
|
1079
|
+
const config = await (0, getProjectConfig_1.getProjectConfig)();
|
|
1080
|
+
const projectIds = new Set();
|
|
1081
|
+
for (const {
|
|
1082
|
+
componentName,
|
|
1083
|
+
env
|
|
1084
|
+
} of await (0, getProjectConfig_1.getAllComponentsWithAllEnvsFlat)()) {
|
|
1085
|
+
const {
|
|
1086
|
+
deploy
|
|
1087
|
+
} = (0, pipeline_1.getEnvConfig)(config, componentName, env);
|
|
1088
|
+
if (deploy && deploy.type === "google-cloudrun") {
|
|
1089
|
+
projectIds.add(deploy.projectId);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
if (projectIds.size === 0) return;
|
|
1093
|
+
report.section("catladder store (.catladder-store/store.yml)");
|
|
1094
|
+
const root = (_a = await (0, projects_1.getGitRoot)()) !== null && _a !== void 0 ? _a : process.cwd();
|
|
1095
|
+
const store = (0, pipeline_1.readCatladderStore)(root);
|
|
1096
|
+
for (const projectId of projectIds) {
|
|
1097
|
+
const projectNumber = (_c = (_b = store.gcloudProjects) === null || _b === void 0 ? void 0 : _b[projectId]) === null || _c === void 0 ? void 0 : _c.projectNumber;
|
|
1098
|
+
if (projectNumber) {
|
|
1099
|
+
report.ok(`project number for ${projectId}: ${projectNumber}`);
|
|
1100
|
+
} else {
|
|
1101
|
+
report.fail(`missing project number for ${projectId}`, "run: catladder project setup");
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
exports.checkStore = checkStore;
|
|
1106
|
+
|
|
1107
|
+
/***/ }),
|
|
1108
|
+
|
|
1109
|
+
/***/ 96270:
|
|
1110
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
1111
|
+
|
|
1112
|
+
"use strict";
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
Object.defineProperty(exports, "__esModule", ({
|
|
1116
|
+
value: true
|
|
1117
|
+
}));
|
|
1118
|
+
exports.doctorProject = void 0;
|
|
1119
|
+
const pipeline_1 = __nccwpck_require__(60241);
|
|
1120
|
+
const getProjectConfig_1 = __nccwpck_require__(50001);
|
|
1121
|
+
const checkCloudRun_1 = __nccwpck_require__(65424);
|
|
1122
|
+
const checkGithub_1 = __nccwpck_require__(93265);
|
|
1123
|
+
const checkGitlab_1 = __nccwpck_require__(47273);
|
|
1124
|
+
const checkStore_1 = __nccwpck_require__(21141);
|
|
1125
|
+
const DoctorReport_1 = __nccwpck_require__(88089);
|
|
1126
|
+
/**
|
|
1127
|
+
* read-only health check: compares the state `project setup` (and
|
|
1128
|
+
* `project secrets-sync-github`) would provision for the CURRENT config
|
|
1129
|
+
* against what is actually there, and reports drift together with the
|
|
1130
|
+
* command that heals it. `project setup` is a point-in-time
|
|
1131
|
+
* provisioner, so config edits that change required infrastructure
|
|
1132
|
+
* (IAM roles, services, secrets) drift silently until something breaks
|
|
1133
|
+
* in ci — the doctor surfaces that before it does.
|
|
1134
|
+
*
|
|
1135
|
+
* The component filter only narrows the per-component checks; the
|
|
1136
|
+
* cheap project-global checks (store, gitlab, github) always run.
|
|
1137
|
+
*/
|
|
1138
|
+
const doctorProject = async (instance, onlyComponents) => {
|
|
1139
|
+
const config = await (0, getProjectConfig_1.getProjectConfig)();
|
|
1140
|
+
if (!config) {
|
|
1141
|
+
throw new Error("no catladder config found");
|
|
1142
|
+
}
|
|
1143
|
+
instance.log("🐱 🩺 checking project health — read-only, nothing will be changed");
|
|
1144
|
+
const report = new DoctorReport_1.DoctorReport(instance);
|
|
1145
|
+
await (0, checkStore_1.checkStore)(report);
|
|
1146
|
+
let contexts = [];
|
|
1147
|
+
try {
|
|
1148
|
+
contexts = await (0, getProjectConfig_1.getAllPipelineContexts)(onlyComponents);
|
|
1149
|
+
} catch (e) {
|
|
1150
|
+
report.section("pipeline contexts");
|
|
1151
|
+
report.fail(`could not create pipeline contexts: ${e.message}`, "run: catladder project setup");
|
|
1152
|
+
}
|
|
1153
|
+
if ((0, pipeline_1.getEnabledPipelineTypes)(config).includes("gitlab")) {
|
|
1154
|
+
await (0, checkGitlab_1.checkGitlab)(instance, report, contexts);
|
|
1155
|
+
}
|
|
1156
|
+
await (0, checkCloudRun_1.checkCloudRun)(report, contexts);
|
|
1157
|
+
await (0, checkGithub_1.checkGithub)(report, config);
|
|
1158
|
+
report.summarize();
|
|
1159
|
+
};
|
|
1160
|
+
exports.doctorProject = doctorProject;
|
|
1161
|
+
|
|
1162
|
+
/***/ }),
|
|
1163
|
+
|
|
590
1164
|
/***/ 22661:
|
|
591
1165
|
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
592
1166
|
|
|
@@ -746,7 +1320,10 @@ const setupAccessTokens = async instance => {
|
|
|
746
1320
|
const {
|
|
747
1321
|
id: projectId
|
|
748
1322
|
} = await (0, gitlab_1.getProjectInfo)(instance);
|
|
749
|
-
|
|
1323
|
+
// all pages: rotated tokens accumulate one (inactive) entry per
|
|
1324
|
+
// rotation, so the active one quickly moves past the first page —
|
|
1325
|
+
// missing it here created a brand-new duplicate token on every setup
|
|
1326
|
+
const projectTokens = await (0, gitlab_1.doGitlabRequestAllPages)(instance, `projects/${projectId}/access_tokens`);
|
|
750
1327
|
const token = projectTokens.find(t => t.name === TOKEN_NAME && t.active === true);
|
|
751
1328
|
const expires_at = (0, date_fns_1.format)((0, date_fns_1.add)(new Date(), {
|
|
752
1329
|
years: 1,
|
|
@@ -963,13 +1540,40 @@ exports.setupAgents = setupAgents;
|
|
|
963
1540
|
Object.defineProperty(exports, "__esModule", ({
|
|
964
1541
|
value: true
|
|
965
1542
|
}));
|
|
966
|
-
exports.setupCloudRun = void 0;
|
|
1543
|
+
exports.setupCloudRun = exports.getCloudRunDeployRoles = exports.getCloudRunRequiredServices = exports.CLOUD_RUN_DEPLOY_SA_NAME = void 0;
|
|
967
1544
|
const pipeline_1 = __nccwpck_require__(60241);
|
|
968
1545
|
const artifactsRegistry_1 = __nccwpck_require__(41624);
|
|
969
1546
|
const enableServices_1 = __nccwpck_require__(96761);
|
|
970
1547
|
const getProjectNumber_1 = __nccwpck_require__(68666);
|
|
971
1548
|
const serviceAccounts_1 = __nccwpck_require__(35611);
|
|
972
1549
|
const updateCatladderStore_1 = __nccwpck_require__(66487);
|
|
1550
|
+
exports.CLOUD_RUN_DEPLOY_SA_NAME = "cl-d";
|
|
1551
|
+
/**
|
|
1552
|
+
* the gcloud services the given deploy config requires.
|
|
1553
|
+
*
|
|
1554
|
+
* Shared with `project doctor`: setup enables them, doctor compares
|
|
1555
|
+
* them against what is actually enabled — config edits after setup
|
|
1556
|
+
* (e.g. adding `cloudSql`) would otherwise drift silently.
|
|
1557
|
+
*/
|
|
1558
|
+
const getCloudRunRequiredServices = config => ["run.googleapis.com", "artifactregistry.googleapis.com", "cloudscheduler.googleapis.com",
|
|
1559
|
+
// for scheduling jobs
|
|
1560
|
+
"cloudresourcemanager.googleapis.com",
|
|
1561
|
+
// only required to get google cloud project number
|
|
1562
|
+
...(config.cloudSql ? ["sqladmin.googleapis.com", "sql-component.googleapis.com"] : [])];
|
|
1563
|
+
exports.getCloudRunRequiredServices = getCloudRunRequiredServices;
|
|
1564
|
+
/**
|
|
1565
|
+
* the IAM roles the deploy service account needs for the given deploy
|
|
1566
|
+
* config. Shared with `project doctor` (same rationale as
|
|
1567
|
+
* {@link getCloudRunRequiredServices}) — the motivating case: a
|
|
1568
|
+
* component gets `cloudSql` after its setup ran, deploys keep working,
|
|
1569
|
+
* and only the review teardown hits the missing `cloudsql.admin`.
|
|
1570
|
+
*/
|
|
1571
|
+
const getCloudRunDeployRoles = config => [
|
|
1572
|
+
// TODO: run.admin is a bit much, would be better to follow https://stackoverflow.com/questions/55788714/deploying-to-cloud-run-with-a-custom-service-account-failed-with-iam-serviceacco
|
|
1573
|
+
"roles/artifactregistry.repoAdmin", "roles/run.admin", "roles/iam.serviceAccountUser", "roles/cloudscheduler.admin",
|
|
1574
|
+
// for scheduling
|
|
1575
|
+
...(config.cloudSql ? ["roles/cloudsql.admin"] : [])];
|
|
1576
|
+
exports.getCloudRunDeployRoles = getCloudRunDeployRoles;
|
|
973
1577
|
const setupCloudRun = async (instance, context) => {
|
|
974
1578
|
var _a, _b;
|
|
975
1579
|
if (!(0, pipeline_1.isOfDeployType)((_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config, "google-cloudrun")) {
|
|
@@ -978,11 +1582,7 @@ const setupCloudRun = async (instance, context) => {
|
|
|
978
1582
|
const config = (_b = context.deploy) === null || _b === void 0 ? void 0 : _b.config;
|
|
979
1583
|
// enable services
|
|
980
1584
|
instance.log("enable required services...");
|
|
981
|
-
await (0, enableServices_1.enableGCloudServices)(
|
|
982
|
-
// for scheduling jobs
|
|
983
|
-
"cloudresourcemanager.googleapis.com",
|
|
984
|
-
// only required to get google cloud project number
|
|
985
|
-
...(config.cloudSql ? ["sqladmin.googleapis.com", "sql-component.googleapis.com"] : [])], config);
|
|
1585
|
+
await (0, enableServices_1.enableGCloudServices)((0, exports.getCloudRunRequiredServices)(config), config);
|
|
986
1586
|
instance.log("upsert artifacts registry...");
|
|
987
1587
|
await (0, artifactsRegistry_1.upsertGcloudArtifactsRegistry)(config);
|
|
988
1588
|
instance.log("upsert deploy service account...");
|
|
@@ -990,11 +1590,8 @@ const setupCloudRun = async (instance, context) => {
|
|
|
990
1590
|
projectId: config.projectId,
|
|
991
1591
|
displayName: "Catladder gcloud deploy",
|
|
992
1592
|
description: "This service account deploys to google cloud",
|
|
993
|
-
name:
|
|
994
|
-
|
|
995
|
-
roles: ["roles/artifactregistry.repoAdmin", "roles/run.admin", "roles/iam.serviceAccountUser", "roles/cloudscheduler.admin",
|
|
996
|
-
// for scheduling
|
|
997
|
-
...(config.cloudSql ? ["roles/cloudsql.admin"] : [])]
|
|
1593
|
+
name: exports.CLOUD_RUN_DEPLOY_SA_NAME,
|
|
1594
|
+
roles: (0, exports.getCloudRunDeployRoles)(config)
|
|
998
1595
|
}, pipeline_1.GCLOUD_DEPLOY_CREDENTIALS_KEY);
|
|
999
1596
|
// the project number is needed at generation time to construct cloud
|
|
1000
1597
|
// run's deterministic urls (<service>-<projectNumber>.<region>.run.app)
|
|
@@ -2064,7 +2661,7 @@ exports.commandKubeStopPortforward = (0, defineCommand_1.defineCommand)({
|
|
|
2064
2661
|
Object.defineProperty(exports, "__esModule", ({
|
|
2065
2662
|
value: true
|
|
2066
2663
|
}));
|
|
2067
|
-
exports.commandStarwars = exports.commandDadjoke = exports.commandSecurityAuditCreate = exports.commandSecurityAuditEvaluate = exports.commandSecurityAuditCiJob = exports.commandRestoreDb = exports.commandMongoDestroyMember = exports.commandMongoPortForward = exports.commandMongoGetShell = exports.commandTriggerCronjobProject = exports.commandDeleteProject = exports.commandPauseProject = exports.commandDeletePods = exports.commandGetShell = exports.commandListPods = exports.commandNamespace = exports.commandSecurityEvaluate = exports.commandProjectRestoreDb = exports.commandCiJobLog = exports.commandCiJobOpen = exports.commandSetup = exports.commandGetMyTotalWorktime = exports.commandRenewToken = exports.commandTriggerCronjobGeneral = exports.commandOpenLogs = exports.commandSecretsSyncGithub = exports.commandSecretsClearBackups = exports.commandConfigSecrets = exports.commandCloudSqlProxy = exports.commandOpenGit = exports.commandOpenEnv = exports.commandPortForward = exports.commandEnvVars = exports.commandKubePortForward = exports.commandKubeGetShell = exports.commandKubeStopPortforward = exports.commandKubeListPods = exports.commandKubeListSecrets = exports.commandKubeListNamespaces = exports.commandKubeCurrentContext = void 0;
|
|
2664
|
+
exports.commandStarwars = exports.commandDadjoke = exports.commandSecurityAuditCreate = exports.commandSecurityAuditEvaluate = exports.commandSecurityAuditCiJob = exports.commandRestoreDb = exports.commandMongoDestroyMember = exports.commandMongoPortForward = exports.commandMongoGetShell = exports.commandTriggerCronjobProject = exports.commandDeleteProject = exports.commandPauseProject = exports.commandDeletePods = exports.commandGetShell = exports.commandListPods = exports.commandNamespace = exports.commandSecurityEvaluate = exports.commandProjectRestoreDb = exports.commandCiJobLog = exports.commandCiJobOpen = exports.commandDoctor = exports.commandSetup = exports.commandGetMyTotalWorktime = exports.commandRenewToken = exports.commandTriggerCronjobGeneral = exports.commandOpenLogs = exports.commandSecretsSyncGithub = exports.commandSecretsClearBackups = exports.commandConfigSecrets = exports.commandCloudSqlProxy = exports.commandOpenGit = exports.commandOpenEnv = exports.commandPortForward = exports.commandEnvVars = exports.commandKubePortForward = exports.commandKubeGetShell = exports.commandKubeStopPortforward = exports.commandKubeListPods = exports.commandKubeListSecrets = exports.commandKubeListNamespaces = exports.commandKubeCurrentContext = void 0;
|
|
2068
2665
|
// General commands
|
|
2069
2666
|
var commandKubeCurrentContext_1 = __nccwpck_require__(70243);
|
|
2070
2667
|
Object.defineProperty(exports, "commandKubeCurrentContext", ({
|
|
@@ -2207,6 +2804,13 @@ Object.defineProperty(exports, "commandSetup", ({
|
|
|
2207
2804
|
return commandSetup_1.commandSetup;
|
|
2208
2805
|
}
|
|
2209
2806
|
}));
|
|
2807
|
+
var commandDoctor_1 = __nccwpck_require__(80248);
|
|
2808
|
+
Object.defineProperty(exports, "commandDoctor", ({
|
|
2809
|
+
enumerable: true,
|
|
2810
|
+
get: function () {
|
|
2811
|
+
return commandDoctor_1.commandDoctor;
|
|
2812
|
+
}
|
|
2813
|
+
}));
|
|
2210
2814
|
var commandGitlabCi_1 = __nccwpck_require__(57076);
|
|
2211
2815
|
Object.defineProperty(exports, "commandCiJobOpen", ({
|
|
2212
2816
|
enumerable: true,
|
|
@@ -2750,6 +3354,38 @@ exports.commandConfigSecrets = (0, defineCommand_1.defineCommand)({
|
|
|
2750
3354
|
|
|
2751
3355
|
/***/ }),
|
|
2752
3356
|
|
|
3357
|
+
/***/ 80248:
|
|
3358
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
3359
|
+
|
|
3360
|
+
"use strict";
|
|
3361
|
+
|
|
3362
|
+
|
|
3363
|
+
Object.defineProperty(exports, "__esModule", ({
|
|
3364
|
+
value: true
|
|
3365
|
+
}));
|
|
3366
|
+
exports.commandDoctor = void 0;
|
|
3367
|
+
const doctor_1 = __nccwpck_require__(96270);
|
|
3368
|
+
const defineCommand_1 = __nccwpck_require__(39489);
|
|
3369
|
+
exports.commandDoctor = (0, defineCommand_1.defineCommand)({
|
|
3370
|
+
name: "project doctor",
|
|
3371
|
+
description: "Checks for drift between the config and the provisioned infrastructure (read-only): service accounts, IAM roles, gitlab tokens, github secrets, store entries.",
|
|
3372
|
+
group: "project",
|
|
3373
|
+
inputs: {
|
|
3374
|
+
component: {
|
|
3375
|
+
type: "string",
|
|
3376
|
+
message: "component name",
|
|
3377
|
+
positional: true,
|
|
3378
|
+
required: false
|
|
3379
|
+
}
|
|
3380
|
+
},
|
|
3381
|
+
execute: async ctx => {
|
|
3382
|
+
const component = await ctx.get("component");
|
|
3383
|
+
await (0, doctor_1.doctorProject)(ctx, component);
|
|
3384
|
+
}
|
|
3385
|
+
});
|
|
3386
|
+
|
|
3387
|
+
/***/ }),
|
|
3388
|
+
|
|
2753
3389
|
/***/ 47838:
|
|
2754
3390
|
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
2755
3391
|
|
|
@@ -4743,11 +5379,12 @@ exports.MissingInputError = MissingInputError;
|
|
|
4743
5379
|
Object.defineProperty(exports, "__esModule", ({
|
|
4744
5380
|
value: true
|
|
4745
5381
|
}));
|
|
4746
|
-
exports.upsertGcloudArtifactsRegistry = void 0;
|
|
5382
|
+
exports.upsertGcloudArtifactsRegistry = exports.CATLADDER_REGISTRY_NAME = void 0;
|
|
4747
5383
|
const child_process_promise_1 = __nccwpck_require__(51748);
|
|
5384
|
+
exports.CATLADDER_REGISTRY_NAME = "catladder-deploy";
|
|
4748
5385
|
const upsertGcloudArtifactsRegistry = async config => {
|
|
4749
5386
|
try {
|
|
4750
|
-
await (0, child_process_promise_1.exec)(`gcloud artifacts repositories create
|
|
5387
|
+
await (0, child_process_promise_1.exec)(`gcloud artifacts repositories create ${exports.CATLADDER_REGISTRY_NAME} --project="${config.projectId}" --repository-format=docker --location=${config.region}`);
|
|
4751
5388
|
} catch (e) {
|
|
4752
5389
|
// probably already exists
|
|
4753
5390
|
//
|
|
@@ -5006,7 +5643,7 @@ exports.openGoogleCloudDashboard = openGoogleCloudDashboard;
|
|
|
5006
5643
|
|
|
5007
5644
|
/***/ }),
|
|
5008
5645
|
|
|
5009
|
-
/***/
|
|
5646
|
+
/***/ 83798:
|
|
5010
5647
|
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
5011
5648
|
|
|
5012
5649
|
"use strict";
|
|
@@ -5015,26 +5652,21 @@ exports.openGoogleCloudDashboard = openGoogleCloudDashboard;
|
|
|
5015
5652
|
Object.defineProperty(exports, "__esModule", ({
|
|
5016
5653
|
value: true
|
|
5017
5654
|
}));
|
|
5018
|
-
exports.
|
|
5019
|
-
const child_process_promise_1 = __nccwpck_require__(51748);
|
|
5655
|
+
exports.getGcloudServiceAccountNames = void 0;
|
|
5020
5656
|
const crypto_1 = __nccwpck_require__(76982);
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
const upsertGcloudServiceAccount = async (context, account) => {
|
|
5657
|
+
/**
|
|
5658
|
+
* the deterministic gcloud service account naming used by
|
|
5659
|
+
* `project setup`. Extracted so `project doctor` can compute the
|
|
5660
|
+
* expected identifier of an account without creating anything.
|
|
5661
|
+
*
|
|
5662
|
+
* gcloud limits account ids to 30 chars: the customer/app middle part
|
|
5663
|
+
* (and, if even that is not enough, the env/component suffix) is
|
|
5664
|
+
* replaced by a hash when the full name would not fit.
|
|
5665
|
+
*/
|
|
5666
|
+
const getGcloudServiceAccountNames = (context, account) => {
|
|
5032
5667
|
const {
|
|
5033
5668
|
projectId,
|
|
5034
|
-
name
|
|
5035
|
-
displayName,
|
|
5036
|
-
roles,
|
|
5037
|
-
description
|
|
5669
|
+
name
|
|
5038
5670
|
} = account;
|
|
5039
5671
|
// name has limit of 30
|
|
5040
5672
|
const namePrefix = `${name}`;
|
|
@@ -5055,8 +5687,54 @@ const upsertGcloudServiceAccount = async (context, account) => {
|
|
|
5055
5687
|
nameSuffix = nameSuffixRaw;
|
|
5056
5688
|
}
|
|
5057
5689
|
const fullName = `${namePrefix}-${nameMiddle}-${nameSuffix}`;
|
|
5058
|
-
const fullDisplayName = `${context.fullConfig.customerName}-${context.fullConfig.appName} ${context.env}:${context.name} | ${displayName}`;
|
|
5059
5690
|
const fullIdentifier = `${fullName}@${projectId}.iam.gserviceaccount.com`;
|
|
5691
|
+
return {
|
|
5692
|
+
fullName,
|
|
5693
|
+
fullIdentifier
|
|
5694
|
+
};
|
|
5695
|
+
};
|
|
5696
|
+
exports.getGcloudServiceAccountNames = getGcloudServiceAccountNames;
|
|
5697
|
+
const hashIfNessecary = (str, maxLength) => str.length > maxLength ? hash(str, maxLength) : str;
|
|
5698
|
+
const hash = (str, length) => {
|
|
5699
|
+
return (0, crypto_1.createHmac)("sha256", str).digest("hex").substring(0, length);
|
|
5700
|
+
};
|
|
5701
|
+
|
|
5702
|
+
/***/ }),
|
|
5703
|
+
|
|
5704
|
+
/***/ 35611:
|
|
5705
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
5706
|
+
|
|
5707
|
+
"use strict";
|
|
5708
|
+
|
|
5709
|
+
|
|
5710
|
+
Object.defineProperty(exports, "__esModule", ({
|
|
5711
|
+
value: true
|
|
5712
|
+
}));
|
|
5713
|
+
exports.upsertGcloudServiceAccountAndSaveSecret = exports.accountExists = void 0;
|
|
5714
|
+
const child_process_promise_1 = __nccwpck_require__(51748);
|
|
5715
|
+
const gitlab_1 = __nccwpck_require__(4508);
|
|
5716
|
+
const serviceAccountNames_1 = __nccwpck_require__(83798);
|
|
5717
|
+
const accountExists = async fullIdentifier => {
|
|
5718
|
+
try {
|
|
5719
|
+
await (0, child_process_promise_1.exec)(`gcloud iam service-accounts describe ${fullIdentifier}`);
|
|
5720
|
+
return true;
|
|
5721
|
+
} catch (_a) {
|
|
5722
|
+
return false;
|
|
5723
|
+
}
|
|
5724
|
+
};
|
|
5725
|
+
exports.accountExists = accountExists;
|
|
5726
|
+
const upsertGcloudServiceAccount = async (context, account) => {
|
|
5727
|
+
const {
|
|
5728
|
+
projectId,
|
|
5729
|
+
displayName,
|
|
5730
|
+
roles,
|
|
5731
|
+
description
|
|
5732
|
+
} = account;
|
|
5733
|
+
const {
|
|
5734
|
+
fullName,
|
|
5735
|
+
fullIdentifier
|
|
5736
|
+
} = (0, serviceAccountNames_1.getGcloudServiceAccountNames)(context, account);
|
|
5737
|
+
const fullDisplayName = `${context.fullConfig.customerName}-${context.fullConfig.appName} ${context.env}:${context.name} | ${displayName}`;
|
|
5060
5738
|
const existing = await (0, exports.accountExists)(fullIdentifier);
|
|
5061
5739
|
if (!existing) {
|
|
5062
5740
|
await (0, child_process_promise_1.exec)(`gcloud iam service-accounts create ${fullName} --display-name="${fullDisplayName}" --project="${projectId}" --description="${description}"`);
|
|
@@ -5084,10 +5762,6 @@ const upsertGcloudServiceAccountAndSaveSecret = async (instance, context, accoun
|
|
|
5084
5762
|
instance.log("done!");
|
|
5085
5763
|
};
|
|
5086
5764
|
exports.upsertGcloudServiceAccountAndSaveSecret = upsertGcloudServiceAccountAndSaveSecret;
|
|
5087
|
-
const hashIfNessecary = (str, maxLength) => str.length > maxLength ? hash(str, maxLength) : str;
|
|
5088
|
-
const hash = (str, length) => {
|
|
5089
|
-
return (0, crypto_1.createHmac)("sha256", str).digest("hex").substring(0, length);
|
|
5090
|
-
};
|
|
5091
5765
|
|
|
5092
5766
|
/***/ }),
|
|
5093
5767
|
|
|
@@ -5665,6 +6339,40 @@ exports.connectToCluster = connectToCluster;
|
|
|
5665
6339
|
|
|
5666
6340
|
/***/ }),
|
|
5667
6341
|
|
|
6342
|
+
/***/ 5092:
|
|
6343
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
6344
|
+
|
|
6345
|
+
"use strict";
|
|
6346
|
+
|
|
6347
|
+
|
|
6348
|
+
Object.defineProperty(exports, "__esModule", ({
|
|
6349
|
+
value: true
|
|
6350
|
+
}));
|
|
6351
|
+
exports.mapWithConcurrency = void 0;
|
|
6352
|
+
/**
|
|
6353
|
+
* maps over items with at most `limit` calls in flight, preserving the
|
|
6354
|
+
* input order in the result (unlike reporting-as-you-go, this lets
|
|
6355
|
+
* callers run slow per-item lookups in parallel and still log the
|
|
6356
|
+
* outcomes in a stable order)
|
|
6357
|
+
*/
|
|
6358
|
+
const mapWithConcurrency = async (items, limit, fn) => {
|
|
6359
|
+
const results = new Array(items.length);
|
|
6360
|
+
let next = 0;
|
|
6361
|
+
const workers = Array.from({
|
|
6362
|
+
length: Math.max(1, Math.min(limit, items.length))
|
|
6363
|
+
}, async () => {
|
|
6364
|
+
while (next < items.length) {
|
|
6365
|
+
const index = next++;
|
|
6366
|
+
results[index] = await fn(items[index], index);
|
|
6367
|
+
}
|
|
6368
|
+
});
|
|
6369
|
+
await Promise.all(workers);
|
|
6370
|
+
return results;
|
|
6371
|
+
};
|
|
6372
|
+
exports.mapWithConcurrency = mapWithConcurrency;
|
|
6373
|
+
|
|
6374
|
+
/***/ }),
|
|
6375
|
+
|
|
5668
6376
|
/***/ 82805:
|
|
5669
6377
|
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
|
5670
6378
|
|
|
@@ -5778,7 +6486,7 @@ exports["default"] = async () => {
|
|
|
5778
6486
|
Object.defineProperty(exports, "__esModule", ({
|
|
5779
6487
|
value: true
|
|
5780
6488
|
}));
|
|
5781
|
-
exports.ensureGithubEnvironment = exports.setGithubVariable = exports.setGithubSecret = exports.getGithubRepoFromRemote = exports.isGhAuthenticated = void 0;
|
|
6489
|
+
exports.ensureGithubEnvironment = exports.setGithubVariable = exports.setGithubSecret = exports.listGithubVariableNames = exports.listGithubSecretNames = exports.listGithubEnvironments = exports.getGithubRepoFromRemote = exports.isGhAuthenticated = void 0;
|
|
5782
6490
|
const child_process_1 = __nccwpck_require__(35317);
|
|
5783
6491
|
const util_1 = __nccwpck_require__(39023);
|
|
5784
6492
|
const execFile = (0, util_1.promisify)(child_process_1.execFile);
|
|
@@ -5811,6 +6519,31 @@ const getGithubRepoFromRemote = async remoteName => {
|
|
|
5811
6519
|
}
|
|
5812
6520
|
};
|
|
5813
6521
|
exports.getGithubRepoFromRemote = getGithubRepoFromRemote;
|
|
6522
|
+
/**
|
|
6523
|
+
* runs a paginated `gh api` query and returns the jq-extracted lines
|
|
6524
|
+
* (one value per line, e.g. names)
|
|
6525
|
+
*/
|
|
6526
|
+
const ghApiLines = async (path, jq) => {
|
|
6527
|
+
const {
|
|
6528
|
+
stdout
|
|
6529
|
+
} = await execFile("gh", ["api", "--paginate", path, "--jq", jq], {
|
|
6530
|
+
maxBuffer: 10 * 1024 * 1024
|
|
6531
|
+
});
|
|
6532
|
+
return stdout.split("\n").map(line => line.trim()).filter(Boolean);
|
|
6533
|
+
};
|
|
6534
|
+
/** names of all deployment environments of the repository */
|
|
6535
|
+
const listGithubEnvironments = repo => ghApiLines(`repos/${repo}/environments?per_page=100`, ".environments[].name");
|
|
6536
|
+
exports.listGithubEnvironments = listGithubEnvironments;
|
|
6537
|
+
/**
|
|
6538
|
+
* names of the actions secrets — of an environment, or of the repo
|
|
6539
|
+
* level when no environment is given. Github secrets are write-only:
|
|
6540
|
+
* names are all that can ever be inspected.
|
|
6541
|
+
*/
|
|
6542
|
+
const listGithubSecretNames = (repo, environment) => ghApiLines(environment ? `repos/${repo}/environments/${encodeURIComponent(environment)}/secrets?per_page=100` : `repos/${repo}/actions/secrets?per_page=100`, ".secrets[].name");
|
|
6543
|
+
exports.listGithubSecretNames = listGithubSecretNames;
|
|
6544
|
+
/** names of the actions variables (environment or repo level) */
|
|
6545
|
+
const listGithubVariableNames = (repo, environment) => ghApiLines(environment ? `repos/${repo}/environments/${encodeURIComponent(environment)}/variables?per_page=100` : `repos/${repo}/actions/variables?per_page=100`, ".variables[].name");
|
|
6546
|
+
exports.listGithubVariableNames = listGithubVariableNames;
|
|
5814
6547
|
const ghSetViaStdin = (kind, repo, name, value, environment) => new Promise((resolve, reject) => {
|
|
5815
6548
|
var _a, _b;
|
|
5816
6549
|
const child = (0, child_process_1.spawn)("gh", [kind, "set", name, "--repo", repo, ...(environment ? ["--env", environment] : [])], {
|
|
@@ -5866,7 +6599,7 @@ var __importDefault = this && this.__importDefault || function (mod) {
|
|
|
5866
6599
|
Object.defineProperty(exports, "__esModule", ({
|
|
5867
6600
|
value: true
|
|
5868
6601
|
}));
|
|
5869
|
-
exports.upsertAllVariables = exports.clearBackups = exports.getVariableValueByRawName = exports.getAllVariables = exports.getProjectInfo = exports.doGitlabRequest = exports.getGitlabToken = exports.setupGitlabToken = exports.hasGitlabToken = void 0;
|
|
6602
|
+
exports.upsertAllVariables = exports.clearBackups = exports.getVariableValueByRawName = exports.getAllVariables = exports.getProjectInfo = exports.doGitlabRequestAllPages = exports.doGitlabRequest = exports.getGitlabToken = exports.setupGitlabToken = exports.hasGitlabToken = void 0;
|
|
5870
6603
|
const pipeline_1 = __nccwpck_require__(60241);
|
|
5871
6604
|
const lodash_es_1 = __nccwpck_require__(64284);
|
|
5872
6605
|
const memoizee_1 = __importDefault(__nccwpck_require__(57009));
|
|
@@ -5937,6 +6670,24 @@ const doGitlabRequest = async (io, path, data = undefined, method = "GET") => {
|
|
|
5937
6670
|
throw new Error(`Could not send request to gitlab api ${path}: ${result.status} "${result.statusText}".\nResponse: ${JSON.stringify(await result.json(), null, 2)}`);
|
|
5938
6671
|
};
|
|
5939
6672
|
exports.doGitlabRequest = doGitlabRequest;
|
|
6673
|
+
/**
|
|
6674
|
+
* GET a list endpoint across ALL pages (gitlab returns 20 items per
|
|
6675
|
+
* page by default — long-lived projects easily exceed that, e.g.
|
|
6676
|
+
* rotated access tokens accumulate one entry per rotation)
|
|
6677
|
+
*/
|
|
6678
|
+
const doGitlabRequestAllPages = async (io, path) => {
|
|
6679
|
+
const PER_PAGE = 100;
|
|
6680
|
+
const separator = path.includes("?") ? "&" : "?";
|
|
6681
|
+
const all = [];
|
|
6682
|
+
for (let page = 1;; page++) {
|
|
6683
|
+
const items = await (0, exports.doGitlabRequest)(io, `${path}${separator}per_page=${PER_PAGE}&page=${page}`);
|
|
6684
|
+
if (!(items === null || items === void 0 ? void 0 : items.length)) break;
|
|
6685
|
+
all.push(...items);
|
|
6686
|
+
if (items.length < PER_PAGE) break;
|
|
6687
|
+
}
|
|
6688
|
+
return all;
|
|
6689
|
+
};
|
|
6690
|
+
exports.doGitlabRequestAllPages = doGitlabRequestAllPages;
|
|
5940
6691
|
const getProjectInfo = async io => {
|
|
5941
6692
|
const {
|
|
5942
6693
|
gitRemotePath
|
|
@@ -7205,13 +7956,25 @@ const resolveGithubNeeds = (context, job, allJobs, extraNeeds = []) => {
|
|
|
7205
7956
|
return [...new Map(resolved.map(n => [n.id, n])).values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
7206
7957
|
};
|
|
7207
7958
|
exports.resolveGithubNeeds = resolveGithubNeeds;
|
|
7959
|
+
/** step id of a referenceable cache restore step (see `cacheId`) */
|
|
7960
|
+
const cacheStepId = cacheId => `cache-${cacheId.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
|
|
7208
7961
|
const createCacheSteps = (context, job) => {
|
|
7209
7962
|
var _a, _b;
|
|
7210
7963
|
if (!((_a = job.caches) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
7211
7964
|
return [];
|
|
7212
7965
|
}
|
|
7213
|
-
const lowered = (_b = (0, createJobCache_1.createJobCacheFromCacheConfigs)(context, job.caches)) !== null && _b !== void 0 ? _b : [];
|
|
7214
|
-
|
|
7966
|
+
const lowered = (0, utils_1.ensureArray)((_b = (0, createJobCache_1.createJobCacheFromCacheConfigs)(context, job.caches)) !== null && _b !== void 0 ? _b : []);
|
|
7967
|
+
// caches marked redundantOnExactHitOf skip their (expensive) restore
|
|
7968
|
+
// when the referenced sibling cache hit its exact key — e.g. the yarn
|
|
7969
|
+
// zip cache is never read when node_modules restored for the same
|
|
7970
|
+
// lockfile. The referenced cache's step must come first (its
|
|
7971
|
+
// `cache-hit` output drives the `if`), so providers are emitted
|
|
7972
|
+
// before dependents; the reorder is github-only and does not affect
|
|
7973
|
+
// the gitlab cache list.
|
|
7974
|
+
const referencedIds = new Set(lowered.map(cache => cache.redundantOnExactHitOf).filter(id => !!id));
|
|
7975
|
+
const providedIds = new Set(lowered.map(cache => cache.cacheId).filter(id => !!id));
|
|
7976
|
+
const ordered = [...lowered.filter(cache => !cache.redundantOnExactHitOf), ...lowered.filter(cache => cache.redundantOnExactHitOf)];
|
|
7977
|
+
return ordered.map(cache => {
|
|
7215
7978
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
7216
7979
|
const paths = (0, utils_1.ensureArray)((_a = cache.paths) !== null && _a !== void 0 ? _a : []);
|
|
7217
7980
|
const key = cache.key;
|
|
@@ -7236,8 +7999,18 @@ const createCacheSteps = (context, job) => {
|
|
|
7236
7999
|
restoreKeyPrefix: `${(_e = key.prefix) !== null && _e !== void 0 ? _e : "files"}-`,
|
|
7237
8000
|
label: (_f = key.prefix) !== null && _f !== void 0 ? _f : ((_g = key.files) !== null && _g !== void 0 ? _g : []).join(",")
|
|
7238
8001
|
};
|
|
8002
|
+
// the sibling-hit condition only applies when the referenced cache
|
|
8003
|
+
// is actually part of this job — otherwise restore unconditionally
|
|
8004
|
+
const skipCondition = cache.redundantOnExactHitOf && providedIds.has(cache.redundantOnExactHitOf) ? `\${{ steps.${cacheStepId(cache.redundantOnExactHitOf)}.outputs.cache-hit != 'true' }}` : undefined;
|
|
7239
8005
|
return {
|
|
7240
8006
|
name: `Cache ${label}`,
|
|
8007
|
+
// referenced caches carry an id so dependents can read cache-hit
|
|
8008
|
+
...(cache.cacheId && referencedIds.has(cache.cacheId) ? {
|
|
8009
|
+
id: cacheStepId(cache.cacheId)
|
|
8010
|
+
} : {}),
|
|
8011
|
+
...(skipCondition ? {
|
|
8012
|
+
if: skipCondition
|
|
8013
|
+
} : {}),
|
|
7241
8014
|
// pull-only caches are restored but never saved
|
|
7242
8015
|
uses: cache.policy === "pull" ? "actions/cache/restore@v4" : "actions/cache@v4",
|
|
7243
8016
|
with: {
|
|
@@ -8012,15 +8785,16 @@ const makeGitlabJob = (context, job, allJobs, images, baseRules) => {
|
|
|
8012
8785
|
const when = jobWhen !== null && jobWhen !== void 0 ? jobWhen : gate ? gate === "manual" ? "manual" : "on_success" : undefined;
|
|
8013
8786
|
const allow_failure = jobAllowFailure !== null && jobAllowFailure !== void 0 ? jobAllowFailure : gate ? gate === "manual" : undefined;
|
|
8014
8787
|
// the neutral `caches` declarations resolve to gitlab cache configs;
|
|
8015
|
-
// an explicitly set gitlab `cache` takes precedence.
|
|
8016
|
-
//
|
|
8017
|
-
|
|
8788
|
+
// an explicitly set gitlab `cache` takes precedence. keyFiles /
|
|
8789
|
+
// cacheId / redundantOnExactHitOf are github-only hints (content
|
|
8790
|
+
// keys, conditional restore) and must not leak into the gitlab yaml
|
|
8791
|
+
const stripGithubCacheHints = ({
|
|
8018
8792
|
keyFiles: _keyFiles,
|
|
8793
|
+
cacheId: _cacheId,
|
|
8794
|
+
redundantOnExactHitOf: _redundantOnExactHitOf,
|
|
8019
8795
|
...cacheRest
|
|
8020
|
-
}) => cacheRest
|
|
8021
|
-
|
|
8022
|
-
...cacheRest
|
|
8023
|
-
}) => cacheRest)(resolved);
|
|
8796
|
+
}) => cacheRest;
|
|
8797
|
+
const stripKeyFiles = resolved => Array.isArray(resolved) ? resolved.map(stripGithubCacheHints) : stripGithubCacheHints(resolved);
|
|
8024
8798
|
const resolvedCaches = caches && context.type !== "agent" ? (0, createJobCache_1.createJobCacheFromCacheConfigs)(context, caches) : undefined;
|
|
8025
8799
|
const cache = jobCache !== null && jobCache !== void 0 ? jobCache : resolvedCaches && stripKeyFiles(resolvedCaches);
|
|
8026
8800
|
const stage = envMode === "stagePerEnv" && context.type !== "agent" ? `${job.stage} ${context.env}` : job.stage;
|
|
@@ -9314,6 +10088,8 @@ const createJobCacheFromCacheConfigs = (context, caches) => {
|
|
|
9314
10088
|
pathMode,
|
|
9315
10089
|
buildDir,
|
|
9316
10090
|
keyFiles,
|
|
10091
|
+
cacheId,
|
|
10092
|
+
redundantOnExactHitOf,
|
|
9317
10093
|
...rest
|
|
9318
10094
|
}) => {
|
|
9319
10095
|
var _a;
|
|
@@ -9330,6 +10106,12 @@ const createJobCacheFromCacheConfigs = (context, caches) => {
|
|
|
9330
10106
|
...((keyFiles === null || keyFiles === void 0 ? void 0 : keyFiles.length) ? {
|
|
9331
10107
|
keyFiles: keyFiles.map(f => pathMode === "absolute" ? f : (0, path_1.join)(baseDir, f))
|
|
9332
10108
|
} : {}),
|
|
10109
|
+
...(cacheId ? {
|
|
10110
|
+
cacheId
|
|
10111
|
+
} : {}),
|
|
10112
|
+
...(redundantOnExactHitOf ? {
|
|
10113
|
+
redundantOnExactHitOf
|
|
10114
|
+
} : {}),
|
|
9333
10115
|
...rest
|
|
9334
10116
|
};
|
|
9335
10117
|
});
|
|
@@ -9873,6 +10655,12 @@ exports.getNodeCache = exports.getNodeModulesCache = exports.getYarnCache = void
|
|
|
9873
10655
|
const lodash_es_1 = __nccwpck_require__(64284);
|
|
9874
10656
|
const path_1 = __nccwpck_require__(16928);
|
|
9875
10657
|
const slugify_1 = __importDefault(__nccwpck_require__(54206));
|
|
10658
|
+
/**
|
|
10659
|
+
* stable reference between the two node caches: the yarn (zip) cache
|
|
10660
|
+
* declares itself redundant when the node_modules cache scored an
|
|
10661
|
+
* exact-lockfile hit (github skips the ~0.6GB download on warm runs)
|
|
10662
|
+
*/
|
|
10663
|
+
const NODE_MODULES_CACHE_ID = "node-modules";
|
|
9876
10664
|
const getYarnCache = async (context, policy = "pull-push") => {
|
|
9877
10665
|
const packageManagerInfo = await context.packageManagerInfo;
|
|
9878
10666
|
const componentIsInWorkspace = context.type === "component" && "componentIsInWorkspace" in packageManagerInfo && packageManagerInfo.componentIsInWorkspace;
|
|
@@ -9884,7 +10672,12 @@ const getYarnCache = async (context, policy = "pull-push") => {
|
|
|
9884
10672
|
paths: [".yarn"],
|
|
9885
10673
|
// content-key for immutable-cache backends (github); the lockfile
|
|
9886
10674
|
// decides whether the cached content could have changed
|
|
9887
|
-
keyFiles: ["yarn.lock"]
|
|
10675
|
+
keyFiles: ["yarn.lock"],
|
|
10676
|
+
// the .yarn zips (~hundreds of MB) are only read when yarn has to
|
|
10677
|
+
// (re)install packages. When the node_modules cache hit for the
|
|
10678
|
+
// same lockfile, install verifies via install-state.gz and never
|
|
10679
|
+
// opens a zip — so github skips downloading this cache entirely
|
|
10680
|
+
redundantOnExactHitOf: NODE_MODULES_CACHE_ID
|
|
9888
10681
|
}];
|
|
9889
10682
|
};
|
|
9890
10683
|
exports.getYarnCache = getYarnCache;
|
|
@@ -9908,6 +10701,9 @@ const getNodeModulesCache = async (context, policy = "pull-push") => {
|
|
|
9908
10701
|
// decides whether the cached content could have changed (absolute
|
|
9909
10702
|
// pathMode: resolve the standalone component's lockfile explicitly)
|
|
9910
10703
|
keyFiles: [componentIsInWorkspace ? "yarn.lock" : (0, path_1.join)(context.build.dir, "yarn.lock")],
|
|
10704
|
+
// referenced by the yarn cache: an exact hit here makes the yarn
|
|
10705
|
+
// zips unnecessary (see redundantOnExactHitOf on getYarnCache)
|
|
10706
|
+
cacheId: NODE_MODULES_CACHE_ID,
|
|
9911
10707
|
policy,
|
|
9912
10708
|
paths: [...(componentIsInWorkspace ? (0, lodash_es_1.uniq)(["node_modules", ...((_a = workspaces.map(w => (0, path_1.join)(w.location, "node_modules"))) !== null && _a !== void 0 ? _a : []), ...(!isClassic ? [".yarn/install-state.gz", ...((_b = workspaces.map(w => (0, path_1.join)(w.location, ".yarn/install-state.gz"))) !== null && _b !== void 0 ? _b : [])] : [])]) : [(0, path_1.join)(context.build.dir, "node_modules"), ...(!isClassic ? [(0, path_1.join)(context.build.dir, ".yarn/install-state.gz")] : [])])]
|
|
9913
10709
|
}];
|
|
@@ -10595,7 +11391,7 @@ Object.defineProperty(exports, "__esModule", ({
|
|
|
10595
11391
|
value: true
|
|
10596
11392
|
}));
|
|
10597
11393
|
exports.DOCKER_REGISTRY = exports.PIPELINE_IMAGE_TAG = void 0;
|
|
10598
|
-
exports.PIPELINE_IMAGE_TAG = "next-
|
|
11394
|
+
exports.PIPELINE_IMAGE_TAG = "next-5dc1f907" || 0;
|
|
10599
11395
|
exports.DOCKER_REGISTRY = "git.panter.ch:5001/catladder/catladder" || 0;
|
|
10600
11396
|
|
|
10601
11397
|
/***/ }),
|