redlinegate 0.0.1
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/LICENSE +21 -0
- package/README.md +188 -0
- package/commands/redline-init.md +54 -0
- package/commands/redline-verify.md +69 -0
- package/dist/bin/redline.js +605 -0
- package/dist/bin/redline.js.map +1 -0
- package/dist/commands/exempt.js +45 -0
- package/dist/commands/exempt.js.map +1 -0
- package/dist/commands/init.js +615 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/policy.js +25 -0
- package/dist/commands/policy.js.map +1 -0
- package/dist/commands/remove.js +427 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/review.js +72 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/sync.js +42 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/verify.js +478 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/config/redline-json.js +187 -0
- package/dist/config/redline-json.js.map +1 -0
- package/dist/core/errors.js +25 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/git.js +152 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/log.js +22 -0
- package/dist/core/log.js.map +1 -0
- package/dist/core/severity.js +16 -0
- package/dist/core/severity.js.map +1 -0
- package/dist/core/version.js +15 -0
- package/dist/core/version.js.map +1 -0
- package/dist/detect/scan.js +79 -0
- package/dist/detect/scan.js.map +1 -0
- package/dist/detect/stack.js +71 -0
- package/dist/detect/stack.js.map +1 -0
- package/dist/enforce/ladder.js +148 -0
- package/dist/enforce/ladder.js.map +1 -0
- package/dist/exempt/parse.js +86 -0
- package/dist/exempt/parse.js.map +1 -0
- package/dist/metrics/options.js +257 -0
- package/dist/metrics/options.js.map +1 -0
- package/dist/metrics/run.js +72 -0
- package/dist/metrics/run.js.map +1 -0
- package/dist/platforms/azure/client.js +53 -0
- package/dist/platforms/azure/client.js.map +1 -0
- package/dist/platforms/azure/index.js +69 -0
- package/dist/platforms/azure/index.js.map +1 -0
- package/dist/platforms/azure/install.js +917 -0
- package/dist/platforms/azure/install.js.map +1 -0
- package/dist/platforms/azure/policy-types.js +70 -0
- package/dist/platforms/azure/policy-types.js.map +1 -0
- package/dist/platforms/azure/verify.js +309 -0
- package/dist/platforms/azure/verify.js.map +1 -0
- package/dist/platforms/detect.js +40 -0
- package/dist/platforms/detect.js.map +1 -0
- package/dist/platforms/github/client.js +49 -0
- package/dist/platforms/github/client.js.map +1 -0
- package/dist/platforms/github/index.js +54 -0
- package/dist/platforms/github/index.js.map +1 -0
- package/dist/platforms/github/install.js +558 -0
- package/dist/platforms/github/install.js.map +1 -0
- package/dist/platforms/github/push.js +102 -0
- package/dist/platforms/github/push.js.map +1 -0
- package/dist/platforms/github/remote.js +36 -0
- package/dist/platforms/github/remote.js.map +1 -0
- package/dist/platforms/github/verify.js +366 -0
- package/dist/platforms/github/verify.js.map +1 -0
- package/dist/platforms/http.js +58 -0
- package/dist/platforms/http.js.map +1 -0
- package/dist/platforms/pull-request-templates.js +157 -0
- package/dist/platforms/pull-request-templates.js.map +1 -0
- package/dist/platforms/remote.js +21 -0
- package/dist/platforms/remote.js.map +1 -0
- package/dist/platforms/resolve.js +69 -0
- package/dist/platforms/resolve.js.map +1 -0
- package/dist/platforms/shape.js +19 -0
- package/dist/platforms/shape.js.map +1 -0
- package/dist/platforms/types.js +24 -0
- package/dist/platforms/types.js.map +1 -0
- package/dist/policy/checks.js +91 -0
- package/dist/policy/checks.js.map +1 -0
- package/dist/policy/diff.js +50 -0
- package/dist/policy/diff.js.map +1 -0
- package/dist/registry/discover.js +74 -0
- package/dist/registry/discover.js.map +1 -0
- package/dist/registry/serialize.js +41 -0
- package/dist/registry/serialize.js.map +1 -0
- package/dist/registry/types.js +2 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/remove/host.js +294 -0
- package/dist/remove/host.js.map +1 -0
- package/dist/render/commands.js +139 -0
- package/dist/render/commands.js.map +1 -0
- package/dist/render/manifest.js +82 -0
- package/dist/render/manifest.js.map +1 -0
- package/dist/render/markers.js +166 -0
- package/dist/render/markers.js.map +1 -0
- package/dist/render/profile.js +23 -0
- package/dist/render/profile.js.map +1 -0
- package/dist/render/standards.js +169 -0
- package/dist/render/standards.js.map +1 -0
- package/dist/render/vendors.js +199 -0
- package/dist/render/vendors.js.map +1 -0
- package/dist/review/engines/api.js +95 -0
- package/dist/review/engines/api.js.map +1 -0
- package/dist/review/engines/embedded.js +20 -0
- package/dist/review/engines/embedded.js.map +1 -0
- package/dist/review/engines/types.js +2 -0
- package/dist/review/engines/types.js.map +1 -0
- package/dist/review/glob.js +29 -0
- package/dist/review/glob.js.map +1 -0
- package/dist/review/prompt.js +45 -0
- package/dist/review/prompt.js.map +1 -0
- package/dist/review/rules.js +25 -0
- package/dist/review/rules.js.map +1 -0
- package/dist/review/schema.js +84 -0
- package/dist/review/schema.js.map +1 -0
- package/dist/review/scope.js +27 -0
- package/dist/review/scope.js.map +1 -0
- package/dist/sarif/map.js +58 -0
- package/dist/sarif/map.js.map +1 -0
- package/dist/sarif/parse.js +88 -0
- package/dist/sarif/parse.js.map +1 -0
- package/dist/sarif/types.js +2 -0
- package/dist/sarif/types.js.map +1 -0
- package/dist/sync/host.js +21 -0
- package/dist/sync/host.js.map +1 -0
- package/dist/sync/plan.js +47 -0
- package/dist/sync/plan.js.map +1 -0
- package/dist/sync/render.js +57 -0
- package/dist/sync/render.js.map +1 -0
- package/dist/sync/run.js +118 -0
- package/dist/sync/run.js.map +1 -0
- package/dist/verify/host.js +52 -0
- package/dist/verify/host.js.map +1 -0
- package/dist/verify/remote.js +146 -0
- package/dist/verify/remote.js.map +1 -0
- package/package.json +52 -0
- package/platforms/azure/gate-template.yml +105 -0
- package/rulesets/redline-org-ruleset.json +46 -0
- package/rulesets/redline-ruleset.json +49 -0
- package/scripts/assign-rule-ids.mjs +129 -0
- package/scripts/build-baseline.mjs +124 -0
- package/scripts/build-correlation.mjs +74 -0
- package/scripts/build-dashboard.mjs +584 -0
- package/scripts/build-digest.mjs +164 -0
- package/scripts/build-inbox.mjs +133 -0
- package/scripts/build-registry.mjs +42 -0
- package/scripts/build-roi.mjs +181 -0
- package/scripts/check-pins.mjs +88 -0
- package/scripts/collect-telemetry.mjs +300 -0
- package/scripts/lib/__tests__/baseline.test.mjs +132 -0
- package/scripts/lib/__tests__/correlate.test.mjs +131 -0
- package/scripts/lib/__tests__/dora.test.mjs +110 -0
- package/scripts/lib/__tests__/exemptions.test.mjs +85 -0
- package/scripts/lib/__tests__/metrics-scanner.test.mjs +65 -0
- package/scripts/lib/__tests__/sarif.test.mjs +125 -0
- package/scripts/lib/__tests__/spend.test.mjs +64 -0
- package/scripts/lib/baseline.mjs +156 -0
- package/scripts/lib/correlate.mjs +125 -0
- package/scripts/lib/dora.mjs +133 -0
- package/scripts/lib/exemptions.mjs +79 -0
- package/scripts/lib/metrics.mjs +172 -0
- package/scripts/lib/rules.mjs +107 -0
- package/scripts/lib/sarif.mjs +108 -0
- package/scripts/lib/spend.mjs +69 -0
- package/scripts/measure-context.mjs +101 -0
- package/scripts/render-self.mjs +18 -0
- package/scripts/score-seeds.mjs +294 -0
- package/scripts/validate.mjs +394 -0
- package/standards/core.md +124 -0
- package/standards/manifest.json +214 -0
- package/standards/stacks/csharp.md +29 -0
- package/standards/stacks/go.md +30 -0
- package/standards/stacks/java.md +30 -0
- package/standards/stacks/javascript.md +42 -0
- package/standards/stacks/kotlin.md +27 -0
- package/standards/stacks/microservices.md +37 -0
- package/standards/stacks/nodejs.md +34 -0
- package/standards/stacks/python.md +29 -0
- package/standards/stacks/react-native.md +29 -0
- package/standards/stacks/react.md +46 -0
- package/standards/stacks/swift.md +26 -0
- package/standards/stacks/terraform.md +27 -0
- package/templates/CODEOWNERS +29 -0
- package/templates/azure/pull_request_template.md +72 -0
- package/templates/github/pull_request_template.md +72 -0
- package/templates/redline.yml +35 -0
- package/templates/repo-context.md +64 -0
- package/workflows/dashboard.yml +119 -0
- package/workflows/inbox.yml +97 -0
- package/workflows/redline-collect.yml +61 -0
- package/workflows/redline-gate.yml +301 -0
- package/workflows/redline-sync.yml +79 -0
- package/workflows/seed-canary.yml +231 -0
- package/workflows/verify-onboarding.yml +115 -0
- package/workflows/weekly-digest.yml +78 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { RedlineError } from "../../core/errors.js";
|
|
2
|
+
import { createGit } from "../../core/git.js";
|
|
3
|
+
import { parseRemote } from "../detect.js";
|
|
4
|
+
import { isNonNullObject } from "../shape.js";
|
|
5
|
+
import { createGitHubInstall } from "./install.js";
|
|
6
|
+
import { createGitHubVerify } from "./verify.js";
|
|
7
|
+
export function createGitHubPlatform(opts) {
|
|
8
|
+
const gitFor = opts.gitFor ?? ((cwd) => createGit(cwd));
|
|
9
|
+
const install = createGitHubInstall(opts.client, gitFor);
|
|
10
|
+
const verify = createGitHubVerify(opts.client);
|
|
11
|
+
return {
|
|
12
|
+
host: 'github',
|
|
13
|
+
// `git.defaultBranch()` falls back to the literal 'main' when
|
|
14
|
+
// refs/remotes/origin/HEAD is not set (a shallow or freshly-created
|
|
15
|
+
// clone), so the branch below is a guess, not a fact. Nothing on the
|
|
16
|
+
// dry-run path reads it today; anything added that does must resolve it
|
|
17
|
+
// properly or report that it could not, rather than inherit the fiction.
|
|
18
|
+
localRef(cwd) {
|
|
19
|
+
const git = gitFor(cwd);
|
|
20
|
+
const identity = parseRemote(git.remoteUrl());
|
|
21
|
+
return {
|
|
22
|
+
host: 'github',
|
|
23
|
+
org: identity.org,
|
|
24
|
+
repo: identity.repo,
|
|
25
|
+
defaultBranch: git.defaultBranch(),
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
async repoRef(cwd) {
|
|
29
|
+
const identity = parseRemote(gitFor(cwd).remoteUrl());
|
|
30
|
+
const path = `/repos/${identity.org}/${identity.repo}`;
|
|
31
|
+
const repo = await opts.client.rest('GET', path);
|
|
32
|
+
if (repo.status === 401 || repo.status === 403) {
|
|
33
|
+
// "Token lacks scope" and "GitHub is down" must exit differently
|
|
34
|
+
// (3 vs 4) so CI can tell an operator problem from a host problem.
|
|
35
|
+
throw new RedlineError('permission', `GitHub returned HTTP ${repo.status} reading ${path}`, 'check the GH_TOKEN scopes, or run: gh auth login');
|
|
36
|
+
}
|
|
37
|
+
if (repo.status < 200 || repo.status >= 300) {
|
|
38
|
+
throw new RedlineError('host', `GitHub returned HTTP ${repo.status} reading ${path}`);
|
|
39
|
+
}
|
|
40
|
+
if (!isNonNullObject(repo.body) || typeof repo.body['default_branch'] !== 'string') {
|
|
41
|
+
throw new RedlineError('host', `GitHub returned an unexpected shape for ${path}`);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
host: 'github',
|
|
45
|
+
org: identity.org,
|
|
46
|
+
repo: identity.repo,
|
|
47
|
+
defaultBranch: repo.body['default_branch'],
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
...install,
|
|
51
|
+
...verify,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../cli/platforms/github/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAY,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAOjD,MAAM,UAAU,oBAAoB,CAAC,IAA2B;IAC9D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAE/C,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,8DAA8D;QAC9D,oEAAoE;QACpE,qEAAqE;QACrE,wEAAwE;QACxE,yEAAyE;QACzE,QAAQ,CAAC,GAAW;YAClB,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YACxB,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;YAC9C,OAAO;gBACL,IAAI,EAAE,QAAQ;gBACd,GAAG,EAAE,QAAQ,CAAC,GAAG;gBACjB,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,aAAa,EAAE,GAAG,CAAC,aAAa,EAAE;aACnC,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,GAAW;YACvB,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;YACtD,MAAM,IAAI,GAAG,UAAU,QAAQ,CAAC,GAAG,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YACvD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAU,KAAK,EAAE,IAAI,CAAC,CAAC;YAC1D,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC/C,iEAAiE;gBACjE,mEAAmE;gBACnE,MAAM,IAAI,YAAY,CACpB,YAAY,EACZ,wBAAwB,IAAI,CAAC,MAAM,YAAY,IAAI,EAAE,EACrD,kDAAkD,CACnD,CAAC;YACJ,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;gBAC5C,MAAM,IAAI,YAAY,CAAC,MAAM,EAAE,wBAAwB,IAAI,CAAC,MAAM,YAAY,IAAI,EAAE,CAAC,CAAC;YACxF,CAAC;YACD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACnF,MAAM,IAAI,YAAY,CAAC,MAAM,EAAE,2CAA2C,IAAI,EAAE,CAAC,CAAC;YACpF,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,QAAQ;gBACd,GAAG,EAAE,QAAQ,CAAC,GAAG;gBACjB,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC;aAC3C,CAAC;QACJ,CAAC;QACD,GAAG,OAAO;QACV,GAAG,MAAM;KACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { RedlineError } from "../../core/errors.js";
|
|
5
|
+
import { isNonNullObject, isSuccess } from "../shape.js";
|
|
6
|
+
import { BEGIN_PREFIX, END, findBlock, wrapBlock } from "../../render/markers.js";
|
|
7
|
+
import { TEMPLATE_DIRS as PULL_REQUEST_TEMPLATE_DIRS } from "../pull-request-templates.js";
|
|
8
|
+
export const RULESET_NAME = 'Redline';
|
|
9
|
+
export const REQUIRED_CHECK = 'redline-gate / gate';
|
|
10
|
+
const PACKAGE_ROOT = fileURLToPath(new URL('../../../', import.meta.url));
|
|
11
|
+
const GATE_LABELS = [
|
|
12
|
+
{ name: 'no-adr', color: 'ededed', description: 'PR intentionally ships without an ADR' },
|
|
13
|
+
{
|
|
14
|
+
name: 'redline-exempt',
|
|
15
|
+
color: 'fbca04',
|
|
16
|
+
description: 'Gate process checks soft-failed with reviewer sign-off',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'redline-sync',
|
|
20
|
+
color: '0e8a16',
|
|
21
|
+
description: 'Automated standards sync from the Redline source repo',
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
function parseRulesetSummaries(body) {
|
|
25
|
+
if (!Array.isArray(body))
|
|
26
|
+
return null;
|
|
27
|
+
const summaries = [];
|
|
28
|
+
for (const item of body) {
|
|
29
|
+
if (!isNonNullObject(item) || typeof item['id'] !== 'number' || typeof item['name'] !== 'string') {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
summaries.push({ id: item['id'], name: item['name'] });
|
|
33
|
+
}
|
|
34
|
+
return summaries;
|
|
35
|
+
}
|
|
36
|
+
function parseCreatedPullRequest(body) {
|
|
37
|
+
if (!isNonNullObject(body) || typeof body['number'] !== 'number' || typeof body['html_url'] !== 'string') {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return { number: body['number'], url: body['html_url'] };
|
|
41
|
+
}
|
|
42
|
+
function outcome(capability, status, detail) {
|
|
43
|
+
if (isSuccess(status))
|
|
44
|
+
return { capability, status: 'applied', detail };
|
|
45
|
+
if (status === 401 || status === 403) {
|
|
46
|
+
return { capability, status: 'denied', detail: `${detail} (needs repository admin)` };
|
|
47
|
+
}
|
|
48
|
+
if (status === 404 || status === 422) {
|
|
49
|
+
return { capability, status: 'unsupported', detail: `${detail} (not available on this repository)` };
|
|
50
|
+
}
|
|
51
|
+
return { capability, status: 'denied', detail: `${detail} (HTTP ${status})` };
|
|
52
|
+
}
|
|
53
|
+
// GitHub answers 404, not 403, on these admin write endpoints when a
|
|
54
|
+
// fine-grained token lacks the administration scope on a repository it can
|
|
55
|
+
// otherwise read — so on a write, 404 is a permission denial that must reach
|
|
56
|
+
// pendingAdmin, not a missing feature. Read endpoints keep 404 = unsupported.
|
|
57
|
+
function writeOutcome(capability, status, detail) {
|
|
58
|
+
if (status === 404) {
|
|
59
|
+
return { capability, status: 'denied', detail: `${detail} (needs repository admin)` };
|
|
60
|
+
}
|
|
61
|
+
return outcome(capability, status, detail);
|
|
62
|
+
}
|
|
63
|
+
// Combines several outcomes for one logical capability (e.g. GitHub reports
|
|
64
|
+
// dependency-alerts as two separate calls, and labels are created one at a
|
|
65
|
+
// time) into a single outcome. Ranked by how actionable/notable the status is
|
|
66
|
+
// so a real permission denial on one sub-call is never masked by a merely
|
|
67
|
+
// larger HTTP status number on another (a 404 "unsupported" must not hide a
|
|
68
|
+
// 403 "denied", and an "already exists" 422 must not hide a 403 "denied").
|
|
69
|
+
// `unknown` never appears here today — nothing installGate/applyPolicy/
|
|
70
|
+
// enableSecurityFloor writes degrades a status into it, only verify.ts's
|
|
71
|
+
// readSecurityState does — but the status union requires every member ranked.
|
|
72
|
+
// It sits between `denied` and `unsupported`: a genuine denial must never be
|
|
73
|
+
// masked by an indeterminate read, and an indeterminate read must never be
|
|
74
|
+
// masked by a definite "not available here" or a definite success either.
|
|
75
|
+
const OUTCOME_RANK = {
|
|
76
|
+
denied: 4,
|
|
77
|
+
unknown: 3,
|
|
78
|
+
unsupported: 2,
|
|
79
|
+
already: 1,
|
|
80
|
+
applied: 0,
|
|
81
|
+
};
|
|
82
|
+
function worstOutcome(outcomes) {
|
|
83
|
+
const [first, ...rest] = outcomes;
|
|
84
|
+
if (!first)
|
|
85
|
+
throw new Error('worstOutcome requires at least one outcome');
|
|
86
|
+
return rest.reduce((worst, next) => (OUTCOME_RANK[next.status] > OUTCOME_RANK[worst.status] ? next : worst), first);
|
|
87
|
+
}
|
|
88
|
+
// Writes only when the bytes differ, and answers whether they did — the same
|
|
89
|
+
// contract as cli/render/standards.ts. `redline init` folds the returned file
|
|
90
|
+
// list into its already-onboarded decision, so a file reported as written when
|
|
91
|
+
// nothing changed leaves a modified tracked file behind with no pull request
|
|
92
|
+
// to carry it: exactly what a CLI-version bump used to do to the pinned gate
|
|
93
|
+
// template. `check` computes the answer and writes nothing.
|
|
94
|
+
function syncFile(cwd, relPath, contents, check) {
|
|
95
|
+
const target = join(cwd, relPath);
|
|
96
|
+
const current = existsSync(target) ? readFileSync(target, 'utf8') : null;
|
|
97
|
+
if (current === contents)
|
|
98
|
+
return false;
|
|
99
|
+
if (check)
|
|
100
|
+
return true;
|
|
101
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
102
|
+
writeFileSync(target, contents);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
// The two sections workflows/redline-gate.yml actually reads out of a pull
|
|
106
|
+
// request body, and the reason it is these two and not the others: the
|
|
107
|
+
// `checklist` job fails the pull request outright when `## Launch readiness`
|
|
108
|
+
// is missing, and the `adr` job fails a large diff whose body carries no
|
|
109
|
+
// `docs/adr/` link, which is what `## Architecture decision` prompts for.
|
|
110
|
+
// `## Change type` is gated by nothing (the workflow says so in a comment),
|
|
111
|
+
// `## Automated review` is gated by nothing, and `# Summary` would collide
|
|
112
|
+
// with the heading a repository's own template already has. Dropping the ADR
|
|
113
|
+
// section would leave a merged repository failing a gate job it has no
|
|
114
|
+
// affordance to satisfy.
|
|
115
|
+
//
|
|
116
|
+
// `satisfied` asks what the gate job asks, not what the section looks like.
|
|
117
|
+
// The checklist job's awk matches the heading by prefix, so
|
|
118
|
+
// `## Launch readiness checklist` already satisfies it and must not be given a
|
|
119
|
+
// second, competing section. The adr job greps the whole body for `docs/adr/`,
|
|
120
|
+
// so any existing ADR link satisfies it, heading or no heading.
|
|
121
|
+
const GATED_SECTIONS = [
|
|
122
|
+
{
|
|
123
|
+
heading: 'Launch readiness',
|
|
124
|
+
satisfied: (template) => /^##[ \t]+Launch readiness/m.test(template),
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
heading: 'Architecture decision',
|
|
128
|
+
satisfied: (template) => template.includes('docs/adr/'),
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
// The gate reads a `## Redline exemption` block, so a template without one
|
|
132
|
+
// leaves an author who needs a waiver with nowhere to write it. It rides along in
|
|
133
|
+
// any block Redline is writing anyway, but it is deliberately NOT in
|
|
134
|
+
// GATED_SECTIONS: no gate job fails for its absence, so it must never be the
|
|
135
|
+
// reason a marker block appears in a template a team wrote for themselves.
|
|
136
|
+
const EXEMPTION_HEADING = 'Redline exemption';
|
|
137
|
+
// The headings to put inside a block, given the gated ones that still need
|
|
138
|
+
// covering. Empty in, empty out: no block is created just to carry the optional
|
|
139
|
+
// section.
|
|
140
|
+
const blockSections = (headings) => headings.length === 0 ? [] : [...headings, EXEMPTION_HEADING];
|
|
141
|
+
function gatedSections(template, headings) {
|
|
142
|
+
const kept = [];
|
|
143
|
+
let inside = false;
|
|
144
|
+
for (const line of template.split('\n')) {
|
|
145
|
+
// The packaged template carries the markers itself, so a greenfield file is
|
|
146
|
+
// marked from the first run. The marker lines are the wrapper wrapBlock
|
|
147
|
+
// adds back, never part of the body it wraps.
|
|
148
|
+
if (line.startsWith(BEGIN_PREFIX) || line.startsWith(END))
|
|
149
|
+
continue;
|
|
150
|
+
const heading = /^##\s+(.+?)\s*$/.exec(line);
|
|
151
|
+
if (heading)
|
|
152
|
+
inside = headings.includes(heading[1] ?? '');
|
|
153
|
+
if (inside)
|
|
154
|
+
kept.push(line);
|
|
155
|
+
}
|
|
156
|
+
return kept.join('\n').trim();
|
|
157
|
+
}
|
|
158
|
+
// GitHub's documentation names only `pull_request_template.md`. `.txt` and
|
|
159
|
+
// extension-less forms are not documented, and adopting an undocumented one as
|
|
160
|
+
// the merge target risks merging into a file the host never serves while
|
|
161
|
+
// creating no `.md` at all — so the served name is the only candidate.
|
|
162
|
+
const TEMPLATE_NAMES = ['pull_request_template.md'];
|
|
163
|
+
// GitHub resolves the default template from `.github/`, the repository root
|
|
164
|
+
// and `docs/` — its documentation names the three folders but no precedence.
|
|
165
|
+
// The order itself lives in pull-request-templates.ts, the one source shared
|
|
166
|
+
// with `redline verify` — see the comment there.
|
|
167
|
+
const TEMPLATE_DIRS = PULL_REQUEST_TEMPLATE_DIRS.github;
|
|
168
|
+
// Resolved by listing rather than by `existsSync`, for two reasons the review
|
|
169
|
+
// found the hard way: both hosts treat the folder name as case-insensitive, and
|
|
170
|
+
// `existsSync` is case-insensitive on macOS but not on Linux, so a `.GitHub/`
|
|
171
|
+
// checkout was silently missed on CI and a second template written beside the
|
|
172
|
+
// served one. Listing also answers `isDirectory()`, which is what stops a plain
|
|
173
|
+
// file named `docs` from throwing ENOTDIR mid-run, after the gate workflow has
|
|
174
|
+
// already been written.
|
|
175
|
+
function candidateDirs(cwd) {
|
|
176
|
+
const entries = readdirSync(cwd, { withFileTypes: true });
|
|
177
|
+
const found = [];
|
|
178
|
+
for (const wanted of TEMPLATE_DIRS) {
|
|
179
|
+
if (wanted === '') {
|
|
180
|
+
found.push({ abs: cwd, rel: '' });
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const dir = entries.find((entry) => entry.isDirectory() && entry.name.toLowerCase() === wanted);
|
|
184
|
+
if (dir)
|
|
185
|
+
found.push({ abs: join(cwd, dir.name), rel: dir.name });
|
|
186
|
+
}
|
|
187
|
+
return found;
|
|
188
|
+
}
|
|
189
|
+
const under = (candidate, name) => candidate.rel === '' ? name : `${candidate.rel}/${name}`;
|
|
190
|
+
function findPullRequestTemplate(cwd) {
|
|
191
|
+
for (const candidate of candidateDirs(cwd)) {
|
|
192
|
+
const names = readdirSync(candidate.abs, { withFileTypes: true })
|
|
193
|
+
.filter((entry) => entry.isFile() && TEMPLATE_NAMES.includes(entry.name.toLowerCase()))
|
|
194
|
+
.map((entry) => entry.name);
|
|
195
|
+
// Two case variants in one directory: the host picks one and does not say
|
|
196
|
+
// which, so the tie-break is at least deterministic rather than whatever
|
|
197
|
+
// order the filesystem happened to list. Canonical spelling first,
|
|
198
|
+
// preferred extension next, byte order last.
|
|
199
|
+
names.sort((a, b) => {
|
|
200
|
+
const byName = TEMPLATE_NAMES.indexOf(a.toLowerCase()) - TEMPLATE_NAMES.indexOf(b.toLowerCase());
|
|
201
|
+
if (byName !== 0)
|
|
202
|
+
return byName;
|
|
203
|
+
if (a === b.toLowerCase())
|
|
204
|
+
return -1;
|
|
205
|
+
if (b === a.toLowerCase())
|
|
206
|
+
return 1;
|
|
207
|
+
return a < b ? -1 : 1;
|
|
208
|
+
});
|
|
209
|
+
const hit = names[0];
|
|
210
|
+
if (hit !== undefined)
|
|
211
|
+
return under(candidate, hit);
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
// A repository's own pull request template is a human-owned file, and this was
|
|
216
|
+
// the last host-writing path in either adapter that simply overwrote one.
|
|
217
|
+
// Leaving it alone is not the fix either: the gate fails any pull request
|
|
218
|
+
// whose body has no `## Launch readiness` section, so an untouched brownfield
|
|
219
|
+
// template would block the repository's own pull requests. So: write the
|
|
220
|
+
// packaged template only where the host would resolve none, and otherwise
|
|
221
|
+
// merge only the gated sections the file does not already satisfy into a
|
|
222
|
+
// REDLINE marker block, leaving every other byte alone.
|
|
223
|
+
function mergeTemplate(cwd, relPath, packaged, check) {
|
|
224
|
+
const target = join(cwd, relPath);
|
|
225
|
+
const existing = existsSync(target) ? readFileSync(target, 'utf8') : null;
|
|
226
|
+
if (existing === null) {
|
|
227
|
+
return { path: relPath, changed: syncFile(cwd, relPath, packaged, check), detail: `wrote ${relPath}` };
|
|
228
|
+
}
|
|
229
|
+
// Throws on a half-edited marker pair, or on markers hidden below an unclosed
|
|
230
|
+
// code fence, rather than guessing which span is Redline's — see
|
|
231
|
+
// cli/render/markers.ts. Nothing is written on that path.
|
|
232
|
+
const span = findBlock(existing, relPath);
|
|
233
|
+
if (span !== null) {
|
|
234
|
+
// Measured against what the template provides OUTSIDE the block, never the
|
|
235
|
+
// whole file. Refreshing with every gated section unconditionally put
|
|
236
|
+
// Redline's own `## Launch readiness` inside the block while the
|
|
237
|
+
// repository's stayed outside it, and the gate's awk enforces both — so the
|
|
238
|
+
// second run broke a repository the first run had merged correctly.
|
|
239
|
+
const outside = existing.slice(0, span.start) + existing.slice(span.stop + END.length);
|
|
240
|
+
const wanted = GATED_SECTIONS.filter((section) => !section.satisfied(outside)).map((section) => section.heading);
|
|
241
|
+
if (wanted.length === 0) {
|
|
242
|
+
return {
|
|
243
|
+
path: relPath,
|
|
244
|
+
changed: false,
|
|
245
|
+
detail: `${relPath} satisfies the gate outside the Redline block — left untouched`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const contents = wrapBlock(existing, gatedSections(packaged, blockSections(wanted)), relPath);
|
|
249
|
+
const changed = syncFile(cwd, relPath, contents, check);
|
|
250
|
+
return {
|
|
251
|
+
path: relPath,
|
|
252
|
+
changed,
|
|
253
|
+
detail: changed
|
|
254
|
+
? `refreshed the Redline block in ${relPath}`
|
|
255
|
+
: `${relPath} is already up to date`,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
const missing = GATED_SECTIONS.filter((section) => !section.satisfied(existing)).map((section) => section.heading);
|
|
259
|
+
if (missing.length === 0) {
|
|
260
|
+
// Also where a repository onboarded before the packaged template carried
|
|
261
|
+
// markers lands: its marker-less file already answers both gate jobs, so it
|
|
262
|
+
// keeps what it has and Redline never rewrites it.
|
|
263
|
+
return {
|
|
264
|
+
path: relPath,
|
|
265
|
+
changed: false,
|
|
266
|
+
detail: `${relPath} already satisfies the gate on its own — left untouched`,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
const contents = wrapBlock(existing, gatedSections(packaged, blockSections(missing)), relPath);
|
|
270
|
+
const appended = missing.map((heading) => `"## ${heading}"`).join(' and ');
|
|
271
|
+
return {
|
|
272
|
+
path: relPath,
|
|
273
|
+
changed: syncFile(cwd, relPath, contents, check),
|
|
274
|
+
detail: `kept this repository's ${relPath} and appended ${appended} inside REDLINE markers`,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
// The one file `redline init` writes that cannot take the marker-block merge
|
|
278
|
+
// the shared markdown artifacts take: appending to YAML gives the workflow a
|
|
279
|
+
// second `name:` and `on:` key, and a file that no longer parses runs nothing
|
|
280
|
+
// at all. So the only two honest answers here are "replace Redline's own file"
|
|
281
|
+
// and "stop" — never "overwrite whatever was there".
|
|
282
|
+
//
|
|
283
|
+
// Attribution is read from the bytes rather than from the path, because the
|
|
284
|
+
// path alone proves nothing and the 2.1 rollout wrote its own caller here,
|
|
285
|
+
// which `redline init` exists to migrate. Deliberately generous: the
|
|
286
|
+
// false-negative direction destroys a repository's file, and the
|
|
287
|
+
// false-positive direction only replaces something that already carries
|
|
288
|
+
// Redline's name at Redline's path.
|
|
289
|
+
const CALLER_PATH = '.github/workflows/redline.yml';
|
|
290
|
+
// Positive attribution, not a substring test. Almost every workflow a human
|
|
291
|
+
// writes at a path called `redline.yml` says "redline" somewhere — in `name:`,
|
|
292
|
+
// in a job id, in a `run:` line — so matching the word protected only the
|
|
293
|
+
// repository that never mentioned it, and clobbered the ones that did. Two
|
|
294
|
+
// things are Redline's own: the reusable-workflow reference every v3 caller
|
|
295
|
+
// carries (the same contract cli/commands/init.ts's V3_CALLER reads to tell a
|
|
296
|
+
// v3 repository from a 2.1 one), and the ownership line templates/redline.yml
|
|
297
|
+
// now opens with. A 2.1 caller carries neither, and no fixture of the real
|
|
298
|
+
// thing exists to pin, so it is refused rather than guessed at — `adoptCaller`
|
|
299
|
+
// is where that decision belongs.
|
|
300
|
+
const MANAGED_BY_REDLINE = /^#[ \t]*Managed by Redline\b/m;
|
|
301
|
+
const USES_REDLINE_GATE = /\.github\/workflows\/redline-gate\.yml@/;
|
|
302
|
+
function refuseForeignCaller(cwd, opts) {
|
|
303
|
+
if (opts.adoptCaller === true)
|
|
304
|
+
return;
|
|
305
|
+
const target = join(cwd, CALLER_PATH);
|
|
306
|
+
if (!existsSync(target))
|
|
307
|
+
return;
|
|
308
|
+
const existing = readFileSync(target, 'utf8');
|
|
309
|
+
if (MANAGED_BY_REDLINE.test(existing) || USES_REDLINE_GATE.test(existing))
|
|
310
|
+
return;
|
|
311
|
+
throw new RedlineError('failed', `${CALLER_PATH} already exists in this repository and carries nothing that attributes it to ` +
|
|
312
|
+
'Redline, so installing the merge gate there would destroy it. Nothing was written', 'If that workflow is already this repository\'s merge gate, re-run with --skip gate and Redline ' +
|
|
313
|
+
'will leave it in charge. Otherwise move or rename it and re-run redline init — or, if it is a ' +
|
|
314
|
+
'Redline 2.1 caller this run should replace, re-run with --adopt-caller. Redline cannot merge ' +
|
|
315
|
+
'into it the way it merges into a markdown file: a second `name:` and `on:` key would stop the ' +
|
|
316
|
+
'workflow running at all.');
|
|
317
|
+
}
|
|
318
|
+
function syncPullRequestTemplate(cwd, defaultPath, packaged, check) {
|
|
319
|
+
const results = [mergeTemplate(cwd, findPullRequestTemplate(cwd) ?? defaultPath, packaged, check)];
|
|
320
|
+
const details = [results[0]?.detail ?? ''];
|
|
321
|
+
const files = results.filter((result) => result.changed).map((result) => result.path);
|
|
322
|
+
return { files, changed: files.length > 0, detail: details.join('; ') };
|
|
323
|
+
}
|
|
324
|
+
function buildRules(policy) {
|
|
325
|
+
const rules = [
|
|
326
|
+
{ type: 'deletion' },
|
|
327
|
+
{ type: 'non_fast_forward' },
|
|
328
|
+
{
|
|
329
|
+
type: 'pull_request',
|
|
330
|
+
parameters: {
|
|
331
|
+
required_approving_review_count: policy.requiredApprovals,
|
|
332
|
+
dismiss_stale_reviews_on_push: policy.dismissStaleReviews,
|
|
333
|
+
require_code_owner_review: policy.requireCodeOwnerReview,
|
|
334
|
+
require_last_push_approval: true,
|
|
335
|
+
required_review_thread_resolution: policy.requireThreadResolution,
|
|
336
|
+
automatic_copilot_code_review_enabled: true,
|
|
337
|
+
allowed_merge_methods: ['squash', 'merge'],
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
];
|
|
341
|
+
if (policy.blocking) {
|
|
342
|
+
// The check name is this adapter's own, exactly as Azure's status policy
|
|
343
|
+
// uses AZURE_STATUS_NAME/GENRE. It previously came from
|
|
344
|
+
// policy.requiredChecks, which cli/commands/init.ts passes empty — so
|
|
345
|
+
// `redline init --blocking` published a blocking ruleset that required
|
|
346
|
+
// nothing at all.
|
|
347
|
+
rules.push({
|
|
348
|
+
type: 'required_status_checks',
|
|
349
|
+
parameters: {
|
|
350
|
+
strict_required_status_checks_policy: false,
|
|
351
|
+
do_not_enforce_on_create: false,
|
|
352
|
+
required_status_checks: [{ context: REQUIRED_CHECK }],
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
return rules;
|
|
357
|
+
}
|
|
358
|
+
export function createGitHubInstall(client, gitFor) {
|
|
359
|
+
const repoPath = (ref) => `/repos/${ref.org}/${ref.repo}`;
|
|
360
|
+
return {
|
|
361
|
+
async enableSecurityFloor(ref) {
|
|
362
|
+
const scanning = await client.rest('PATCH', repoPath(ref), {
|
|
363
|
+
security_and_analysis: {
|
|
364
|
+
secret_scanning: { status: 'enabled' },
|
|
365
|
+
secret_scanning_push_protection: { status: 'enabled' },
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
const alerts = await client.rest('PUT', `${repoPath(ref)}/vulnerability-alerts`);
|
|
369
|
+
const fixes = await client.rest('PUT', `${repoPath(ref)}/automated-security-fixes`);
|
|
370
|
+
// Two independent host calls fold into one "dependency-alerts" capability.
|
|
371
|
+
// Combine by outcome severity, not by comparing raw status numbers — see
|
|
372
|
+
// worstOutcome.
|
|
373
|
+
const dependencyAlerts = worstOutcome([
|
|
374
|
+
writeOutcome('dependency-alerts', alerts.status, 'dependabot alerts (vulnerability alerts)'),
|
|
375
|
+
writeOutcome('dependency-alerts', fixes.status, 'dependabot alerts (automated security fixes)'),
|
|
376
|
+
]);
|
|
377
|
+
return {
|
|
378
|
+
outcomes: [
|
|
379
|
+
writeOutcome('secret-scanning', scanning.status, 'secret scanning'),
|
|
380
|
+
writeOutcome('push-protection', scanning.status, 'secret scanning push protection'),
|
|
381
|
+
dependencyAlerts,
|
|
382
|
+
],
|
|
383
|
+
};
|
|
384
|
+
},
|
|
385
|
+
async applyPolicy(ref, policy) {
|
|
386
|
+
const existing = await client.rest('GET', `${repoPath(ref)}/rulesets`);
|
|
387
|
+
const payload = {
|
|
388
|
+
name: RULESET_NAME,
|
|
389
|
+
target: 'branch',
|
|
390
|
+
enforcement: 'active',
|
|
391
|
+
bypass_actors: [],
|
|
392
|
+
conditions: { ref_name: { include: ['~DEFAULT_BRANCH'], exclude: [] } },
|
|
393
|
+
rules: buildRules(policy),
|
|
394
|
+
};
|
|
395
|
+
let mergePolicy;
|
|
396
|
+
let policyApplied = false;
|
|
397
|
+
if (!isSuccess(existing.status)) {
|
|
398
|
+
// Denied (401/403) or unsupported (404/422) — never guess whether a
|
|
399
|
+
// Redline ruleset already exists, and never crash on it. Skip the
|
|
400
|
+
// write entirely; onboarding still continues below.
|
|
401
|
+
mergePolicy = outcome('merge-policy', existing.status, 'branch ruleset');
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
const summaries = parseRulesetSummaries(existing.body);
|
|
405
|
+
if (summaries === null) {
|
|
406
|
+
throw new RedlineError('host', 'GitHub returned an unexpected shape for the rulesets list');
|
|
407
|
+
}
|
|
408
|
+
const mine = summaries.find((r) => r.name === RULESET_NAME);
|
|
409
|
+
const method = mine ? 'PUT' : 'POST';
|
|
410
|
+
const path = mine
|
|
411
|
+
? `${repoPath(ref)}/rulesets/${mine.id}`
|
|
412
|
+
: `${repoPath(ref)}/rulesets`;
|
|
413
|
+
const applied = await client.rest(method, path, payload);
|
|
414
|
+
if (applied.status === 422) {
|
|
415
|
+
// A rejected ruleset payload is a Redline bug or a repo-settings
|
|
416
|
+
// conflict — surfacing it as "unsupported" would leave the repo
|
|
417
|
+
// silently policy-less with exit 0.
|
|
418
|
+
throw new RedlineError('host', `GitHub rejected the Redline ruleset payload (HTTP 422 on ${method} ${path})`);
|
|
419
|
+
}
|
|
420
|
+
mergePolicy = writeOutcome('merge-policy', applied.status, 'branch ruleset');
|
|
421
|
+
policyApplied = isSuccess(applied.status);
|
|
422
|
+
}
|
|
423
|
+
const property = await client.rest('PATCH', `${repoPath(ref)}/properties/values`, {
|
|
424
|
+
properties: [{ property_name: 'redline', value: 'onboarded' }],
|
|
425
|
+
});
|
|
426
|
+
return {
|
|
427
|
+
outcomes: [
|
|
428
|
+
mergePolicy,
|
|
429
|
+
outcome('repo-property', property.status, 'repository property "redline=onboarded"'),
|
|
430
|
+
],
|
|
431
|
+
policy: policyApplied ? policy : null,
|
|
432
|
+
};
|
|
433
|
+
},
|
|
434
|
+
async installGate(ref, cwd, opts, check = false) {
|
|
435
|
+
const files = [];
|
|
436
|
+
refuseForeignCaller(cwd, opts);
|
|
437
|
+
const caller = readFileSync(join(PACKAGE_ROOT, 'templates/redline.yml'), 'utf8')
|
|
438
|
+
.replaceAll('<org>', ref.org)
|
|
439
|
+
.replace(/adr-diff-threshold: \d+/, `adr-diff-threshold: ${opts.adrDiffThreshold}`)
|
|
440
|
+
.replace(/fail-on-dependency-severity: \w+/, `fail-on-dependency-severity: ${opts.failOnDependencySeverity}`)
|
|
441
|
+
.replace(/soft-fail-labels: .+/, `soft-fail-labels: ${opts.softFailLabels.join(',')}`)
|
|
442
|
+
.replace(/rung: \w[\w-]*/, `rung: ${opts.rung ?? 'observe'}`);
|
|
443
|
+
if (syncFile(cwd, '.github/workflows/redline.yml', caller, check)) {
|
|
444
|
+
files.push('.github/workflows/redline.yml');
|
|
445
|
+
}
|
|
446
|
+
// Read from templates/, never from this repository's own .github/:
|
|
447
|
+
// .github/ is deliberately outside package.json "files" (shipping it
|
|
448
|
+
// would push Redline's own CI workflows into every consumer), so a
|
|
449
|
+
// template read from there is absent in the published tarball and the
|
|
450
|
+
// throw lands after the workflow above has already been written.
|
|
451
|
+
const template = readFileSync(join(PACKAGE_ROOT, 'templates/github/pull_request_template.md'), 'utf8');
|
|
452
|
+
const prTemplate = syncPullRequestTemplate(cwd, '.github/pull_request_template.md', template, check);
|
|
453
|
+
files.push(...prTemplate.files);
|
|
454
|
+
if (check)
|
|
455
|
+
return { files, outcomes: [] };
|
|
456
|
+
// Deselected: not attempted, and no outcome either. An outcome for work
|
|
457
|
+
// that never happened is how a deliberate choice gets read back as a
|
|
458
|
+
// capability that failed.
|
|
459
|
+
const labelOutcomes = [];
|
|
460
|
+
for (const label of opts.manageLabels === false ? [] : GATE_LABELS) {
|
|
461
|
+
const res = await client.rest('POST', `${repoPath(ref)}/labels`, label);
|
|
462
|
+
labelOutcomes.push(res.status === 422
|
|
463
|
+
? { capability: 'labels', status: 'already', detail: `label "${label.name}" already exists` }
|
|
464
|
+
: outcome('labels', res.status, `label "${label.name}"`));
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
files,
|
|
468
|
+
outcomes: [
|
|
469
|
+
...(labelOutcomes.length > 0 ? [worstOutcome(labelOutcomes)] : []),
|
|
470
|
+
{
|
|
471
|
+
capability: 'gate',
|
|
472
|
+
status: prTemplate.changed ? 'applied' : 'already',
|
|
473
|
+
detail: prTemplate.detail,
|
|
474
|
+
},
|
|
475
|
+
],
|
|
476
|
+
};
|
|
477
|
+
},
|
|
478
|
+
async ensureReviewOwnership(ref, cwd, rules, check = false) {
|
|
479
|
+
const candidates = ['.github/CODEOWNERS', 'CODEOWNERS', 'docs/CODEOWNERS'];
|
|
480
|
+
if (candidates.some((p) => existsSync(join(cwd, p)))) {
|
|
481
|
+
return {
|
|
482
|
+
files: [],
|
|
483
|
+
outcomes: [
|
|
484
|
+
{
|
|
485
|
+
capability: 'review-ownership',
|
|
486
|
+
status: 'already',
|
|
487
|
+
detail: 'this repository already has a CODEOWNERS file — left untouched',
|
|
488
|
+
},
|
|
489
|
+
],
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
const body = rules.map((r) => `${r.pattern} ${r.owners.join(' ')}`).join('\n');
|
|
493
|
+
// No candidate exists (checked above), so this always writes — `check`
|
|
494
|
+
// is what holds the write back on a dry run.
|
|
495
|
+
syncFile(cwd, '.github/CODEOWNERS', `# Managed by Redline.\n\n${body}\n`, check);
|
|
496
|
+
return {
|
|
497
|
+
files: ['.github/CODEOWNERS'],
|
|
498
|
+
outcomes: [
|
|
499
|
+
{ capability: 'review-ownership', status: 'applied', detail: 'seeded .github/CODEOWNERS' },
|
|
500
|
+
],
|
|
501
|
+
};
|
|
502
|
+
},
|
|
503
|
+
async openPullRequest(ref, cwd, change) {
|
|
504
|
+
const git = gitFor(cwd);
|
|
505
|
+
// `git commit -m` commits the WHOLE index, so anything the user staged
|
|
506
|
+
// before running redline would be swept into the onboarding PR.
|
|
507
|
+
// Refusing beats `commit -- <paths>`: a partial-index commit surprises
|
|
508
|
+
// in the opposite direction.
|
|
509
|
+
if (git.hasStagedChanges()) {
|
|
510
|
+
throw new RedlineError('usage', 'this repository already has staged changes', 'commit or unstage them first, then re-run — redline will not sweep them into its onboarding pull request');
|
|
511
|
+
}
|
|
512
|
+
const originalBranch = git.currentBranch();
|
|
513
|
+
git.checkoutNewBranch(change.branch);
|
|
514
|
+
let gitFailed = false;
|
|
515
|
+
try {
|
|
516
|
+
// Stage only what Redline itself wrote — never sweep in pre-existing
|
|
517
|
+
// dirty or untracked state from the working tree (decision 6: no
|
|
518
|
+
// customer secret is ever committed by this tool).
|
|
519
|
+
git.stagePaths(change.files);
|
|
520
|
+
if (!git.hasStagedChanges()) {
|
|
521
|
+
// Already onboarded and nothing changed: a legitimate no-op, not
|
|
522
|
+
// an error — the caller reports it instead of a pull request.
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
git.commit(change.title);
|
|
526
|
+
git.push(change.branch);
|
|
527
|
+
}
|
|
528
|
+
catch (error) {
|
|
529
|
+
gitFailed = true;
|
|
530
|
+
throw error;
|
|
531
|
+
}
|
|
532
|
+
finally {
|
|
533
|
+
// Leave the operator on their own branch, never on redline/onboard.
|
|
534
|
+
// When a git step failed its error already says where the work sits,
|
|
535
|
+
// and a failed restore must not mask it.
|
|
536
|
+
try {
|
|
537
|
+
git.checkoutBranch(originalBranch);
|
|
538
|
+
}
|
|
539
|
+
catch (restoreError) {
|
|
540
|
+
if (!gitFailed)
|
|
541
|
+
throw restoreError;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
const created = await client.rest('POST', `${repoPath(ref)}/pulls`, { title: change.title, body: change.body, head: change.branch, base: ref.defaultBranch });
|
|
545
|
+
const pr = isSuccess(created.status) ? parseCreatedPullRequest(created.body) : null;
|
|
546
|
+
if (!pr) {
|
|
547
|
+
throw new RedlineError('host', `could not open a pull request (HTTP ${created.status})`);
|
|
548
|
+
}
|
|
549
|
+
if (change.labels.length > 0) {
|
|
550
|
+
await client.rest('POST', `${repoPath(ref)}/issues/${pr.number}/labels`, {
|
|
551
|
+
labels: change.labels,
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
return pr;
|
|
555
|
+
},
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../cli/platforms/github/install.ts"],"names":[],"mappings":"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"}
|