rvlib-shipkit 2.1.0 → 2.1.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/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/cli/cmd_CacheAudit.ts +3 -2
- package/src/cli/cmd_Ci.ts +4 -3
- package/src/cli/cmd_Fleet.ts +3 -1
- package/src/cli/cmd_GlobalProcesses.ts +7 -2
- package/src/cli/list-flag.ts +3 -0
- package/src/mv/mv-md.ts +2 -1
- package/src/ts/rename.ts +9 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## 2.1.1
|
|
2
|
+
|
|
3
|
+
- **`--only a,b` takes a comma list** in `sk ci`, `sk cache-audit` and `sk fleet`, the same as repeating the flag.
|
|
4
|
+
- **`sk gp start` waits for pm to act.** The state the switch leaves (down for a start, up for a stop) counts as pm not there yet for 30 s, so a process that takes a few seconds to launch no longer reads as down. A crash still fails at once.
|
|
5
|
+
- **`sk mv` rewrites relative markdown links on a folder move**, through the same pass the page move uses, and a page move no longer crashes on a repo with a git submodule.
|
|
6
|
+
|
|
1
7
|
## 2.1.0
|
|
2
8
|
|
|
3
9
|
- **SK027 requires exhaustive agent/ indexes.** Every `agent/` page is listed by an index (a bullet, numbered item, table row, `→` pointer or `@` include names it or its folder), and every index is loaded from CLAUDE.md or listed by a loaded file. A page linked only from prose now fails, with the page that already names it as the fix. The template's house `[lint]` block makes `typescript/no-empty-object-type` an error.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rvlib-shipkit",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"funding": "https://github.com/sponsors/rvion",
|
|
5
5
|
"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",
|
|
6
6
|
"license": "MIT",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { Command, Option } from 'clipanion'
|
|
5
5
|
import { loadRepoConfig } from '../repo-config.ts'
|
|
6
6
|
import { auditCache } from '../core/cache-audit.ts'
|
|
7
|
+
import { listFlag } from './list-flag.ts'
|
|
7
8
|
|
|
8
9
|
export class CacheAuditCommand extends Command {
|
|
9
10
|
static override paths = [['cache-audit']]
|
|
@@ -28,9 +29,9 @@ export class CacheAuditCommand extends Command {
|
|
|
28
29
|
const root = process.cwd()
|
|
29
30
|
const n = Number(this.sample)
|
|
30
31
|
if (!Number.isInteger(n) || n < 1) { this.context.stdout.write(`FAIL: --sample takes a whole number above 0, got '${this.sample}'\n`); return 1 }
|
|
31
|
-
const r = await auditCache(root, await loadRepoConfig(root), { sample: n, only: this.only })
|
|
32
|
+
const r = await auditCache(root, await loadRepoConfig(root), { sample: n, only: this.only == null ? undefined : listFlag(this.only) })
|
|
32
33
|
if (this.json) { this.context.stdout.write(`${JSON.stringify(r)}\n`); return r.stale.length > 0 ? 1 : 0 }
|
|
33
|
-
if (r.rows.length === 0) { this.context.stdout.write(`skip: nothing to audit, the cache answers none of ${this.only
|
|
34
|
+
if (r.rows.length === 0) { this.context.stdout.write(`skip: nothing to audit, the cache answers none of ${this.only == null ? 'the checks' : listFlag(this.only).join(', ')} right now\n`); return 0 }
|
|
34
35
|
for (const row of r.rows) {
|
|
35
36
|
const same = row.fresh === row.cached || row.fresh === 'skip'
|
|
36
37
|
this.context.stdout.write(same
|
package/src/cli/cmd_Ci.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { runJob } from '../core/job-run.ts'
|
|
|
7
7
|
import { runCi } from '../core/run-ci.ts'
|
|
8
8
|
import { runViaHub } from '../home/via-hub.ts'
|
|
9
9
|
import { maybeSelfUpdateAndRestart } from '../core/self-update.ts'
|
|
10
|
+
import { listFlag } from './list-flag.ts'
|
|
10
11
|
|
|
11
12
|
export class CiCommand extends Command {
|
|
12
13
|
static override paths = [['ci']]
|
|
@@ -37,7 +38,7 @@ export class CiCommand extends Command {
|
|
|
37
38
|
frozen = Option.Boolean('--frozen', false, { description: 'report-only mode for self-maintaining checks: SK005 never touches package.json, and generated paths are never untracked. STATUS.md is still refreshed on disk, but it is gitignored, so a run leaves the tree clean' })
|
|
38
39
|
skip = Option.Array('--skip', [], { description: 'skip a check group (repeatable)' })
|
|
39
40
|
withIds = Option.Array('--with', [], { description: 'include a check the quick gate leaves out (repeatable)' })
|
|
40
|
-
only = Option.Array('--only', [], { description: 'run only this check + its dep closure (repeatable). A group name runs every check declaring that group. No repair pass unless --fix is named' })
|
|
41
|
+
only = Option.Array('--only', [], { description: 'run only this check + its dep closure (repeatable, or comma separated). A group name runs every check declaring that group. No repair pass unless --fix is named' })
|
|
41
42
|
fresh = Option.Boolean('--fresh', false, { description: 'run every check: no answer comes from the cache, and what this run finds is stored fresh' })
|
|
42
43
|
full = Option.Boolean('--full', false, { description: `run the FULL test tier: every suite the repo declares, the slow ones it skips while you work included. What the release path always runs. Same as SHIPKIT_TEST_FULL=1` })
|
|
43
44
|
|
|
@@ -74,7 +75,7 @@ export class CiCommand extends Command {
|
|
|
74
75
|
...(this.frozen ? ['--frozen'] : []),
|
|
75
76
|
...this.skip.flatMap((v) => ['--skip', v]),
|
|
76
77
|
...this.withIds.flatMap((v) => ['--with', v]),
|
|
77
|
-
...this.only.flatMap((v) => ['--only', v]),
|
|
78
|
+
...listFlag(this.only).flatMap((v) => ['--only', v]),
|
|
78
79
|
...(this.fresh ? ['--fresh'] : []),
|
|
79
80
|
...(this.full ? ['--full'] : []),
|
|
80
81
|
...(this.verbose ? ['--verbose'] : []),
|
|
@@ -95,7 +96,7 @@ export class CiCommand extends Command {
|
|
|
95
96
|
frozen: this.frozen,
|
|
96
97
|
skipGroups: this.skip,
|
|
97
98
|
withIds: this.withIds,
|
|
98
|
-
onlyIds: this.only,
|
|
99
|
+
onlyIds: listFlag(this.only),
|
|
99
100
|
fresh: this.fresh,
|
|
100
101
|
...(this.full ? { tests: 'full' as const } : {}),
|
|
101
102
|
...(this.verbose ? { verbose: true } : {}),
|
package/src/cli/cmd_Fleet.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Command, Option } from 'clipanion'
|
|
|
6
6
|
import { join } from 'path'
|
|
7
7
|
import { npmLatest, resolveLatest, scanFleet, type FleetRepo } from '../fleet/scan-fleet.ts'
|
|
8
8
|
import { short, updateFleet } from '../fleet/update-fleet.ts'
|
|
9
|
+
import { listFlag } from './list-flag.ts'
|
|
9
10
|
|
|
10
11
|
export class FleetCommand extends Command {
|
|
11
12
|
static override paths = [['fleet']]
|
|
@@ -32,7 +33,8 @@ export class FleetCommand extends Command {
|
|
|
32
33
|
async execute(): Promise<number> {
|
|
33
34
|
const root = this.root ?? join(process.cwd(), '..')
|
|
34
35
|
let repos = scanFleet(root)
|
|
35
|
-
|
|
36
|
+
const only = listFlag(this.only)
|
|
37
|
+
if (only.length > 0) repos = repos.filter((r) => only.includes(r.name))
|
|
36
38
|
const w = (l: string) => this.context.stdout.write(`${l}\n`)
|
|
37
39
|
if (repos.length === 0) {
|
|
38
40
|
w(`skip: no repo under ${root} depends on an rvlib-* package`)
|
|
@@ -172,6 +172,8 @@ export type SwitchDeps = {
|
|
|
172
172
|
/** the longest a switch is followed: a bring-up polls its health for minutes */
|
|
173
173
|
export const SWITCH_WAIT_MS = 10 * 60_000
|
|
174
174
|
const SWITCH_POLL_MS = 1_000
|
|
175
|
+
/** how long the state the switch left counts as pm not having acted yet */
|
|
176
|
+
export const SWITCH_GRACE_MS = 30_000
|
|
175
177
|
|
|
176
178
|
/** flip a global process through pm, then follow pm's state until its probe
|
|
177
179
|
* settles it: 0 once it is up (or down for stop), 1 otherwise. The launcher's
|
|
@@ -183,7 +185,8 @@ export async function switchProcess(id: string, act: SwitchAct, d: SwitchDeps, w
|
|
|
183
185
|
return 1
|
|
184
186
|
}
|
|
185
187
|
const want = act === 'stop' ? 'down' : 'up'
|
|
186
|
-
const
|
|
188
|
+
const start = d.now()
|
|
189
|
+
const end = start + waitMs
|
|
187
190
|
let said = ''
|
|
188
191
|
for (;;) {
|
|
189
192
|
const v = await d.view(id)
|
|
@@ -195,7 +198,9 @@ export async function switchProcess(id: string, act: SwitchAct, d: SwitchDeps, w
|
|
|
195
198
|
said = v.status
|
|
196
199
|
d.log(`${id}: ${v.status}`)
|
|
197
200
|
}
|
|
198
|
-
|
|
201
|
+
// the state it is leaving, read just after the switch, is pm not there yet
|
|
202
|
+
const leaving = v.status === (want === 'up' ? 'down' : 'up') && d.now() - start < SWITCH_GRACE_MS
|
|
203
|
+
const moving = v.status === 'starting' || v.status === 'stopping' || v.want !== want || leaving
|
|
199
204
|
if (!moving) {
|
|
200
205
|
if (v.status === want) return 0
|
|
201
206
|
d.log(`FAIL ${id} is ${v.status}, not ${want}${v.note == null ? '' : `: ${v.note}`}`)
|
package/src/mv/mv-md.ts
CHANGED
|
@@ -154,7 +154,8 @@ export function movePage(root: string, oldRel: string, newRel: string, io: MvIo,
|
|
|
154
154
|
let touched = 0
|
|
155
155
|
for (const f of tracked) {
|
|
156
156
|
const p = join(root, f)
|
|
157
|
-
|
|
157
|
+
// a submodule is one ls-files entry and a directory: nothing to read
|
|
158
|
+
if (!isFile(p)) continue
|
|
158
159
|
const before = readFileSync(p, 'utf8')
|
|
159
160
|
let after = retargetRelativeRefs(root, before, dirOf(f), dirOf(moves.get(f) ?? f), moves)
|
|
160
161
|
for (const [oldTok, newTok] of pairs) after = after.replace(bound(oldTok), newTok)
|
package/src/ts/rename.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { basename, dirname, extname, join, relative, resolve } from 'path'
|
|
|
16
16
|
import { canonicalSpecifier, isSkipped, loadConfig, toPosix, type RvtConfig } from 'rvlib-shipkit/src/ts/config.ts'
|
|
17
17
|
import { extractSpecs } from 'rvlib-shipkit/src/ts/source-graph.ts'
|
|
18
18
|
import { gitBin } from '../core/git-bin.ts'
|
|
19
|
+
import { retargetRelativeRefs } from '../mv/mv-md.ts'
|
|
19
20
|
|
|
20
21
|
const CODE_RE = /\.[cm]?[jt]sx?$/
|
|
21
22
|
|
|
@@ -106,7 +107,15 @@ export function runRename(srcArg: string, dstArg: string, opts: { apply?: boolea
|
|
|
106
107
|
const isFile = (rel: string): boolean => {
|
|
107
108
|
try { return statSync(join(repoRoot, rel)).isFile() } catch { return false }
|
|
108
109
|
}
|
|
110
|
+
// a markdown link is relative to its page: the md engine's pass retargets it
|
|
111
|
+
const mdMoves = new Map(allFiles.flatMap((f): [string, string][] => { const to = movedTo(f); return to == null ? [] : [[f, to]] }))
|
|
112
|
+
const dirOf = (p: string): string => { const d = dirname(p); return d === '.' ? '' : d }
|
|
109
113
|
const rewriteFile = (file: string, content: string): { text: string; count: number } => {
|
|
114
|
+
if (file.endsWith('.md')) {
|
|
115
|
+
const linked = retargetRelativeRefs(repoRoot, content, dirOf(file), dirOf(movedTo(file) ?? file), mdMoves)
|
|
116
|
+
const scanned = scanPairs(linked, pairs)
|
|
117
|
+
return { text: scanned.text, count: scanned.count + (linked === content ? 0 : 1) }
|
|
118
|
+
}
|
|
110
119
|
const rel = CODE_RE.test(file) ? relativeRewrites(file, content, movedTo, isFile) : new Map<string, string>()
|
|
111
120
|
if (rel.size === 0) return scanPairs(content, pairs)
|
|
112
121
|
// the relative specifiers this move retargets are set aside while the
|