@arjunkhera/atlas 0.4.4 → 0.4.6
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/.claude-plugin/plugin.json +1 -1
- package/door/lib/gate.mjs +11 -3
- package/door/lib/upkeep.mjs +43 -1
- package/package.json +1 -1
- package/skills/sdlc-task/SKILL.md +3 -1
- package/skills/upkeep/SKILL.md +3 -1
package/door/lib/gate.mjs
CHANGED
|
@@ -206,9 +206,17 @@ export function readyCommand({ repo, pr, answer }, line, gh = ghApi) {
|
|
|
206
206
|
const rows = checkStates(checks, checkRuns(gh, repo, sha));
|
|
207
207
|
line(`atlas gate ready: #${pr} at ${sha.slice(0, 7)}, ${checks.length} required check(s) from atlas.yaml on ${pull.base.ref}.`);
|
|
208
208
|
for (const row of rows) line(checkLine(row));
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
209
|
+
// Checks that ran on an old base prove nothing about the merge: two pull
|
|
210
|
+
// requests, each green alone, can turn the main branch red together (atlas#188).
|
|
211
|
+
const behind = Number(gh('GET', `repos/${repo}/compare/${encodeURIComponent(pull.base.ref)}...${sha}`)?.behind_by ?? NaN);
|
|
212
|
+
if (!(behind >= 0)) throw new GateError(`GitHub gave no compare of ${pull.base.ref} and ${sha.slice(0, 7)}.`);
|
|
213
|
+
line(` ${behind === 0 ? '✓' : '✗'} ${(behind === 0 ? 'current' : 'behind').padEnd(8)} ${behind === 0 ? `the head holds every commit of ${pull.base.ref}` : `${pull.base.ref} has ${behind} commit(s) that the head does not hold`}`);
|
|
214
|
+
const green = rows.every((row) => row.state === 'green');
|
|
215
|
+
if (green && behind === 0) { line('READY: every required check is green, on top of the current main branch. Merge with a merge commit, at this head.'); return 0; }
|
|
216
|
+
line(behind > 0
|
|
217
|
+
? `NOT READY: do not merge. Merge ${pull.base.ref} in, wait for green, then run ready again.`
|
|
218
|
+
: 'NOT READY: do not merge. Wait for a pending check; fix a red or missing one.');
|
|
219
|
+
return 1;
|
|
212
220
|
}
|
|
213
221
|
|
|
214
222
|
// The merge door hook (packages/atlas/hooks/door.mjs) reads this file before
|
package/door/lib/upkeep.mjs
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
// It reads the tree and the event file, and writes nothing.
|
|
18
18
|
import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
|
|
19
19
|
import { join, dirname, posix, resolve } from 'node:path';
|
|
20
|
+
import { listChanged } from './changed-tests.mjs';
|
|
20
21
|
|
|
21
22
|
const INDEX = 'docs/index.md';
|
|
22
23
|
const SKIP_DIRS = new Set(['node_modules', 'archive', 'fixtures']);
|
|
@@ -155,6 +156,41 @@ export function prTextFindings(body) {
|
|
|
155
156
|
return findings;
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
// The rows of the changed-test list that a person must name under "Changed
|
|
160
|
+
// tests": test cases and scenario words. A helper, a runner config or the kit
|
|
161
|
+
// manifest (whose version moves in each release) may be named, but need not be.
|
|
162
|
+
const NAMED_GROUPS = new Set(['unit-test', 'check-block', 'scenario-words']);
|
|
163
|
+
|
|
164
|
+
// "Changed tests" against the changed-test list of the pull request (roadmap
|
|
165
|
+
// R8 line 4, design docs/design-docs/pr-template/). The list comes from git,
|
|
166
|
+
// between the base and the head commits of the event. With no list, the
|
|
167
|
+
// section says "none"; with a list, it names the path of each row.
|
|
168
|
+
export function changedTestsFindings(body, rows) {
|
|
169
|
+
const section = prSections(body).get('changed tests');
|
|
170
|
+
if (section === undefined) return [];
|
|
171
|
+
const paths = [...new Set(rows.filter((row) => NAMED_GROUPS.has(row.group)).map((row) => row.path))].sort();
|
|
172
|
+
// An empty list needs a section that opens with the word None.
|
|
173
|
+
if (!paths.length) return /^\s*none\b/i.test(section) ? [] : ['pr text: "Changed tests" names tests, but the changed-test list is empty; write "None."'];
|
|
174
|
+
// A path counts only as a whole name: `src/a.test.mjs.old` does not name `src/a.test.mjs`.
|
|
175
|
+
const named = (path) => new RegExp(`(^|[^\\w./-])${path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}($|\\.(?![\\w/-])|[^\\w./-])`).test(section);
|
|
176
|
+
const missing = paths.filter((path) => !named(path));
|
|
177
|
+
if (!missing.length) return [];
|
|
178
|
+
return [`pr text: "Changed tests" does not name ${missing.length} changed test file(s): ${missing.slice(0, MAX_LISTED).join(', ')}`];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// The changed-test list of the event's pull request, or why there is none.
|
|
182
|
+
// A root that is no git work tree is not checked. A git tree that lacks a
|
|
183
|
+
// commit of the event is an error, because the check in CI fetches them all.
|
|
184
|
+
function changedRows(root, pr) {
|
|
185
|
+
const base = pr?.base?.sha;
|
|
186
|
+
const head = pr?.head?.sha;
|
|
187
|
+
if (!base || !head) return { checked: false, why: 'the event names no base and head commit' };
|
|
188
|
+
if (!existsSync(join(root, '.git'))) return { checked: false, why: 'the root is no git work tree' };
|
|
189
|
+
try { return { checked: true, rows: listChanged({ root, base, head }).rows }; } catch (error) {
|
|
190
|
+
return { checked: true, error: error.message };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
158
194
|
// Does the pull request name its tracker item? Read from a GitHub event file.
|
|
159
195
|
export function itemNamed(event) {
|
|
160
196
|
const pr = event?.pull_request;
|
|
@@ -188,7 +224,12 @@ export function upkeepCheck(root, { event = null } = {}) {
|
|
|
188
224
|
// The heading check holds a pull request that names an item, in a repo that
|
|
189
225
|
// keeps the Atlas template. A repo without the template is not held yet.
|
|
190
226
|
const text = { checked: Boolean(item.named) && existsSync(join(root, PR_TEMPLATE_PATH)) };
|
|
191
|
-
if (text.checked)
|
|
227
|
+
if (text.checked) {
|
|
228
|
+
findings.push(...prTextFindings(event.pull_request.body));
|
|
229
|
+
text.tests = changedRows(root, event.pull_request);
|
|
230
|
+
if (text.tests.error) findings.push(`pr text: the changed-test list could not be made: ${text.tests.error}`);
|
|
231
|
+
else if (text.tests.checked) findings.push(...changedTestsFindings(event.pull_request.body, text.tests.rows));
|
|
232
|
+
}
|
|
192
233
|
return { green: findings.length === 0, findings, index, dead, item, text };
|
|
193
234
|
}
|
|
194
235
|
|
|
@@ -204,6 +245,7 @@ export function upkeepCommand({ what, event: eventFile, json = false }, root, li
|
|
|
204
245
|
line(`dead links: ${result.dead.length}`);
|
|
205
246
|
line(`item: ${!result.item.checked ? 'not a pull request' : result.item.ok ? result.item.how : 'none named'}`);
|
|
206
247
|
line(`pr text: ${result.text.checked ? 'the six headings were read' : 'not checked'}`);
|
|
248
|
+
if (result.text.checked) line(`changed tests: ${result.text.tests?.checked ? (result.text.tests.error ? 'no list' : `${result.text.tests.rows.filter((row) => NAMED_GROUPS.has(row.group)).length} row(s) to name`) : `not checked (${result.text.tests?.why})`}`);
|
|
207
249
|
for (const finding of result.findings) line(` - ${finding}`);
|
|
208
250
|
line(`upkeep check: ${result.green ? 'GREEN' : 'RED'}`);
|
|
209
251
|
return result.green ? 0 : 1;
|
package/package.json
CHANGED
|
@@ -148,7 +148,9 @@ A lock is the owner's approval of a short design.
|
|
|
148
148
|
nothing, so this step is the gate (owner, 24 October 2026).
|
|
149
149
|
1. Right before the merge, run `atlas gate` with the verb `ready`,
|
|
150
150
|
`--repo` and `--pr`.
|
|
151
|
-
2. A pending check: wait for it. A red or missing check: fix it.
|
|
151
|
+
2. A pending check: wait for it. A red or missing check: fix it. A
|
|
152
|
+
branch behind the main branch: merge the main branch in, wait for
|
|
153
|
+
green, and run `ready` again.
|
|
152
154
|
3. Merge only when it prints READY. Name the head SHA that it read in
|
|
153
155
|
the merge call, so a later push cannot slip in.
|
|
154
156
|
4. Where the verb `apply` set the rulesets, the branch rule holds the
|
package/skills/upkeep/SKILL.md
CHANGED
|
@@ -97,7 +97,9 @@ If the tracker cannot be reached, skip this half and say so.
|
|
|
97
97
|
diff changes only index lines and link targets, run `atlas gate` with
|
|
98
98
|
the verb `ready`, `--repo` and `--pr`. On READY, merge it with a merge
|
|
99
99
|
commit at the head SHA that ready read. The merge door hook refuses any
|
|
100
|
-
other merge.
|
|
100
|
+
other merge. When the branch is behind the main branch, merge the main
|
|
101
|
+
branch in, wait for green, and run `ready` again. Otherwise leave it
|
|
102
|
+
open and say why in the report.
|
|
101
103
|
|
|
102
104
|
## The run line
|
|
103
105
|
|