rvlib-shipkit 0.17.1 → 0.17.2
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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/core/run-check.ts +10 -1
- package/src/core/run-ci.ts +11 -5
- package/src/release/run-release.ts +11 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.17.2
|
|
4
|
+
|
|
5
|
+
- **A release no longer probes the world.** The release gate runs only the checks that can BLOCK. A check declaring `blocking: false` cannot change the outcome, so running it spent time and a third party API call per publish to learn whether a DNS provider was reachable. `release.requireHealthy` still makes those failures fatal, and then they run.
|
|
6
|
+
- **The release gate stopped busting the check caches.** 0.17.0's gate ran fresh, so every cached probe refired on every publish. Caching exists to rate-limit exactly those calls.
|
|
7
|
+
- **New `release.skipTasks`.** Names the repo-wide tasks a release does not need, for a monorepo whose commit gate runs every package's suite. Releasing one unit already runs that unit's own tests and typecheck, and the other packages are not in its tarball.
|
|
8
|
+
|
|
9
|
+
|
|
3
10
|
## 0.17.1
|
|
4
11
|
|
|
5
12
|
- **Fixed: `shipkit ci`'s fix pass ignored `defaults: { 'imports:check': false }`** and rewrote every relative import anyway. Reported from a repo that deliberately keeps relative imports: one plain `shipkit ci` rewrote 215 files with no tsconfig paths wired, so typecheck and the tests went red on the gate's own repair. A fix slot repairs the same concern its floor task checks, so one disable governs both directions now.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rvlib-shipkit",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.2",
|
|
4
4
|
"description": "All-in-one repo companion: ci gate (task DAG), auto-discovered health checks, release-contract fulfillness, asks queue, md-wiki tools, asset atelier, live tui",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/core/run-check.ts
CHANGED
|
@@ -36,6 +36,12 @@ export type CheckOpts = {
|
|
|
36
36
|
* decides severity and the engine that runs the rules must read the same
|
|
37
37
|
* object, or a blocking rule failure silently reclassifies as a probe */
|
|
38
38
|
config?: RepoConfig
|
|
39
|
+
/** run only the checks that can BLOCK. A check declaring blocking:false
|
|
40
|
+
* cannot change the outcome, so running it is pure cost: a release does
|
|
41
|
+
* not need to know whether a DNS provider is reachable, and probing one
|
|
42
|
+
* spends a third-party API call per publish. Undeclared checks are still
|
|
43
|
+
* collected, because an unclassified check stays fatal. */
|
|
44
|
+
onlyBlocking?: boolean
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
export type CheckRun = {
|
|
@@ -78,13 +84,15 @@ export async function collectChecks(root: string, o: CheckOpts = {}): Promise<Ch
|
|
|
78
84
|
t.failed = false
|
|
79
85
|
t.warned = true
|
|
80
86
|
}
|
|
81
|
-
for (const rule of [...RULES, ...(c.rules ?? [])])
|
|
87
|
+
for (const rule of [...RULES, ...(c.rules ?? [])]) {
|
|
88
|
+
if (o.onlyBlocking === true && !rule.blocking) continue
|
|
82
89
|
suite.addCheck({
|
|
83
90
|
name: rule.title,
|
|
84
91
|
remainValidFor: rule.ttl,
|
|
85
92
|
cacheExtra: rule.cacheKey != null ? () => rule.cacheKey!({ root, config: c }) : undefined,
|
|
86
93
|
logic: async (t) => { await runRuleLint(rule, t, { root, config: c }); demote(t, rule.blocking, rule.title) },
|
|
87
94
|
})
|
|
95
|
+
}
|
|
88
96
|
const childEnv: Record<string, string | undefined> = {
|
|
89
97
|
...process.env,
|
|
90
98
|
...(o.fresh === true ? { SHIPKIT_FRESH: '1', RV_CHECK_FRESH: '1' } : {}),
|
|
@@ -95,6 +103,7 @@ export async function collectChecks(root: string, o: CheckOpts = {}): Promise<Ch
|
|
|
95
103
|
// check is an error, never a guess
|
|
96
104
|
const blocking = d.blocking ?? c.checkDefaults?.blocking
|
|
97
105
|
if (blocking == null) undeclared.push(rel)
|
|
106
|
+
if (o.onlyBlocking === true && blocking !== true) continue
|
|
98
107
|
suite.addCheck({
|
|
99
108
|
name: d.name,
|
|
100
109
|
remainValidFor: d.selfCached ? undefined : DISCOVERED_TTL,
|
package/src/core/run-ci.ts
CHANGED
|
@@ -27,9 +27,11 @@ export type CiOpts = {
|
|
|
27
27
|
fix?: boolean
|
|
28
28
|
/** also apply the structural SK rule fixers during the fix pass */
|
|
29
29
|
fixRules?: boolean
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
/** run only the checks that can block (core/run-check onlyBlocking) */
|
|
31
|
+
onlyBlockingChecks?: boolean
|
|
32
|
+
/** task ids the caller does not need. A sub-unit release skips the tasks
|
|
33
|
+
* that gate the whole repo, since the unit's own gate already ran. */
|
|
34
|
+
skipTaskIds?: string[]
|
|
33
35
|
/** report-only for self-maintaining rules and checks (SHIPKIT_FROZEN
|
|
34
36
|
* travels to child checks). The release path passes it: a rule that
|
|
35
37
|
* rewrites package.json between the gate and `npm publish` packs a tree
|
|
@@ -76,7 +78,11 @@ export async function runCi(root: string, o: CiOpts = {}): Promise<number> {
|
|
|
76
78
|
if (o.fix !== false)
|
|
77
79
|
fixFailed = (await runFixPass(root, c, { rules: o.fixRules !== false, print: sink })).failed
|
|
78
80
|
else sink('skip: fix pass (--no-fix)')
|
|
79
|
-
const
|
|
81
|
+
const all = allTasks(c, root)
|
|
82
|
+
const { disabled, unfilled } = all
|
|
83
|
+
const skipIds = new Set(o.skipTaskIds ?? [])
|
|
84
|
+
const tasks = all.tasks.filter((t) => !skipIds.has(t.id))
|
|
85
|
+
for (const t of all.tasks) if (skipIds.has(t.id)) sink(`skip: ${t.id} (not part of this release, the unit's own gate covers it)`)
|
|
80
86
|
for (const d of disabled) sink(`skip: ${d} (skipped-by-config)`)
|
|
81
87
|
// a floor slot nothing could fill is VISIBLE — a thin gate must never
|
|
82
88
|
// look like a full one (the fresh-repo floor gap)
|
|
@@ -96,7 +102,7 @@ export async function runCi(root: string, o: CiOpts = {}): Promise<number> {
|
|
|
96
102
|
// declares whether it blocks, and that is the only question about it.
|
|
97
103
|
const checks = o.skipChecks === true
|
|
98
104
|
? { lines: [], blocked: false, advisoryFailures: [], undeclared: [] }
|
|
99
|
-
: await collectChecks(root, { status: false, config: c, frozen: o.frozen,
|
|
105
|
+
: await collectChecks(root, { status: false, config: c, frozen: o.frozen, onlyBlocking: o.onlyBlockingChecks })
|
|
100
106
|
const color = process.stdout.isTTY && process.env.NO_COLOR == null
|
|
101
107
|
for (const l of checks.lines) { collected.push(formatLine(l, false)); print(formatLine(l, color)) }
|
|
102
108
|
for (const u of checks.undeclared) {
|
|
@@ -39,6 +39,11 @@ export type ReleaseConfig = {
|
|
|
39
39
|
/** repo-relative tsconfig for the ROOT unit's own typecheck (default
|
|
40
40
|
* '<root>/tsconfig.json' when it exists) */
|
|
41
41
|
tsconfig?: string
|
|
42
|
+
/** task ids the release gate skips. A monorepo's repo-wide task (every
|
|
43
|
+
* package's suite, a full matrix build) re-proves for six units what the
|
|
44
|
+
* released unit's own gate already proved for one. Naming them here is
|
|
45
|
+
* the repo saying which tasks are about the REPO rather than the release. */
|
|
46
|
+
skipTasks?: string[]
|
|
42
47
|
/** `shipkit bump` run from a feature branch merges it into the release
|
|
43
48
|
* branch first, with a merge commit. Default true. A branch is a GATE, so
|
|
44
49
|
* this is deliberately reachable ONLY by running bump by hand. */
|
|
@@ -92,11 +97,13 @@ export async function runReleaseGate(root: string, config: RepoConfig, print: (l
|
|
|
92
97
|
fix: o.fix ?? false,
|
|
93
98
|
frozen: true,
|
|
94
99
|
status: false,
|
|
95
|
-
// a release must not gate on a clean pass cached hours ago: SK005's
|
|
96
|
-
// blocking branches are local facts (a link: that stopped resolving, a
|
|
97
|
-
// dep nobody installed) and a TTL replays them as green
|
|
98
|
-
fresh: true,
|
|
99
100
|
config,
|
|
101
|
+
// a check that cannot block cannot change the outcome, so a release
|
|
102
|
+
// never probes the world: publishing a lib does not depend on whether
|
|
103
|
+
// a DNS provider answers, and asking spends an API call per publish.
|
|
104
|
+
// requireHealthy is the repo saying it wants them fatal, so then they run.
|
|
105
|
+
onlyBlockingChecks: config.release?.requireHealthy !== true,
|
|
106
|
+
skipTaskIds: config.release?.skipTasks ?? [],
|
|
100
107
|
failOnAdvisory: config.release?.requireHealthy === true,
|
|
101
108
|
print: (l) => print(` ${l}`),
|
|
102
109
|
})
|