@catladder/cli 0.0.0-main-a6b45abe → 0.0.0-main-fd9c5f51
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/apps/cli/src/apps/cli/commands/project/doctor/checkGithub.js +48 -9
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithub.js.map +1 -1
- package/dist/apps/cli/src/apps/cli/commands/project/githubMergeGating.d.ts +83 -0
- package/dist/apps/cli/src/apps/cli/commands/project/githubMergeGating.js +80 -0
- package/dist/apps/cli/src/apps/cli/commands/project/githubMergeGating.js.map +1 -0
- package/dist/apps/cli/src/apps/cli/commands/project/setup/index.js +4 -3
- package/dist/apps/cli/src/apps/cli/commands/project/setup/index.js.map +1 -1
- package/dist/apps/cli/src/apps/cli/commands/project/setup/setupGithub.d.ts +12 -7
- package/dist/apps/cli/src/apps/cli/commands/project/setup/setupGithub.js +138 -31
- package/dist/apps/cli/src/apps/cli/commands/project/setup/setupGithub.js.map +1 -1
- package/dist/apps/cli/src/release/changesetsReleaseJob.js +64 -4
- package/dist/apps/cli/src/release/changesetsReleaseJob.js.map +1 -1
- package/dist/apps/cli/src/release/releaseGit.d.ts +2 -0
- package/dist/apps/cli/src/release/releaseGit.js +9 -1
- package/dist/apps/cli/src/release/releaseGit.js.map +1 -1
- package/dist/apps/cli/src/utils/github.d.ts +1 -1
- package/dist/apps/cli/src/utils/github.js.map +1 -1
- package/dist/bundles/catci/index.js +5 -5
- package/dist/bundles/catenv/index.js +5 -0
- package/dist/bundles/cli/index.js +282 -43
- package/dist/bundles/runner-images/changesets/Dockerfile +2 -1
- package/dist/bundles/skills/catladder-migrate-ci-backend/SKILL.md +15 -7
- package/dist/packages/pipeline/src/backends/github/githubReleaseJobs.js +5 -0
- package/dist/packages/pipeline/src/backends/github/githubReleaseJobs.js.map +1 -1
- package/dist/runner-images/changesets/Dockerfile +2 -1
- package/dist/skills/catladder-migrate-ci-backend/SKILL.md +15 -7
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/apps/cli/commands/project/__tests__/githubMergeGating.test.ts +123 -0
- package/src/apps/cli/commands/project/doctor/checkGithub.ts +74 -8
- package/src/apps/cli/commands/project/githubMergeGating.ts +101 -0
- package/src/apps/cli/commands/project/setup/index.ts +4 -3
- package/src/apps/cli/commands/project/setup/setupGithub.ts +190 -27
- package/src/release/changesetsReleaseJob.ts +81 -6
- package/src/release/releaseGit.ts +11 -0
- package/src/utils/github.ts +1 -1
|
@@ -3581,6 +3581,11 @@ const createReleaseJob = (config, images, options = {}) => {
|
|
|
3581
3581
|
...(allNeeds.length > 0 ? { needs: allNeeds } : {}),
|
|
3582
3582
|
env: {
|
|
3583
3583
|
GITHUB_TOKEN: "${{ github.token }}",
|
|
3584
|
+
// the release deploy key (provisioned by `project setup`): the
|
|
3585
|
+
// release push must bypass the merge-gating ruleset, which only
|
|
3586
|
+
// a deploy key can — empty when not provisioned, then the job
|
|
3587
|
+
// pushes with the workflow token (fine without gating)
|
|
3588
|
+
CATLADDER_RELEASE_KEY: "${{ secrets.CATLADDER_RELEASE_KEY }}",
|
|
3584
3589
|
...options.extraEnv,
|
|
3585
3590
|
},
|
|
3586
3591
|
steps: [
|
|
@@ -619,6 +619,7 @@ const pipeline_1 = __nccwpck_require__(53695);
|
|
|
619
619
|
const commandSecretsSyncGithub_1 = __nccwpck_require__(27565);
|
|
620
620
|
const concurrency_1 = __nccwpck_require__(59723);
|
|
621
621
|
const github_1 = __nccwpck_require__(41353);
|
|
622
|
+
const githubMergeGating_1 = __nccwpck_require__(14966);
|
|
622
623
|
/** marker for repo-level (environment-less) targets, see collectSecretTargets */
|
|
623
624
|
const REPO_LEVEL = "";
|
|
624
625
|
/**
|
|
@@ -722,10 +723,13 @@ const checkGithub = async (report, config) => {
|
|
|
722
723
|
exports.checkGithub = checkGithub;
|
|
723
724
|
/**
|
|
724
725
|
* verifies the merge gating `project setup` configures: auto-merge
|
|
725
|
-
* enabled and the
|
|
726
|
-
*
|
|
727
|
-
*
|
|
728
|
-
*
|
|
726
|
+
* enabled and the `catladder merge gating` ruleset requiring the
|
|
727
|
+
* generated `catladder ✅` aggregate check on the default branch (with
|
|
728
|
+
* the bypass that lets the release job push). Without the check a PR
|
|
729
|
+
* is mergeable while its checks are still running — gitlab ships this
|
|
730
|
+
* behavior built-in, github silently does not. The check required via
|
|
731
|
+
* classic branch protection instead is flagged: that setup rejects the
|
|
732
|
+
* release job's own push (GH006).
|
|
729
733
|
*/
|
|
730
734
|
const checkGithubMergeGating = async (report, repo) => {
|
|
731
735
|
var _a, _b;
|
|
@@ -744,17 +748,52 @@ const checkGithubMergeGating = async (report, repo) => {
|
|
|
744
748
|
report.fail("auto-merge disabled — the merge-when-pipeline-succeeds button cannot appear", "run: catladder project setup");
|
|
745
749
|
}
|
|
746
750
|
const branch = repoInfo.default_branch;
|
|
751
|
+
let rulesets;
|
|
752
|
+
try {
|
|
753
|
+
rulesets = await (0, github_1.ghApiJson)("GET", `repos/${repo}/rulesets?per_page=100`);
|
|
754
|
+
}
|
|
755
|
+
catch (e) {
|
|
756
|
+
report.warn(`could not list rulesets of ${repo} (${e.message})`);
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
const named = rulesets.find((r) => r.name === githubMergeGating_1.MERGE_GATING_RULESET_NAME);
|
|
760
|
+
const ruleset = named
|
|
761
|
+
? await (0, github_1.ghApiJson)("GET", `repos/${repo}/rulesets/${named.id}`).catch(() => null)
|
|
762
|
+
: null;
|
|
763
|
+
if (ruleset && (0, githubMergeGating_1.rulesetProvidesMergeGating)(ruleset)) {
|
|
764
|
+
report.ok(`ruleset '${githubMergeGating_1.MERGE_GATING_RULESET_NAME}': '${pipeline_1.AGGREGATE_CHECK_JOB_NAME}' required on ${branch}, deploy-key bypass for the release push`);
|
|
765
|
+
}
|
|
766
|
+
else if (ruleset) {
|
|
767
|
+
report.fail(`ruleset '${githubMergeGating_1.MERGE_GATING_RULESET_NAME}' exists but is incomplete (inactive, check not required, or deploy-key bypass missing)`, "run: catladder project setup");
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
report.fail(`no '${githubMergeGating_1.MERGE_GATING_RULESET_NAME}' ruleset on ${repo} — PRs can merge while the pipeline is red or still running`, "run: catladder project setup");
|
|
771
|
+
}
|
|
772
|
+
try {
|
|
773
|
+
const [keys, secretNames] = await Promise.all([
|
|
774
|
+
(0, github_1.ghApiJson)("GET", `repos/${repo}/keys?per_page=100`),
|
|
775
|
+
(0, github_1.listGithubSecretNames)(repo),
|
|
776
|
+
]);
|
|
777
|
+
const key = keys.find((k) => k.title === githubMergeGating_1.RELEASE_DEPLOY_KEY_TITLE);
|
|
778
|
+
const hasSecret = secretNames.some((name) => name.toUpperCase() === githubMergeGating_1.RELEASE_DEPLOY_KEY_SECRET);
|
|
779
|
+
if (key && !key.read_only && hasSecret) {
|
|
780
|
+
report.ok(`release deploy key '${githubMergeGating_1.RELEASE_DEPLOY_KEY_TITLE}' + secret ${githubMergeGating_1.RELEASE_DEPLOY_KEY_SECRET} in place`);
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
report.fail(`release deploy key incomplete (key: ${key ? (key.read_only ? "read-only" : "ok") : "missing"}, secret ${githubMergeGating_1.RELEASE_DEPLOY_KEY_SECRET}: ${hasSecret ? "set" : "missing"}) — the release job cannot push past the merge gating`, "run: catladder project setup");
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
catch (e) {
|
|
787
|
+
report.warn(`could not check the release deploy key (${e.message})`);
|
|
788
|
+
}
|
|
747
789
|
const protection = await (0, github_1.ghApiJson)("GET", `repos/${repo}/branches/${branch}/protection`).catch(() => null);
|
|
748
790
|
const checks = (_b = (_a = protection === null || protection === void 0 ? void 0 : protection.required_status_checks) === null || _a === void 0 ? void 0 : _a.checks) !== null && _b !== void 0 ? _b : [];
|
|
749
791
|
if (checks.some((c) => c.context === pipeline_1.AGGREGATE_CHECK_JOB_NAME)) {
|
|
750
|
-
report.
|
|
751
|
-
}
|
|
752
|
-
else {
|
|
753
|
-
report.fail(`'${pipeline_1.AGGREGATE_CHECK_JOB_NAME}' is not a required check on ${branch} — PRs can merge while the pipeline is red or still running`, "run: catladder project setup");
|
|
792
|
+
report.fail(`'${pipeline_1.AGGREGATE_CHECK_JOB_NAME}' is still required via classic branch protection on ${branch} — that rejects the release job's push (GH006)`, "run: catladder project setup");
|
|
754
793
|
}
|
|
755
794
|
const stale = checks.filter((c) => c.context !== pipeline_1.AGGREGATE_CHECK_JOB_NAME);
|
|
756
795
|
if (stale.length > 0) {
|
|
757
|
-
report.warn(`${branch} requires ${stale.length} additional named check(s) (${stale
|
|
796
|
+
report.warn(`${branch} requires ${stale.length} additional named check(s) via classic branch protection (${stale
|
|
758
797
|
.map((c) => c.context)
|
|
759
798
|
.join(", ")}) — job names change with components; prefer only '${pipeline_1.AGGREGATE_CHECK_JOB_NAME}'`);
|
|
760
799
|
}
|
|
@@ -950,6 +989,93 @@ exports.doctorProject = doctorProject;
|
|
|
950
989
|
|
|
951
990
|
/***/ }),
|
|
952
991
|
|
|
992
|
+
/***/ 14966:
|
|
993
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
994
|
+
|
|
995
|
+
"use strict";
|
|
996
|
+
|
|
997
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
998
|
+
exports.rulesetProvidesMergeGating = exports.desiredMergeGatingRuleset = exports.RELEASE_DEPLOY_KEY_SECRET = exports.RELEASE_DEPLOY_KEY_TITLE = exports.MERGE_GATING_RULESET_NAME = exports.GITHUB_ACTIONS_APP_ID = void 0;
|
|
999
|
+
const pipeline_1 = __nccwpck_require__(53695);
|
|
1000
|
+
/**
|
|
1001
|
+
* github merge gating — shared between `project setup` (writes it) and
|
|
1002
|
+
* `project doctor` (verifies it).
|
|
1003
|
+
*
|
|
1004
|
+
* The gating lives in a repository RULESET, not classic branch
|
|
1005
|
+
* protection: only rulesets can carry bypass actors, and the release
|
|
1006
|
+
* job needs one. It pushes the release commit and tag straight to the
|
|
1007
|
+
* default branch — under classic protection the required `catladder ✅`
|
|
1008
|
+
* check rejects that push (GH006), because the fresh release commit
|
|
1009
|
+
* cannot have a passing check yet.
|
|
1010
|
+
*
|
|
1011
|
+
* The bypass actor is a DEPLOY KEY: github deliberately refuses the
|
|
1012
|
+
* built-in GitHub Actions app on bypass lists (any collaborator could
|
|
1013
|
+
* push anywhere by authoring a workflow), so the workflow token can
|
|
1014
|
+
* never bypass. Instead `project setup` provisions a write deploy key
|
|
1015
|
+
* (public half on the repo, private half as an actions secret) and the
|
|
1016
|
+
* release job pushes over ssh with it. Deploy keys are repo-scoped,
|
|
1017
|
+
* only admins can add them, and they never expire — gitlab's
|
|
1018
|
+
* counterpart is the maintainer-role `GL_TOKEN`, which has to be
|
|
1019
|
+
* renewed yearly.
|
|
1020
|
+
*/
|
|
1021
|
+
/** the app id of GitHub Actions — the integration reporting the check */
|
|
1022
|
+
exports.GITHUB_ACTIONS_APP_ID = 15368;
|
|
1023
|
+
exports.MERGE_GATING_RULESET_NAME = "catladder merge gating";
|
|
1024
|
+
/** title of the deploy key `project setup` registers on the repo */
|
|
1025
|
+
exports.RELEASE_DEPLOY_KEY_TITLE = "catladder release";
|
|
1026
|
+
/**
|
|
1027
|
+
* actions secret holding the deploy key's private half; the generated
|
|
1028
|
+
* release jobs pass it to catci, which pushes over ssh when it is set
|
|
1029
|
+
*/
|
|
1030
|
+
exports.RELEASE_DEPLOY_KEY_SECRET = "CATLADDER_RELEASE_KEY";
|
|
1031
|
+
/** the ruleset `project setup` creates (and restores on drift) */
|
|
1032
|
+
const desiredMergeGatingRuleset = () => ({
|
|
1033
|
+
name: exports.MERGE_GATING_RULESET_NAME,
|
|
1034
|
+
target: "branch",
|
|
1035
|
+
enforcement: "active",
|
|
1036
|
+
conditions: { ref_name: { include: ["~DEFAULT_BRANCH"], exclude: [] } },
|
|
1037
|
+
rules: [
|
|
1038
|
+
{
|
|
1039
|
+
type: "required_status_checks",
|
|
1040
|
+
parameters: {
|
|
1041
|
+
strict_required_status_checks_policy: false,
|
|
1042
|
+
do_not_enforce_on_create: false,
|
|
1043
|
+
required_status_checks: [
|
|
1044
|
+
{
|
|
1045
|
+
context: pipeline_1.AGGREGATE_CHECK_JOB_NAME,
|
|
1046
|
+
integration_id: exports.GITHUB_ACTIONS_APP_ID,
|
|
1047
|
+
},
|
|
1048
|
+
],
|
|
1049
|
+
},
|
|
1050
|
+
},
|
|
1051
|
+
],
|
|
1052
|
+
bypass_actors: [
|
|
1053
|
+
// deploy keys carry no id — the bypass covers every deploy key of
|
|
1054
|
+
// the repository (adding one requires admin anyway)
|
|
1055
|
+
{ actor_id: null, actor_type: "DeployKey", bypass_mode: "always" },
|
|
1056
|
+
],
|
|
1057
|
+
});
|
|
1058
|
+
exports.desiredMergeGatingRuleset = desiredMergeGatingRuleset;
|
|
1059
|
+
/**
|
|
1060
|
+
* whether a ruleset provides working merge gating: active, requires
|
|
1061
|
+
* the aggregate check, and lets the release deploy key bypass it.
|
|
1062
|
+
* Extra rules or bypass actors someone added are fine.
|
|
1063
|
+
*/
|
|
1064
|
+
const rulesetProvidesMergeGating = (ruleset) => {
|
|
1065
|
+
var _a, _b;
|
|
1066
|
+
return ruleset.enforcement === "active" &&
|
|
1067
|
+
((_a = ruleset.rules) !== null && _a !== void 0 ? _a : []).some((rule) => {
|
|
1068
|
+
var _a, _b;
|
|
1069
|
+
return rule.type === "required_status_checks" &&
|
|
1070
|
+
((_b = (_a = rule.parameters) === null || _a === void 0 ? void 0 : _a.required_status_checks) !== null && _b !== void 0 ? _b : []).some((check) => check.context === pipeline_1.AGGREGATE_CHECK_JOB_NAME);
|
|
1071
|
+
}) &&
|
|
1072
|
+
((_b = ruleset.bypass_actors) !== null && _b !== void 0 ? _b : []).some((actor) => actor.actor_type === "DeployKey" && actor.bypass_mode === "always");
|
|
1073
|
+
};
|
|
1074
|
+
exports.rulesetProvidesMergeGating = rulesetProvidesMergeGating;
|
|
1075
|
+
//# sourceMappingURL=githubMergeGating.js.map
|
|
1076
|
+
|
|
1077
|
+
/***/ }),
|
|
1078
|
+
|
|
953
1079
|
/***/ 2231:
|
|
954
1080
|
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
955
1081
|
|
|
@@ -1045,13 +1171,14 @@ const setupProject = async (instance, onlyComponents) => {
|
|
|
1045
1171
|
});
|
|
1046
1172
|
const enabledPipelines = config ? (0, pipeline_1.getEnabledPipelineTypes)(config) : [];
|
|
1047
1173
|
await (0, logSection_1.logSection)(instance, "base setup", async () => {
|
|
1048
|
-
// gitlab-specific provisioning (access token, agent webhooks
|
|
1049
|
-
// meaningless (and failing) on a
|
|
1174
|
+
// gitlab-specific provisioning (access token, agent webhooks, the
|
|
1175
|
+
// `catladder` project topic) — meaningless (and failing) on a
|
|
1176
|
+
// github-only project
|
|
1050
1177
|
if (enabledPipelines.includes("gitlab")) {
|
|
1051
1178
|
await (0, setupAccessTokens_1.setupAccessTokens)(instance);
|
|
1052
1179
|
await (0, setupAgents_1.setupAgents)(instance);
|
|
1180
|
+
await (0, setupTopic_1.setupTopic)(instance);
|
|
1053
1181
|
}
|
|
1054
|
-
await (0, setupTopic_1.setupTopic)(instance);
|
|
1055
1182
|
if (config) {
|
|
1056
1183
|
await (0, setupGithub_1.setupGithub)(instance, config);
|
|
1057
1184
|
}
|
|
@@ -1434,8 +1561,15 @@ exports.setupContext = setupContext;
|
|
|
1434
1561
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1435
1562
|
exports.setupGithub = void 0;
|
|
1436
1563
|
const pipeline_1 = __nccwpck_require__(53695);
|
|
1564
|
+
const child_process_1 = __nccwpck_require__(35317);
|
|
1565
|
+
const promises_1 = __nccwpck_require__(91943);
|
|
1566
|
+
const os_1 = __nccwpck_require__(70857);
|
|
1567
|
+
const path_1 = __nccwpck_require__(16928);
|
|
1568
|
+
const util_1 = __nccwpck_require__(39023);
|
|
1437
1569
|
const commandSecretsSyncGithub_1 = __nccwpck_require__(27565);
|
|
1438
1570
|
const github_1 = __nccwpck_require__(41353);
|
|
1571
|
+
const githubMergeGating_1 = __nccwpck_require__(14966);
|
|
1572
|
+
const execFile = (0, util_1.promisify)(child_process_1.execFile);
|
|
1439
1573
|
/**
|
|
1440
1574
|
* configures github merge gating — the sane default gitlab ships
|
|
1441
1575
|
* built-in and a fresh github repository lacks entirely: without it a
|
|
@@ -1443,18 +1577,22 @@ const github_1 = __nccwpck_require__(41353);
|
|
|
1443
1577
|
* auto-merge button never appears.
|
|
1444
1578
|
*
|
|
1445
1579
|
* - repo settings: allow auto-merge, delete merged head branches
|
|
1446
|
-
* -
|
|
1580
|
+
* - the release deploy key: a write deploy key on the repo, its
|
|
1581
|
+
* private half stored as the CATLADDER_RELEASE_KEY actions secret —
|
|
1582
|
+
* the release job pushes the release commit and tag with it
|
|
1583
|
+
* - a repository ruleset on the default branch requiring the generated
|
|
1447
1584
|
* `catladder ✅` aggregate check (the one stable context — individual
|
|
1448
|
-
* job names change with every component add/rename)
|
|
1585
|
+
* job names change with every component add/rename), with a deploy
|
|
1586
|
+
* key bypass so the release push goes through (see
|
|
1587
|
+
* githubMergeGating.ts for the full rationale)
|
|
1449
1588
|
*
|
|
1450
|
-
*
|
|
1451
|
-
*
|
|
1452
|
-
*
|
|
1453
|
-
*
|
|
1454
|
-
*
|
|
1589
|
+
* Earlier versions required the check via classic branch protection,
|
|
1590
|
+
* which has no bypass list — the release job's direct push to the
|
|
1591
|
+
* default branch bounced off catladder's own gating (GH006). Setup
|
|
1592
|
+
* migrates that away: the aggregate context is removed from an
|
|
1593
|
+
* existing classic rule, everything else in it is preserved.
|
|
1455
1594
|
*/
|
|
1456
1595
|
const setupGithub = async (instance, config) => {
|
|
1457
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
1458
1596
|
if (!(0, pipeline_1.getEnabledPipelineTypes)(config).includes("github")) {
|
|
1459
1597
|
return;
|
|
1460
1598
|
}
|
|
@@ -1478,34 +1616,128 @@ const setupGithub = async (instance, config) => {
|
|
|
1478
1616
|
else {
|
|
1479
1617
|
instance.log("✅ auto-merge already enabled");
|
|
1480
1618
|
}
|
|
1619
|
+
await ensureReleaseDeployKey(instance, repo);
|
|
1481
1620
|
const branch = repoInfo.default_branch;
|
|
1482
|
-
const
|
|
1483
|
-
const
|
|
1484
|
-
if (
|
|
1485
|
-
|
|
1621
|
+
const rulesets = await (0, github_1.ghApiJson)("GET", `repos/${repo}/rulesets?per_page=100`);
|
|
1622
|
+
const existing = rulesets.find((r) => r.name === githubMergeGating_1.MERGE_GATING_RULESET_NAME);
|
|
1623
|
+
if (!existing) {
|
|
1624
|
+
await (0, github_1.ghApiJson)("POST", `repos/${repo}/rulesets`, (0, githubMergeGating_1.desiredMergeGatingRuleset)());
|
|
1625
|
+
instance.log(`✅ ruleset '${githubMergeGating_1.MERGE_GATING_RULESET_NAME}': '${pipeline_1.AGGREGATE_CHECK_JOB_NAME}' required on ${branch} — merges wait for the pipeline, the release job can push`);
|
|
1626
|
+
}
|
|
1627
|
+
else {
|
|
1628
|
+
const detail = await (0, github_1.ghApiJson)("GET", `repos/${repo}/rulesets/${existing.id}`);
|
|
1629
|
+
if ((0, githubMergeGating_1.rulesetProvidesMergeGating)(detail)) {
|
|
1630
|
+
instance.log(`✅ ruleset '${githubMergeGating_1.MERGE_GATING_RULESET_NAME}' already in place on ${branch}`);
|
|
1631
|
+
}
|
|
1632
|
+
else {
|
|
1633
|
+
await (0, github_1.ghApiJson)("PUT", `repos/${repo}/rulesets/${existing.id}`, (0, githubMergeGating_1.desiredMergeGatingRuleset)());
|
|
1634
|
+
instance.log(`✅ ruleset '${githubMergeGating_1.MERGE_GATING_RULESET_NAME}' restored — '${pipeline_1.AGGREGATE_CHECK_JOB_NAME}' required, release-job bypass in place`);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
await migrateAwayFromClassicProtection(instance, repo, branch);
|
|
1638
|
+
};
|
|
1639
|
+
exports.setupGithub = setupGithub;
|
|
1640
|
+
/**
|
|
1641
|
+
* provisions the release deploy key: an ed25519 keypair whose public
|
|
1642
|
+
* half becomes a write deploy key on the repo and whose private half
|
|
1643
|
+
* becomes the CATLADDER_RELEASE_KEY actions secret. The key and the
|
|
1644
|
+
* secret only work as a pair, so when either half is missing the pair
|
|
1645
|
+
* is rotated (github secrets are write-only — the private half can
|
|
1646
|
+
* never be read back to check it matches).
|
|
1647
|
+
*/
|
|
1648
|
+
const ensureReleaseDeployKey = async (instance, repo) => {
|
|
1649
|
+
const [keys, secretNames] = await Promise.all([
|
|
1650
|
+
(0, github_1.ghApiJson)("GET", `repos/${repo}/keys?per_page=100`),
|
|
1651
|
+
(0, github_1.listGithubSecretNames)(repo),
|
|
1652
|
+
]);
|
|
1653
|
+
const existingKey = keys.find((k) => k.title === githubMergeGating_1.RELEASE_DEPLOY_KEY_TITLE);
|
|
1654
|
+
const hasSecret = secretNames.some((name) => name.toUpperCase() === githubMergeGating_1.RELEASE_DEPLOY_KEY_SECRET);
|
|
1655
|
+
if (existingKey && !existingKey.read_only && hasSecret) {
|
|
1656
|
+
instance.log(`✅ release deploy key '${githubMergeGating_1.RELEASE_DEPLOY_KEY_TITLE}' + secret ${githubMergeGating_1.RELEASE_DEPLOY_KEY_SECRET} in place`);
|
|
1486
1657
|
return;
|
|
1487
1658
|
}
|
|
1488
|
-
|
|
1489
|
-
|
|
1659
|
+
if (existingKey) {
|
|
1660
|
+
await (0, github_1.ghApiJson)("DELETE", `repos/${repo}/keys/${existingKey.id}`);
|
|
1661
|
+
instance.log(`♻️ release deploy key '${githubMergeGating_1.RELEASE_DEPLOY_KEY_TITLE}' rotated (${existingKey.read_only
|
|
1662
|
+
? "was read-only"
|
|
1663
|
+
: `secret ${githubMergeGating_1.RELEASE_DEPLOY_KEY_SECRET} missing`})`);
|
|
1664
|
+
}
|
|
1665
|
+
const dir = await (0, promises_1.mkdtemp)((0, path_1.join)((0, os_1.tmpdir)(), "catladder-release-key-"));
|
|
1666
|
+
try {
|
|
1667
|
+
const keyFile = (0, path_1.join)(dir, "id_ed25519");
|
|
1668
|
+
await execFile("ssh-keygen", [
|
|
1669
|
+
...["-t", "ed25519"],
|
|
1670
|
+
...["-C", `${githubMergeGating_1.RELEASE_DEPLOY_KEY_TITLE} (${repo})`],
|
|
1671
|
+
...["-N", ""],
|
|
1672
|
+
...["-f", keyFile],
|
|
1673
|
+
"-q",
|
|
1674
|
+
]);
|
|
1675
|
+
const [privateKey, publicKey] = await Promise.all([
|
|
1676
|
+
(0, promises_1.readFile)(keyFile, "utf-8"),
|
|
1677
|
+
(0, promises_1.readFile)(`${keyFile}.pub`, "utf-8"),
|
|
1678
|
+
]);
|
|
1679
|
+
await (0, github_1.ghApiJson)("POST", `repos/${repo}/keys`, {
|
|
1680
|
+
title: githubMergeGating_1.RELEASE_DEPLOY_KEY_TITLE,
|
|
1681
|
+
key: publicKey.trim(),
|
|
1682
|
+
read_only: false,
|
|
1683
|
+
});
|
|
1684
|
+
await (0, github_1.setGithubSecret)(repo, githubMergeGating_1.RELEASE_DEPLOY_KEY_SECRET, privateKey);
|
|
1685
|
+
}
|
|
1686
|
+
finally {
|
|
1687
|
+
await (0, promises_1.rm)(dir, { recursive: true, force: true });
|
|
1688
|
+
}
|
|
1689
|
+
instance.log(`✅ release deploy key '${githubMergeGating_1.RELEASE_DEPLOY_KEY_TITLE}' registered, private half stored as secret ${githubMergeGating_1.RELEASE_DEPLOY_KEY_SECRET} — the release job pushes with it`);
|
|
1690
|
+
};
|
|
1691
|
+
/**
|
|
1692
|
+
* removes the aggregate check from a classic branch protection rule
|
|
1693
|
+
* set up by earlier catladder versions — with the check now required
|
|
1694
|
+
* via the ruleset, the classic one would still reject the release
|
|
1695
|
+
* job's push. Everything else in the rule is preserved; the whole rule
|
|
1696
|
+
* is deleted when the check was all it carried.
|
|
1697
|
+
*/
|
|
1698
|
+
const migrateAwayFromClassicProtection = async (instance, repo, branch) => {
|
|
1699
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1;
|
|
1700
|
+
const protection = await (0, github_1.ghApiJson)("GET", `repos/${repo}/branches/${branch}/protection`).catch(() => null); // 404: branch not protected
|
|
1701
|
+
const checks = (_b = (_a = protection === null || protection === void 0 ? void 0 : protection.required_status_checks) === null || _a === void 0 ? void 0 : _a.checks) !== null && _b !== void 0 ? _b : [];
|
|
1702
|
+
if (!checks.some((c) => c.context === pipeline_1.AGGREGATE_CHECK_JOB_NAME)) {
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
const remaining = checks.filter((c) => c.context !== pipeline_1.AGGREGATE_CHECK_JOB_NAME);
|
|
1706
|
+
const carriesAnythingElse = remaining.length > 0 ||
|
|
1707
|
+
((_c = protection === null || protection === void 0 ? void 0 : protection.enforce_admins) === null || _c === void 0 ? void 0 : _c.enabled) ||
|
|
1708
|
+
(protection === null || protection === void 0 ? void 0 : protection.required_pull_request_reviews) ||
|
|
1709
|
+
(protection === null || protection === void 0 ? void 0 : protection.restrictions) ||
|
|
1710
|
+
((_d = protection === null || protection === void 0 ? void 0 : protection.required_linear_history) === null || _d === void 0 ? void 0 : _d.enabled) ||
|
|
1711
|
+
((_e = protection === null || protection === void 0 ? void 0 : protection.required_conversation_resolution) === null || _e === void 0 ? void 0 : _e.enabled) ||
|
|
1712
|
+
((_f = protection === null || protection === void 0 ? void 0 : protection.required_signatures) === null || _f === void 0 ? void 0 : _f.enabled) ||
|
|
1713
|
+
((_g = protection === null || protection === void 0 ? void 0 : protection.lock_branch) === null || _g === void 0 ? void 0 : _g.enabled) ||
|
|
1714
|
+
((_h = protection === null || protection === void 0 ? void 0 : protection.allow_force_pushes) === null || _h === void 0 ? void 0 : _h.enabled) ||
|
|
1715
|
+
((_j = protection === null || protection === void 0 ? void 0 : protection.allow_deletions) === null || _j === void 0 ? void 0 : _j.enabled);
|
|
1716
|
+
if (!carriesAnythingElse) {
|
|
1717
|
+
await (0, github_1.ghApiJson)("DELETE", `repos/${repo}/branches/${branch}/protection`);
|
|
1718
|
+
instance.log(`✅ classic branch protection on ${branch} removed — '${pipeline_1.AGGREGATE_CHECK_JOB_NAME}' was all it carried, and it is now required via the ruleset`);
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
// PUT replaces the whole rule, so every preserved setting must be
|
|
1722
|
+
// carried over explicitly
|
|
1490
1723
|
await (0, github_1.ghApiJson)("PUT", `repos/${repo}/branches/${branch}/protection`, {
|
|
1491
|
-
required_status_checks:
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1724
|
+
required_status_checks: remaining.length > 0
|
|
1725
|
+
? {
|
|
1726
|
+
strict: (_l = (_k = protection === null || protection === void 0 ? void 0 : protection.required_status_checks) === null || _k === void 0 ? void 0 : _k.strict) !== null && _l !== void 0 ? _l : false,
|
|
1727
|
+
checks: remaining.map(({ context, app_id }) => ({
|
|
1495
1728
|
context,
|
|
1496
1729
|
...(app_id !== undefined ? { app_id } : {}),
|
|
1497
1730
|
})),
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
enforce_admins: (_f = (_e = protection === null || protection === void 0 ? void 0 : protection.enforce_admins) === null || _e === void 0 ? void 0 : _e.enabled) !== null && _f !== void 0 ? _f : false,
|
|
1731
|
+
}
|
|
1732
|
+
: null,
|
|
1733
|
+
enforce_admins: (_o = (_m = protection === null || protection === void 0 ? void 0 : protection.enforce_admins) === null || _m === void 0 ? void 0 : _m.enabled) !== null && _o !== void 0 ? _o : false,
|
|
1502
1734
|
required_pull_request_reviews: (protection === null || protection === void 0 ? void 0 : protection.required_pull_request_reviews)
|
|
1503
1735
|
? {
|
|
1504
|
-
required_approving_review_count: (
|
|
1505
|
-
.required_approving_review_count) !== null &&
|
|
1506
|
-
dismiss_stale_reviews: (
|
|
1507
|
-
require_code_owner_reviews: (
|
|
1508
|
-
.require_code_owner_reviews) !== null &&
|
|
1736
|
+
required_approving_review_count: (_p = protection.required_pull_request_reviews
|
|
1737
|
+
.required_approving_review_count) !== null && _p !== void 0 ? _p : 0,
|
|
1738
|
+
dismiss_stale_reviews: (_q = protection.required_pull_request_reviews.dismiss_stale_reviews) !== null && _q !== void 0 ? _q : false,
|
|
1739
|
+
require_code_owner_reviews: (_r = protection.required_pull_request_reviews
|
|
1740
|
+
.require_code_owner_reviews) !== null && _r !== void 0 ? _r : false,
|
|
1509
1741
|
}
|
|
1510
1742
|
: null,
|
|
1511
1743
|
restrictions: (protection === null || protection === void 0 ? void 0 : protection.restrictions)
|
|
@@ -1515,12 +1747,14 @@ const setupGithub = async (instance, config) => {
|
|
|
1515
1747
|
apps: protection.restrictions.apps.map((a) => a.slug),
|
|
1516
1748
|
}
|
|
1517
1749
|
: null,
|
|
1518
|
-
|
|
1519
|
-
|
|
1750
|
+
required_linear_history: (_t = (_s = protection === null || protection === void 0 ? void 0 : protection.required_linear_history) === null || _s === void 0 ? void 0 : _s.enabled) !== null && _t !== void 0 ? _t : false,
|
|
1751
|
+
required_conversation_resolution: (_v = (_u = protection === null || protection === void 0 ? void 0 : protection.required_conversation_resolution) === null || _u === void 0 ? void 0 : _u.enabled) !== null && _v !== void 0 ? _v : false,
|
|
1752
|
+
lock_branch: (_x = (_w = protection === null || protection === void 0 ? void 0 : protection.lock_branch) === null || _w === void 0 ? void 0 : _w.enabled) !== null && _x !== void 0 ? _x : false,
|
|
1753
|
+
allow_force_pushes: (_z = (_y = protection === null || protection === void 0 ? void 0 : protection.allow_force_pushes) === null || _y === void 0 ? void 0 : _y.enabled) !== null && _z !== void 0 ? _z : false,
|
|
1754
|
+
allow_deletions: (_1 = (_0 = protection === null || protection === void 0 ? void 0 : protection.allow_deletions) === null || _0 === void 0 ? void 0 : _0.enabled) !== null && _1 !== void 0 ? _1 : false,
|
|
1520
1755
|
});
|
|
1521
|
-
instance.log(`✅ branch protection on ${branch}
|
|
1756
|
+
instance.log(`✅ '${pipeline_1.AGGREGATE_CHECK_JOB_NAME}' removed from classic branch protection on ${branch} — now required via the ruleset; the rest of the rule is preserved`);
|
|
1522
1757
|
};
|
|
1523
|
-
exports.setupGithub = setupGithub;
|
|
1524
1758
|
//# sourceMappingURL=setupGithub.js.map
|
|
1525
1759
|
|
|
1526
1760
|
/***/ }),
|
|
@@ -9513,6 +9747,11 @@ const createReleaseJob = (config, images, options = {}) => {
|
|
|
9513
9747
|
...(allNeeds.length > 0 ? { needs: allNeeds } : {}),
|
|
9514
9748
|
env: {
|
|
9515
9749
|
GITHUB_TOKEN: "${{ github.token }}",
|
|
9750
|
+
// the release deploy key (provisioned by `project setup`): the
|
|
9751
|
+
// release push must bypass the merge-gating ruleset, which only
|
|
9752
|
+
// a deploy key can — empty when not provisioned, then the job
|
|
9753
|
+
// pushes with the workflow token (fine without gating)
|
|
9754
|
+
CATLADDER_RELEASE_KEY: "${{ secrets.CATLADDER_RELEASE_KEY }}",
|
|
9516
9755
|
...options.extraEnv,
|
|
9517
9756
|
},
|
|
9518
9757
|
steps: [
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
FROM node:lts-alpine
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# openssh: the release job pushes with the release deploy key over ssh
|
|
4
|
+
RUN apk add --no-cache bash git git-lfs openssh-client
|
|
4
5
|
|
|
5
6
|
# no release tooling baked in: the changesets release logic lives in
|
|
6
7
|
# catci, which is materialized into the repository checkout
|
|
@@ -260,13 +260,21 @@ now-wrong `gitRemote`, regenerate once more, and finish with
|
|
|
260
260
|
PR is mergeable the moment it opens, checks still running, and the
|
|
261
261
|
auto-merge button does not even appear. `yarn catladder project setup`
|
|
262
262
|
configures it: auto-merge + delete-merged-branches on the repo, and a
|
|
263
|
-
|
|
264
|
-
**`catladder ✅`** aggregate check
|
|
265
|
-
require individual job names, they
|
|
266
|
-
add/rename and a stale required name
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
263
|
+
repository **ruleset** (`catladder merge gating`) on the default
|
|
264
|
+
branch requiring the generated **`catladder ✅`** aggregate check
|
|
265
|
+
(the one stable context — never require individual job names, they
|
|
266
|
+
change with every component add/rename and a stale required name
|
|
267
|
+
blocks its own PR forever). The ruleset carries a **deploy-key
|
|
268
|
+
bypass**: the release job pushes the release commit and tag straight
|
|
269
|
+
to the default branch, which the required check would otherwise
|
|
270
|
+
reject (GH006) — and github deliberately never lets the workflow
|
|
271
|
+
token bypass rulesets, so setup provisions a write deploy key
|
|
272
|
+
("catladder release", private half in the `CATLADDER_RELEASE_KEY`
|
|
273
|
+
actions secret; deploy keys never expire) that the release job
|
|
274
|
+
pushes with over ssh. Classic branch protection has no bypass list
|
|
275
|
+
at all, which is why setup uses a ruleset and migrates old
|
|
276
|
+
classic-protection gating away. `project doctor` verifies all of it.
|
|
277
|
+
Required reviews stay a human choice.
|
|
270
278
|
- Tell the team where the pipeline lives now and how manual actions
|
|
271
279
|
work there (on GitHub, manual jobs are dispatch workflows in the
|
|
272
280
|
Actions sidebar — `🚀 catladder create release`, `▶️ catladder deploy`,
|
|
@@ -57,6 +57,11 @@ const createReleaseJob = (config, images, options = {}) => {
|
|
|
57
57
|
...(allNeeds.length > 0 ? { needs: allNeeds } : {}),
|
|
58
58
|
env: {
|
|
59
59
|
GITHUB_TOKEN: "${{ github.token }}",
|
|
60
|
+
// the release deploy key (provisioned by `project setup`): the
|
|
61
|
+
// release push must bypass the merge-gating ruleset, which only
|
|
62
|
+
// a deploy key can — empty when not provisioned, then the job
|
|
63
|
+
// pushes with the workflow token (fine without gating)
|
|
64
|
+
CATLADDER_RELEASE_KEY: "${{ secrets.CATLADDER_RELEASE_KEY }}",
|
|
60
65
|
...options.extraEnv,
|
|
61
66
|
},
|
|
62
67
|
steps: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"githubReleaseJobs.js","sourceRoot":"","sources":["../../../../../../../../packages/pipeline/src/backends/github/githubReleaseJobs.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"githubReleaseJobs.js","sourceRoot":"","sources":["../../../../../../../../packages/pipeline/src/backends/github/githubReleaseJobs.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
FROM node:lts-alpine
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# openssh: the release job pushes with the release deploy key over ssh
|
|
4
|
+
RUN apk add --no-cache bash git git-lfs openssh-client
|
|
4
5
|
|
|
5
6
|
# no release tooling baked in: the changesets release logic lives in
|
|
6
7
|
# catci, which is materialized into the repository checkout
|
|
@@ -260,13 +260,21 @@ now-wrong `gitRemote`, regenerate once more, and finish with
|
|
|
260
260
|
PR is mergeable the moment it opens, checks still running, and the
|
|
261
261
|
auto-merge button does not even appear. `yarn catladder project setup`
|
|
262
262
|
configures it: auto-merge + delete-merged-branches on the repo, and a
|
|
263
|
-
|
|
264
|
-
**`catladder ✅`** aggregate check
|
|
265
|
-
require individual job names, they
|
|
266
|
-
add/rename and a stale required name
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
263
|
+
repository **ruleset** (`catladder merge gating`) on the default
|
|
264
|
+
branch requiring the generated **`catladder ✅`** aggregate check
|
|
265
|
+
(the one stable context — never require individual job names, they
|
|
266
|
+
change with every component add/rename and a stale required name
|
|
267
|
+
blocks its own PR forever). The ruleset carries a **deploy-key
|
|
268
|
+
bypass**: the release job pushes the release commit and tag straight
|
|
269
|
+
to the default branch, which the required check would otherwise
|
|
270
|
+
reject (GH006) — and github deliberately never lets the workflow
|
|
271
|
+
token bypass rulesets, so setup provisions a write deploy key
|
|
272
|
+
("catladder release", private half in the `CATLADDER_RELEASE_KEY`
|
|
273
|
+
actions secret; deploy keys never expire) that the release job
|
|
274
|
+
pushes with over ssh. Classic branch protection has no bypass list
|
|
275
|
+
at all, which is why setup uses a ruleset and migrates old
|
|
276
|
+
classic-protection gating away. `project doctor` verifies all of it.
|
|
277
|
+
Required reviews stay a human choice.
|
|
270
278
|
- Tell the team where the pipeline lives now and how manual actions
|
|
271
279
|
work there (on GitHub, manual jobs are dispatch workflows in the
|
|
272
280
|
Actions sidebar — `🚀 catladder create release`, `▶️ catladder deploy`,
|