redlinegate 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -7
- package/dist/bin/redline.js +338 -43
- package/dist/bin/redline.js.map +1 -1
- package/dist/commands/init.js +353 -34
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/remove.js +43 -1
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/status.js +94 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/verify.js +82 -2
- package/dist/commands/verify.js.map +1 -1
- package/dist/config/redline-json.js +63 -2
- package/dist/config/redline-json.js.map +1 -1
- package/dist/core/git.js +50 -3
- package/dist/core/git.js.map +1 -1
- package/dist/core/version.js +6 -0
- package/dist/core/version.js.map +1 -1
- package/dist/detect/existing.js +151 -0
- package/dist/detect/existing.js.map +1 -0
- package/dist/detect/setup.js +200 -0
- package/dist/detect/setup.js.map +1 -0
- package/dist/detect/stack.js +56 -17
- package/dist/detect/stack.js.map +1 -1
- package/dist/exempt/parse.js +8 -1
- package/dist/exempt/parse.js.map +1 -1
- package/dist/metrics/options.js +0 -9
- package/dist/metrics/options.js.map +1 -1
- package/dist/platforms/azure/install.js +9 -9
- package/dist/platforms/azure/install.js.map +1 -1
- package/dist/platforms/azure/verify.js +10 -2
- package/dist/platforms/azure/verify.js.map +1 -1
- package/dist/platforms/github/install.js +192 -26
- package/dist/platforms/github/install.js.map +1 -1
- package/dist/platforms/github/preflight.js +63 -0
- package/dist/platforms/github/preflight.js.map +1 -0
- package/dist/platforms/github/vendor.js +81 -0
- package/dist/platforms/github/vendor.js.map +1 -0
- package/dist/platforms/github/verify.js +79 -7
- package/dist/platforms/github/verify.js.map +1 -1
- package/dist/platforms/types.js +8 -0
- package/dist/platforms/types.js.map +1 -1
- package/dist/policy/diff.js +26 -9
- package/dist/policy/diff.js.map +1 -1
- package/dist/render/contexts.js +39 -0
- package/dist/render/contexts.js.map +1 -0
- package/dist/render/profile.js +44 -10
- package/dist/render/profile.js.map +1 -1
- package/dist/render/standards.js +10 -1
- package/dist/render/standards.js.map +1 -1
- package/dist/render/vendors.js +14 -53
- package/dist/render/vendors.js.map +1 -1
- package/dist/rules/catalogue.js +127 -0
- package/dist/rules/catalogue.js.map +1 -0
- package/dist/ui/facts.js +111 -0
- package/dist/ui/facts.js.map +1 -0
- package/dist/ui/prompt.js +330 -0
- package/dist/ui/prompt.js.map +1 -0
- package/dist/ui/report.js +214 -0
- package/dist/ui/report.js.map +1 -0
- package/dist/ui/tty.js +576 -0
- package/dist/ui/tty.js.map +1 -0
- package/dist/ui/wizard.js +293 -0
- package/dist/ui/wizard.js.map +1 -0
- package/dist/verify/remote.js +33 -0
- package/dist/verify/remote.js.map +1 -1
- package/package.json +13 -1
- package/platforms/azure/gate-template-github.yml +152 -0
- package/platforms/azure/gate-template.yml +59 -6
- package/scripts/check-pins.mjs +84 -1
- package/scripts/fetch-stars.mjs +92 -0
- package/scripts/lib/rules.d.mts +18 -0
- package/scripts/publish-local.mjs +183 -0
- package/standards/contexts/speckit.md +22 -0
- package/standards/contexts/tmf.md +25 -0
- package/standards/manifest.json +78 -7
- package/standards/stacks/angular.md +57 -0
- package/standards/stacks/dom.md +61 -0
- package/standards/stacks/svelte.md +45 -0
- package/standards/stacks/vue.md +54 -0
- package/templates/redline.yml +9 -9
- package/workflows/dashboard.yml +1 -1
- package/workflows/redline-collect.yml +1 -1
- package/workflows/redline-gate.yml +38 -7
- package/workflows/seed-canary.yml +2 -2
- package/workflows/weekly-digest.yml +1 -1
- package/scripts/measure-context.mjs +0 -101
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { RedlineError } from "../core/errors.js";
|
|
2
|
+
import { TOOL_PROBES } from "../detect/existing.js";
|
|
3
|
+
import { RUNGS } from "../enforce/ladder.js";
|
|
4
|
+
// Plain-English, at the moment of choosing. Every one of these was a term the
|
|
5
|
+
// operator met for the first time in output that assumed they knew it.
|
|
6
|
+
const RUNG_HINTS = {
|
|
7
|
+
observe: 'comments only — the check is always green. Start here',
|
|
8
|
+
warn: 'comments and labels the PR — still never blocks a merge',
|
|
9
|
+
'block-blocker': 'a BLOCKER finding fails the check and stops the merge',
|
|
10
|
+
'block-high': 'BLOCKER and HIGH both stop the merge — the strictest rung',
|
|
11
|
+
};
|
|
12
|
+
const CAPABILITY_HINTS = {
|
|
13
|
+
gate: 'the pull request check that runs the review',
|
|
14
|
+
'merge-policy': 'branch protection requiring the check and an approval',
|
|
15
|
+
labels: 'the labels the gate uses to mark and exempt a pull request',
|
|
16
|
+
'review-ownership': 'seeds CODEOWNERS — off unless you ask, it can block every PR',
|
|
17
|
+
};
|
|
18
|
+
// Which Redline check a tool the repository already runs makes redundant.
|
|
19
|
+
// Phrased as what the operator would otherwise get twice, because "stands down
|
|
20
|
+
// the dependencies job" means nothing to someone meeting Redline today.
|
|
21
|
+
const STAND_DOWN_LABEL = {
|
|
22
|
+
secrets: 'secret scanning',
|
|
23
|
+
dependencies: 'dependency vulnerability review',
|
|
24
|
+
policy: 'static analysis',
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The profile list, ordered by how much detection actually knows.
|
|
28
|
+
*
|
|
29
|
+
* A confident detection — the repository's own manifest naming its framework —
|
|
30
|
+
* is hoisted to the top and marked, because it is almost always the answer. A
|
|
31
|
+
* guess is not: hoisting it puts it on the first row with the cursor already on
|
|
32
|
+
* it, which reads as chosen no matter how carefully it is left unticked. So a
|
|
33
|
+
* guess sorts alphabetically with everything else and says what it is.
|
|
34
|
+
*/
|
|
35
|
+
function profileChoices(manifest, detected, confidence) {
|
|
36
|
+
// Alphabetical, always. Hoisting the detected profile to the first row put the
|
|
37
|
+
// cursor on it before anything was typed, which reads as chosen no matter how
|
|
38
|
+
// carefully it is left unticked.
|
|
39
|
+
const ids = Object.keys(manifest.profiles).sort((a, b) => a.localeCompare(b));
|
|
40
|
+
return ids.map((id) => {
|
|
41
|
+
// The stacks' own titles, not their ids. `JavaScript, React (web)` tells an
|
|
42
|
+
// operator what they are choosing; `javascript, react` makes them guess at
|
|
43
|
+
// the difference between two profiles that share a prefix.
|
|
44
|
+
const stacks = (manifest.profiles[id] ?? []).map((stack) => manifest.stacks[stack]?.title ?? stack);
|
|
45
|
+
return {
|
|
46
|
+
value: id,
|
|
47
|
+
label: id,
|
|
48
|
+
hint: stacks.join(', ') +
|
|
49
|
+
(id === detected
|
|
50
|
+
? confidence === 'high'
|
|
51
|
+
? ' — looks like this repository, but pick it yourself'
|
|
52
|
+
: ' — a guess; nothing here names this framework'
|
|
53
|
+
: ''),
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// Every vendor the standards know about, including the ones this organisation
|
|
58
|
+
// has switched off — shown greyed with the reason rather than filtered out.
|
|
59
|
+
//
|
|
60
|
+
// Hiding them was wrong for the same reason hiding a covered capability would
|
|
61
|
+
// be: an operator looking for Cursor and not finding it cannot tell whether
|
|
62
|
+
// Redline forgot about Cursor, does not support it, or was told not to render
|
|
63
|
+
// for it here. Only the last is true, and only the last is something they can
|
|
64
|
+
// go and change.
|
|
65
|
+
function vendorChoices(manifest) {
|
|
66
|
+
return Object.entries(manifest.vendors).map(([id, vendor]) => ({
|
|
67
|
+
value: id,
|
|
68
|
+
label: id,
|
|
69
|
+
...(vendor.enabled
|
|
70
|
+
? { hint: vendor.title }
|
|
71
|
+
: // Reason first: a long vendor title truncates, and the half worth
|
|
72
|
+
// keeping is the part that says why the row cannot be picked.
|
|
73
|
+
{ disabled: `not enabled for this organisation — ${vendor.title}` }),
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Run the menu.
|
|
78
|
+
*
|
|
79
|
+
* Every question is skippable in the sense that it has a preselected answer;
|
|
80
|
+
* none is skippable in the sense of being hidden. A capability the repository
|
|
81
|
+
* already covers is still shown — deselected, with the tool that covers it
|
|
82
|
+
* named — because an operator who cannot see a decision cannot overrule it,
|
|
83
|
+
* and this repository's detection is wrong often enough that overruling has to
|
|
84
|
+
* stay one keystroke away.
|
|
85
|
+
*/
|
|
86
|
+
export async function runWizard(p, facts) {
|
|
87
|
+
const { manifest, survey, recorded } = facts;
|
|
88
|
+
const defaultOwner = facts.defaultOwner ?? 'the platform team';
|
|
89
|
+
p.intro('Redline');
|
|
90
|
+
// Multi-select, because a repository is routinely more than one bundle: a
|
|
91
|
+
// React application with its own Terraform beside it is `web,infra`, and a
|
|
92
|
+
// single-choice question made it pick the half that fitted worst.
|
|
93
|
+
//
|
|
94
|
+
// `.redline.json` still records one string — resolveProfile takes the list
|
|
95
|
+
// and sorts it — so nothing downstream had to learn a new shape.
|
|
96
|
+
// Detection never ticks a row. Which standards a repository is held to is the
|
|
97
|
+
// one answer here that changes what every future pull request is judged
|
|
98
|
+
// against, and a ticked row is consent — an operator pressing enter through
|
|
99
|
+
// the menu has agreed to whatever was ticked, whether or not they read it.
|
|
100
|
+
// Detection earns a label on the row and nothing more.
|
|
101
|
+
//
|
|
102
|
+
// What the repository already recorded is different: that is a decision a
|
|
103
|
+
// person made here before, and re-asking for it from scratch on every run
|
|
104
|
+
// would be its own kind of rude.
|
|
105
|
+
const recordedProfiles = recorded?.profile?.split(',').map((s) => s.trim());
|
|
106
|
+
let profiles = await p.multiselect('Which standards apply here?', profileChoices(manifest, facts.detectedProfile, facts.detectedConfidence), recordedProfiles ?? []);
|
|
107
|
+
// And if nothing is chosen, ask once more rather than choose something.
|
|
108
|
+
// Falling back to the detected profile is how "I did not pick anything"
|
|
109
|
+
// quietly became "Redline picked for me", which is the whole complaint.
|
|
110
|
+
//
|
|
111
|
+
// Once, not until-they-comply: a loop here never ends against a caller that
|
|
112
|
+
// keeps answering nothing, and a prompt that cannot be escaped is worse than
|
|
113
|
+
// one that gives up and says how to pass the answer as a flag.
|
|
114
|
+
if (profiles.length === 0) {
|
|
115
|
+
p.note('Nothing selected — Redline needs at least one profile to know which rules apply here. ' +
|
|
116
|
+
`The checkout looks like ${facts.detectedProfile}; take that, or take another.`);
|
|
117
|
+
profiles = await p.multiselect('Which standards apply here?', profileChoices(manifest, facts.detectedProfile, facts.detectedConfidence), []);
|
|
118
|
+
}
|
|
119
|
+
if (profiles.length === 0) {
|
|
120
|
+
throw new RedlineError('usage', 'no profile chosen, so there are no rules to render', `pick one from the menu, or name it outright: redline init --profile ${facts.detectedProfile}`);
|
|
121
|
+
}
|
|
122
|
+
const profile = profiles.join(',');
|
|
123
|
+
const hostChoices = [
|
|
124
|
+
{
|
|
125
|
+
value: 'github',
|
|
126
|
+
label: 'GitHub',
|
|
127
|
+
hint: 'GitHub.com or GitHub Enterprise Server' + (facts.detectedHost === 'github' ? ' (detected)' : ''),
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
value: 'azure',
|
|
131
|
+
label: 'Azure DevOps',
|
|
132
|
+
// Deliberately no hostnames: cli/platforms/__tests__/boundary.test.ts
|
|
133
|
+
// holds the line that only an adapter knows what a host's URLs look like,
|
|
134
|
+
// and a menu hint is not an adapter.
|
|
135
|
+
hint: 'Azure Repos' + (facts.detectedHost === 'azure' ? ' (detected)' : ''),
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
const host = await p.select('Where does this repository live?', hostChoices, facts.detectedHost ?? 'github');
|
|
139
|
+
// Asked separately from the host because the two genuinely come apart: every
|
|
140
|
+
// VFUK repository is on github.com and built by Azure Pipelines. Deriving the
|
|
141
|
+
// pipeline from the host is what put a GitHub Actions caller into a repo
|
|
142
|
+
// whose real gate is `cicd/pre-merge.yaml`.
|
|
143
|
+
const pipelineChoices = [
|
|
144
|
+
{
|
|
145
|
+
value: 'github-actions',
|
|
146
|
+
label: 'GitHub Actions',
|
|
147
|
+
hint: 'a workflow under .github/workflows',
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
value: 'azure-pipelines',
|
|
151
|
+
label: 'Azure Pipelines',
|
|
152
|
+
hint: facts.existingPipeline === null
|
|
153
|
+
? 'a stage in an Azure pipeline definition'
|
|
154
|
+
: `a stage added to ${facts.existingPipeline} (detected)`,
|
|
155
|
+
},
|
|
156
|
+
];
|
|
157
|
+
const pipeline = await p.select('What runs your pull request checks?', pipelineChoices, facts.existingPipeline !== null ? 'azure-pipelines' : 'github-actions');
|
|
158
|
+
const vendors = await p.multiselect('Which assistants should read the standards?', vendorChoices(manifest), [...(recorded?.vendors ?? facts.detectedVendors)]);
|
|
159
|
+
const contexts = await p.multiselect('Extra context to render beside the rules', [
|
|
160
|
+
{
|
|
161
|
+
value: 'speckit',
|
|
162
|
+
label: 'Spec-driven development',
|
|
163
|
+
hint: 'how to work from a spec — dropped automatically if you already run Spec Kit',
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
value: 'tmf',
|
|
167
|
+
label: 'TM Forum',
|
|
168
|
+
hint: 'resource naming, @type, paging, error bodies. Only if you implement TMF APIs',
|
|
169
|
+
},
|
|
170
|
+
], ['speckit']);
|
|
171
|
+
// What Redline should set up, not what it noticed. A question whose only
|
|
172
|
+
// outcome was a sentence in the report earned nothing: the operator read a
|
|
173
|
+
// list of tools, ticked the ones they had, and nothing happened differently.
|
|
174
|
+
//
|
|
175
|
+
// Only controls Redline can install with no account and no token appear here,
|
|
176
|
+
// and only on a host where they mean something — on Azure DevOps the list is
|
|
177
|
+
// empty and the question does not get asked at all. What the repository
|
|
178
|
+
// already runs is still detected, and still stands Redline's own gate jobs
|
|
179
|
+
// down; it just does not need a question to do it.
|
|
180
|
+
const already = new Set(facts.recorded?.integrations?.length
|
|
181
|
+
? facts.recorded.integrations
|
|
182
|
+
: survey.tools.map((tool) => tool.id));
|
|
183
|
+
const setupChoices = facts.offerable.map((offer) => ({
|
|
184
|
+
value: offer.id,
|
|
185
|
+
label: offer.label,
|
|
186
|
+
...(already.has(offer.id)
|
|
187
|
+
? { disabled: `already here — ${offer.evidence ?? 'detected in this repository'}` }
|
|
188
|
+
: { hint: offer.hint }),
|
|
189
|
+
}));
|
|
190
|
+
const setup = setupChoices.length > 0
|
|
191
|
+
? await p.multiselect('What should Redline set up alongside its own checks?', setupChoices, [])
|
|
192
|
+
: [];
|
|
193
|
+
// The recorded list stays what the repository RUNS — detection plus anything
|
|
194
|
+
// stated with --integrations — and gains whatever was just installed, because
|
|
195
|
+
// after this run it does run it.
|
|
196
|
+
const integrations = [...new Set([...already, ...setup])];
|
|
197
|
+
// A capability a detected tool already covers starts deselected and says
|
|
198
|
+
// which tool. `standDown` is advisory: nothing here is decided for the
|
|
199
|
+
// operator, only defaulted.
|
|
200
|
+
const covered = new Map();
|
|
201
|
+
for (const id of integrations) {
|
|
202
|
+
const probe = TOOL_PROBES.find((candidate) => candidate.id === id);
|
|
203
|
+
if (probe === undefined || probe.standsDown === null)
|
|
204
|
+
continue;
|
|
205
|
+
covered.set(probe.standsDown, probe.label);
|
|
206
|
+
}
|
|
207
|
+
const capabilityChoices = Object.entries(CAPABILITY_HINTS).map(([id, hint]) => ({ value: id, label: id, hint }));
|
|
208
|
+
const capabilities = await p.multiselect('What should Redline install?', capabilityChoices, ['gate', 'merge-policy', 'labels']);
|
|
209
|
+
// Asked here, and asked blind: the wizard runs before the platform is
|
|
210
|
+
// resolved, so nothing yet knows whether this organisation publishes a gate.
|
|
211
|
+
// That is why it defaults to `org` and why the mid-run offer still exists —
|
|
212
|
+
// this question is the operator's preference, and the later one is the same
|
|
213
|
+
// question asked once the host has actually answered. An operator who already
|
|
214
|
+
// knows their organisation has nothing can say so here and never see it.
|
|
215
|
+
const gateSource = capabilities.includes('gate')
|
|
216
|
+
? await p.select('Where should the merge gate live?', [
|
|
217
|
+
{
|
|
218
|
+
value: 'org',
|
|
219
|
+
label: 'in the organisation',
|
|
220
|
+
hint: 'one shared workflow; a pull request cannot edit the gate that judges it',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
value: 'local',
|
|
224
|
+
label: 'in this repository',
|
|
225
|
+
hint: 'no organisation setup needed — weaker: a PR can edit its own gate',
|
|
226
|
+
},
|
|
227
|
+
], 'org')
|
|
228
|
+
: 'org';
|
|
229
|
+
// Asked only when the capability was taken. GitHub silently ignores an owner
|
|
230
|
+
// it cannot resolve, so a CODEOWNERS seeded with a team that does not exist
|
|
231
|
+
// reports as installed and enforces nothing — the one failure mode worth a
|
|
232
|
+
// question, and only for the operators who will actually get a file.
|
|
233
|
+
const reviewOwners = capabilities.includes('review-ownership')
|
|
234
|
+
? (await p.text('Who owns the paths Redline protects?', {
|
|
235
|
+
placeholder: `${defaultOwner} — the default, which may not exist here`,
|
|
236
|
+
hint: 'a team, a user or an email; several separated by commas. GitHub ignores an owner it cannot resolve',
|
|
237
|
+
}))
|
|
238
|
+
.split(',')
|
|
239
|
+
.map((owner) => owner.trim())
|
|
240
|
+
.filter((owner) => owner !== '')
|
|
241
|
+
: [];
|
|
242
|
+
if (covered.size > 0) {
|
|
243
|
+
const named = [...covered]
|
|
244
|
+
.map(([job, label]) => `${label} already covers ${STAND_DOWN_LABEL[job] ?? job}`)
|
|
245
|
+
.join('; ');
|
|
246
|
+
p.note(`${named}. Those gate jobs are stood down rather than run a second time — ` +
|
|
247
|
+
'the rest of the gate still runs.');
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
// The other half of that question, and the half that was missing: what
|
|
251
|
+
// happens when the answer is "none of these". Redline turns on what the
|
|
252
|
+
// host itself provides, and is plain that it does not install anybody
|
|
253
|
+
// else's scanner — offering to would be a promise it cannot keep.
|
|
254
|
+
p.note('Nothing ticked — Redline will switch on the host controls itself: secret scanning, ' +
|
|
255
|
+
'push protection and dependency alerts. It does not install third-party scanners ' +
|
|
256
|
+
'(SonarQube, Snyk, Mend). Add one when you want it, then re-run ' +
|
|
257
|
+
'`redline init --integrations <id>` and the gate stops duplicating what it covers.');
|
|
258
|
+
}
|
|
259
|
+
const rung = await p.select('How hard should the check bite?', RUNGS.map((value) => ({ value, label: value, hint: RUNG_HINTS[value] })), recorded?.rung ?? 'observe');
|
|
260
|
+
const action = await p.select('Ready?', [
|
|
261
|
+
{
|
|
262
|
+
value: 'dry-run',
|
|
263
|
+
label: 'Dry run',
|
|
264
|
+
hint: 'print the plan — writes nothing, contacts no host, needs no credential',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
value: 'no-commit',
|
|
268
|
+
label: 'Write the files only',
|
|
269
|
+
hint: 'writes into your working tree, uncommitted — no host changes, no pull request',
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
value: 'apply',
|
|
273
|
+
label: 'Apply',
|
|
274
|
+
hint: 'write the files and open a pull request on redline/onboard',
|
|
275
|
+
},
|
|
276
|
+
], 'dry-run');
|
|
277
|
+
return {
|
|
278
|
+
profile,
|
|
279
|
+
vendors,
|
|
280
|
+
host,
|
|
281
|
+
pipeline,
|
|
282
|
+
speckit: contexts.includes('speckit'),
|
|
283
|
+
tmf: contexts.includes('tmf'),
|
|
284
|
+
capabilities,
|
|
285
|
+
integrations,
|
|
286
|
+
setup,
|
|
287
|
+
reviewOwners,
|
|
288
|
+
rung,
|
|
289
|
+
gateSource,
|
|
290
|
+
action,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=wizard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wizard.js","sourceRoot":"","sources":["../../cli/ui/wizard.ts"],"names":[],"mappings":"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"}
|
package/dist/verify/remote.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { isRedlineError } from "../core/errors.js";
|
|
2
2
|
import { renderForTarget, managedPaths } from "../sync/render.js";
|
|
3
3
|
export const CALLER_WORKFLOW = '.github/workflows/redline.yml';
|
|
4
|
+
import { VENDORED_GATE_PATH, stampedVersion } from "../platforms/github/vendor.js";
|
|
5
|
+
export { VENDORED_GATE_PATH } from "../platforms/github/vendor.js";
|
|
4
6
|
// Verify a repository without a checkout.
|
|
5
7
|
//
|
|
6
8
|
// Every check that can be sourced from the host is, and every check that cannot
|
|
@@ -70,6 +72,37 @@ export async function verifyRemote(host, ref, opts) {
|
|
|
70
72
|
: machinery.publishes !== machinery.expected
|
|
71
73
|
? `${machinery.path} publishes ${machinery.publishes}, but the policy requires ${machinery.expected}`
|
|
72
74
|
: `publishes ${machinery.publishes}`);
|
|
75
|
+
// --- vendored gate ---------------------------------------------------------
|
|
76
|
+
// A gate referenced from the organisation updates itself: one merge there
|
|
77
|
+
// reaches every repository that points at it. A vendored one does not — it is
|
|
78
|
+
// a copy, and a copy is only as current as the run that wrote it. Nothing
|
|
79
|
+
// else in this report would notice a repository sitting three releases behind
|
|
80
|
+
// on the workflow that decides whether its pull requests can merge.
|
|
81
|
+
//
|
|
82
|
+
// Reported against the CLI running the check rather than against npm: this is
|
|
83
|
+
// the same comparison `redline init --repair` would act on, so a repository
|
|
84
|
+
// told it is behind can always be brought level by the binary that told it.
|
|
85
|
+
if (gateOwned && config.gateSource === 'local') {
|
|
86
|
+
const vendored = await host.readRemoteFile(ref, VENDORED_GATE_PATH);
|
|
87
|
+
const pin = vendored === null ? null : stampedVersion(vendored.content);
|
|
88
|
+
if (vendored === null) {
|
|
89
|
+
add('gate-vendored', false, `${VENDORED_GATE_PATH} is missing, but .redline.json says the gate is vendored here — ` +
|
|
90
|
+
'the caller workflow references a file that is not in this repository');
|
|
91
|
+
}
|
|
92
|
+
else if (pin === null || config.gateVersion === '') {
|
|
93
|
+
// One of the two sides has no version to compare. That is not drift, and
|
|
94
|
+
// reporting it as stale would file work against a repository whose gate
|
|
95
|
+
// may be perfectly current — a development build writes no stamp on
|
|
96
|
+
// purpose. Unknown, with the reason, is the honest answer.
|
|
97
|
+
add('gate-vendored', true, 'vendored, version not recorded — cannot tell current from stale here', true);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
add('gate-vendored', pin === opts.cliVersion, pin === opts.cliVersion
|
|
101
|
+
? `vendored, current with redlinegate ${pin}`
|
|
102
|
+
: `vendored at redlinegate ${pin}, but this check runs ${opts.cliVersion} — ` +
|
|
103
|
+
're-run redline init --repair in that repository to bring the gate level');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
73
106
|
// --- merge policy ----------------------------------------------------------
|
|
74
107
|
const policy = await host.readPolicy(ref);
|
|
75
108
|
if (!config.capabilities.mergePolicy) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../../cli/verify/remote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAInD,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../../cli/verify/remote.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "redlinegate",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Engineering control plane for AI-assisted development: standards, merge gates and evidence across GitHub and Azure DevOps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://redline-gate.vercel.app",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/moelzanaty3/redline.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/moelzanaty3/redline/issues"
|
|
14
|
+
},
|
|
15
|
+
"author": "Mohamed Elzanaty (https://github.com/moelzanaty3)",
|
|
8
16
|
"keywords": [
|
|
9
17
|
"code-review",
|
|
10
18
|
"ai-code-review",
|
|
@@ -28,6 +36,7 @@
|
|
|
28
36
|
"redlinegate": "dist/bin/redline.js"
|
|
29
37
|
},
|
|
30
38
|
"files": [
|
|
39
|
+
"README.md",
|
|
31
40
|
"LICENSE",
|
|
32
41
|
"dist/",
|
|
33
42
|
"scripts/",
|
|
@@ -41,6 +50,9 @@
|
|
|
41
50
|
"scripts": {
|
|
42
51
|
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
43
52
|
"prepack": "npm run build",
|
|
53
|
+
"stars": "node scripts/fetch-stars.mjs",
|
|
54
|
+
"publish:local": "node scripts/publish-local.mjs",
|
|
55
|
+
"publish:local:clean": "npm rm -g redlinegate",
|
|
44
56
|
"test": "node --test 'cli/**/__tests__/*.test.ts' 'scripts/**/__tests__/*.test.mjs'",
|
|
45
57
|
"typecheck": "tsc --noEmit"
|
|
46
58
|
},
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Managed by Redline; regenerate with `redline init` rather than editing here.
|
|
2
|
+
# Redline merge gate for a GitHub-hosted repository built by Azure Pipelines.
|
|
3
|
+
#
|
|
4
|
+
# This is the combination the estate actually runs: the code lives on GitHub,
|
|
5
|
+
# the pull request checks are Azure Pipelines. Neither of Redline's two original
|
|
6
|
+
# paths fitted it — the GitHub path writes an Actions workflow the repository
|
|
7
|
+
# never runs, and the Azure path installs a Build Validation policy against an
|
|
8
|
+
# Azure Repos repository that does not exist here.
|
|
9
|
+
#
|
|
10
|
+
# CONTRACT: this pipeline reports to GitHub as a check named after its own
|
|
11
|
+
# pipeline definition, published by the Azure Pipelines GitHub App. There is no
|
|
12
|
+
# status POST here, unlike the Azure Repos template beside it: on a GitHub-hosted
|
|
13
|
+
# repository the build result IS the check, and posting to the Azure Repos
|
|
14
|
+
# statuses API would address a pull request that does not exist. The name a
|
|
15
|
+
# branch ruleset must require is therefore the pipeline's name in Azure DevOps —
|
|
16
|
+
# `redline init` prints it, and `redline verify` reads the reported names back.
|
|
17
|
+
#
|
|
18
|
+
# A `pr:` block IS honoured here. Azure Pipelines ignores `pr:` triggers only
|
|
19
|
+
# for Azure *Repos*; for a GitHub-hosted repository they are the trigger
|
|
20
|
+
# mechanism, which is why the Azure Repos template beside this one has none.
|
|
21
|
+
|
|
22
|
+
pr:
|
|
23
|
+
branches:
|
|
24
|
+
include:
|
|
25
|
+
- main
|
|
26
|
+
- master
|
|
27
|
+
|
|
28
|
+
trigger: none
|
|
29
|
+
|
|
30
|
+
pool:
|
|
31
|
+
vmImage: ubuntu-latest
|
|
32
|
+
|
|
33
|
+
variables:
|
|
34
|
+
ADR_DIFF_THRESHOLD: 300
|
|
35
|
+
FAIL_ON_DEPENDENCY_SEVERITY: high
|
|
36
|
+
SOFT_FAIL_LABELS: redline-exempt,redline-sync
|
|
37
|
+
# Declared with a default so the final step can read it even when the scan
|
|
38
|
+
# passed and never set it. An undeclared Azure variable interpolates as the
|
|
39
|
+
# literal string "$(NAME)" rather than empty, which is the kind of quiet
|
|
40
|
+
# wrong answer this gate exists to prevent.
|
|
41
|
+
REDLINE_SECURITY_FAILED: 'false'
|
|
42
|
+
# Pinned by image digest, not by tag: a tag is mutable and this scans the diff
|
|
43
|
+
# of every pull request in the repository. The version in the trailing comment
|
|
44
|
+
# is what scripts/check-pins.mjs re-resolves the digest against, exactly as it
|
|
45
|
+
# does for the SHA-pinned actions on the GitHub side of the gate.
|
|
46
|
+
TRUFFLEHOG_IMAGE: trufflesecurity/trufflehog@sha256:deb2af10659a488a14d262a323addcde099d99827a1cf1dc4e93c17915c39f08 # 3.97.1
|
|
47
|
+
|
|
48
|
+
steps:
|
|
49
|
+
- checkout: self
|
|
50
|
+
fetchDepth: 0
|
|
51
|
+
|
|
52
|
+
- task: NodeTool@0
|
|
53
|
+
displayName: Node 22
|
|
54
|
+
inputs:
|
|
55
|
+
versionSpec: '22.x'
|
|
56
|
+
|
|
57
|
+
# No substitute in Mend or SonarQube: both scan a state of the tree, and a
|
|
58
|
+
# credential committed and reverted inside one pull request is still in the
|
|
59
|
+
# history the moment it merges. This scans the RANGE.
|
|
60
|
+
#
|
|
61
|
+
# `--results=verified` only: `unknown` produces false positives and this
|
|
62
|
+
# check cannot be waived by a label.
|
|
63
|
+
- script: |
|
|
64
|
+
set -euo pipefail
|
|
65
|
+
target="${SYSTEM_PULLREQUEST_TARGETBRANCH#refs/heads/}"
|
|
66
|
+
# The PR build checks out a merge commit, and the target branch is not
|
|
67
|
+
# guaranteed to be a local ref on a fresh agent. Fetch it by name before
|
|
68
|
+
# asking for a merge base, or `git merge-base` fails and the scan silently
|
|
69
|
+
# degrades to scanning nothing.
|
|
70
|
+
git fetch --no-tags origin "$target"
|
|
71
|
+
base=$(git merge-base FETCH_HEAD HEAD)
|
|
72
|
+
echo "scanning $base..HEAD"
|
|
73
|
+
|
|
74
|
+
# Recorded BEFORE the exit, because the exemption branch in the final step
|
|
75
|
+
# must never downgrade this one.
|
|
76
|
+
if ! docker run --rm -v "$(pwd):/repo" "$TRUFFLEHOG_IMAGE" \
|
|
77
|
+
git file:///repo --since-commit "$base" --results=verified --fail
|
|
78
|
+
then
|
|
79
|
+
echo "##vso[task.setvariable variable=REDLINE_SECURITY_FAILED]true"
|
|
80
|
+
exit 1
|
|
81
|
+
fi
|
|
82
|
+
displayName: Secret scan (diff)
|
|
83
|
+
name: secrets
|
|
84
|
+
env:
|
|
85
|
+
TRUFFLEHOG_IMAGE: $(TRUFFLEHOG_IMAGE)
|
|
86
|
+
|
|
87
|
+
# --package pins npx to the redlinegate package explicitly. `redline` alone
|
|
88
|
+
# names a different, unrelated package on the public registry.
|
|
89
|
+
- script: npx --yes --package=redlinegate@latest redline verify --gate
|
|
90
|
+
displayName: Redline gate
|
|
91
|
+
name: gate
|
|
92
|
+
# A failed secret scan must not hide the configuration findings: the author
|
|
93
|
+
# deserves the whole list in one run rather than one blocker at a time.
|
|
94
|
+
condition: succeededOrFailed()
|
|
95
|
+
env:
|
|
96
|
+
GH_TOKEN: $(GH_TOKEN)
|
|
97
|
+
ADR_DIFF_THRESHOLD: $(ADR_DIFF_THRESHOLD)
|
|
98
|
+
FAIL_ON_DEPENDENCY_SEVERITY: $(FAIL_ON_DEPENDENCY_SEVERITY)
|
|
99
|
+
SOFT_FAIL_LABELS: $(SOFT_FAIL_LABELS)
|
|
100
|
+
|
|
101
|
+
- script: |
|
|
102
|
+
set -euo pipefail
|
|
103
|
+
if [ "$AGENT_JOBSTATUS" = "Succeeded" ]; then
|
|
104
|
+
echo "gate passed"
|
|
105
|
+
exit 0
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# The secret scan is never waivable, exactly as on the GitHub side where
|
|
109
|
+
# the security jobs sit outside label exemption. A label that could waive
|
|
110
|
+
# a verified credential in the diff would make the label the
|
|
111
|
+
# vulnerability.
|
|
112
|
+
if [ "${REDLINE_SECURITY_FAILED:-}" = "true" ]; then
|
|
113
|
+
echo "##vso[task.logissue type=error]The secret scan failed. This check cannot be waived by a label."
|
|
114
|
+
exit 1
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# Soft-fail escape hatch: a pull request carrying one of SOFT_FAIL_LABELS
|
|
118
|
+
# reports success instead of blocking, so a reviewer can accept a process
|
|
119
|
+
# failure deliberately. It can only ever turn a failure into a success.
|
|
120
|
+
#
|
|
121
|
+
# Fails CLOSED when no token is configured. Without GH_TOKEN the labels
|
|
122
|
+
# cannot be read, and "could not check for an exemption" must block rather
|
|
123
|
+
# than wave through — an unreadable exemption is not an exemption.
|
|
124
|
+
if [ -z "${GH_TOKEN:-}" ] || [ -z "${SOFT_FAIL_LABELS:-}" ]; then
|
|
125
|
+
echo "##vso[task.logissue type=error]Redline gate failed."
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
pr="${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER:-}"
|
|
130
|
+
repo="${BUILD_REPOSITORY_NAME:-}"
|
|
131
|
+
# The token must never appear in argv — visible via `ps` to any process on
|
|
132
|
+
# the agent. It is already an env var; hand it to curl through a stdin
|
|
133
|
+
# config instead of putting it on the command line with -H.
|
|
134
|
+
labels=$(printf 'header = "Authorization: Bearer %s"\n' "$GH_TOKEN" \
|
|
135
|
+
| curl -K - -sS -H "Accept: application/vnd.github+json" \
|
|
136
|
+
"https://api.github.com/repos/${repo}/issues/${pr}/labels") || labels=''
|
|
137
|
+
|
|
138
|
+
for label in ${SOFT_FAIL_LABELS//,/ }; do
|
|
139
|
+
if printf '%s' "$labels" | jq -e --arg l "$label" '[.[]?.name] | index($l)' >/dev/null 2>&1; then
|
|
140
|
+
echo "##vso[task.logissue type=warning]Redline gate failed but the \"$label\" label is applied — a reviewer accepted this deliberately"
|
|
141
|
+
exit 0
|
|
142
|
+
fi
|
|
143
|
+
done
|
|
144
|
+
|
|
145
|
+
echo "##vso[task.logissue type=error]Redline gate failed."
|
|
146
|
+
exit 1
|
|
147
|
+
displayName: Redline gate verdict
|
|
148
|
+
condition: always()
|
|
149
|
+
env:
|
|
150
|
+
GH_TOKEN: $(GH_TOKEN)
|
|
151
|
+
SOFT_FAIL_LABELS: $(SOFT_FAIL_LABELS)
|
|
152
|
+
REDLINE_SECURITY_FAILED: $(REDLINE_SECURITY_FAILED)
|