@aperant/framework 0.6.6 → 0.7.0
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 +215 -0
- package/dist/cli/commands/check-version.d.mts.map +1 -1
- package/dist/cli/commands/check-version.mjs +76 -1
- package/dist/cli/commands/check-version.mjs.map +1 -1
- package/dist/cli/commands/ci-watch.d.mts.map +1 -1
- package/dist/cli/commands/ci-watch.mjs +73 -5
- package/dist/cli/commands/ci-watch.mjs.map +1 -1
- package/dist/cli/commands/health-check.mjs +1 -1
- package/dist/cli/commands/health-check.mjs.map +1 -1
- package/dist/cli/commands/init.d.mts +17 -1
- package/dist/cli/commands/init.d.mts.map +1 -1
- package/dist/cli/commands/init.mjs +183 -11
- package/dist/cli/commands/init.mjs.map +1 -1
- package/dist/cli/commands/release-notes.d.mts +11 -0
- package/dist/cli/commands/release-notes.d.mts.map +1 -0
- package/dist/cli/commands/release-notes.mjs +173 -0
- package/dist/cli/commands/release-notes.mjs.map +1 -0
- package/dist/cli/commands/route.d.mts.map +1 -1
- package/dist/cli/commands/route.mjs +37 -2
- package/dist/cli/commands/route.mjs.map +1 -1
- package/dist/cli/commands/task.d.mts.map +1 -1
- package/dist/cli/commands/task.mjs +48 -0
- package/dist/cli/commands/task.mjs.map +1 -1
- package/dist/cli/config/load.d.mts +14 -0
- package/dist/cli/config/load.d.mts.map +1 -1
- package/dist/cli/config/load.mjs +40 -0
- package/dist/cli/config/load.mjs.map +1 -1
- package/dist/cli/config/upgrade-gitignore.d.mts.map +1 -1
- package/dist/cli/config/upgrade-gitignore.mjs +6 -0
- package/dist/cli/config/upgrade-gitignore.mjs.map +1 -1
- package/dist/cli/consistency/parse-review.d.mts.map +1 -1
- package/dist/cli/consistency/parse-review.mjs +4 -1
- package/dist/cli/consistency/parse-review.mjs.map +1 -1
- package/dist/cli/dispatch.d.mts.map +1 -1
- package/dist/cli/dispatch.mjs +2 -0
- package/dist/cli/dispatch.mjs.map +1 -1
- package/dist/cli/gate/gates/review-clean.d.mts.map +1 -1
- package/dist/cli/gate/gates/review-clean.mjs +4 -2
- package/dist/cli/gate/gates/review-clean.mjs.map +1 -1
- package/dist/cli/help.d.mts.map +1 -1
- package/dist/cli/help.mjs +1 -0
- package/dist/cli/help.mjs.map +1 -1
- package/dist/cli/host/detect.d.mts +122 -9
- package/dist/cli/host/detect.d.mts.map +1 -1
- package/dist/cli/host/detect.mjs +132 -0
- package/dist/cli/host/detect.mjs.map +1 -1
- package/dist/cli/install/legacy-paths.d.mts +38 -0
- package/dist/cli/install/legacy-paths.d.mts.map +1 -0
- package/dist/cli/install/legacy-paths.mjs +69 -0
- package/dist/cli/install/legacy-paths.mjs.map +1 -0
- package/dist/cli/install/runtime-migrate.d.mts +84 -0
- package/dist/cli/install/runtime-migrate.d.mts.map +1 -0
- package/dist/cli/install/runtime-migrate.mjs +244 -0
- package/dist/cli/install/runtime-migrate.mjs.map +1 -0
- package/dist/cli/install/update-chips.d.mts +23 -0
- package/dist/cli/install/update-chips.d.mts.map +1 -1
- package/dist/cli/install/update-chips.mjs +60 -0
- package/dist/cli/install/update-chips.mjs.map +1 -1
- package/dist/cli/release-notes/compile.d.mts +38 -0
- package/dist/cli/release-notes/compile.d.mts.map +1 -0
- package/dist/cli/release-notes/compile.mjs +244 -0
- package/dist/cli/release-notes/compile.mjs.map +1 -0
- package/dist/cli/release-notes/draft.d.mts +73 -0
- package/dist/cli/release-notes/draft.d.mts.map +1 -0
- package/dist/cli/release-notes/draft.mjs +120 -0
- package/dist/cli/release-notes/draft.mjs.map +1 -0
- package/dist/cli/release-notes/output-dir.d.mts +20 -0
- package/dist/cli/release-notes/output-dir.d.mts.map +1 -0
- package/dist/cli/release-notes/output-dir.mjs +42 -0
- package/dist/cli/release-notes/output-dir.mjs.map +1 -0
- package/dist/cli/release-notes/persona-filter.d.mts +51 -0
- package/dist/cli/release-notes/persona-filter.d.mts.map +1 -0
- package/dist/cli/release-notes/persona-filter.mjs +127 -0
- package/dist/cli/release-notes/persona-filter.mjs.map +1 -0
- package/dist/cli/release-notes/publish.d.mts +23 -0
- package/dist/cli/release-notes/publish.d.mts.map +1 -0
- package/dist/cli/release-notes/publish.mjs +125 -0
- package/dist/cli/release-notes/publish.mjs.map +1 -0
- package/dist/cli/release-notes/ship-autodraft.d.mts +37 -0
- package/dist/cli/release-notes/ship-autodraft.d.mts.map +1 -0
- package/dist/cli/release-notes/ship-autodraft.mjs +97 -0
- package/dist/cli/release-notes/ship-autodraft.mjs.map +1 -0
- package/dist/cli/route/drift-detect.d.mts +20 -0
- package/dist/cli/route/drift-detect.d.mts.map +1 -0
- package/dist/cli/route/drift-detect.mjs +107 -0
- package/dist/cli/route/drift-detect.mjs.map +1 -0
- package/dist/cli/util/aperant-section.d.mts +34 -0
- package/dist/cli/util/aperant-section.d.mts.map +1 -0
- package/dist/cli/util/aperant-section.mjs +127 -0
- package/dist/cli/util/aperant-section.mjs.map +1 -0
- package/dist/cli/util/copy.d.mts +28 -1
- package/dist/cli/util/copy.d.mts.map +1 -1
- package/dist/cli/util/copy.mjs +43 -55
- package/dist/cli/util/copy.mjs.map +1 -1
- package/dist/cli/util/semver.d.mts +17 -0
- package/dist/cli/util/semver.d.mts.map +1 -0
- package/dist/cli/util/semver.mjs +29 -0
- package/dist/cli/util/semver.mjs.map +1 -0
- package/dist/cli/util/skill-installs.d.mts +65 -9
- package/dist/cli/util/skill-installs.d.mts.map +1 -1
- package/dist/cli/util/skill-installs.mjs +130 -21
- package/dist/cli/util/skill-installs.mjs.map +1 -1
- package/dist/cli/util/version-preflight.d.mts +44 -0
- package/dist/cli/util/version-preflight.d.mts.map +1 -0
- package/dist/cli/util/version-preflight.mjs +66 -0
- package/dist/cli/util/version-preflight.mjs.map +1 -0
- package/dist/types/config.d.ts +11 -7
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +133 -133
- package/skills/apt-close-task/SKILL.md +30 -0
- package/skills/apt-diagram/SKILL.md +45 -9
- package/skills/apt-release-notes/SKILL.md +193 -0
- package/skills/apt-release-notes/appendices/persona-voice.md +59 -0
- package/skills/apt-setup/SKILL.md +146 -3
- package/skills/apt-ship/SKILL.md +63 -4
- package/skills/apt-spar/SKILL.md +36 -11
- package/skills/apt-update/SKILL.md +77 -10
- package/skills/apt-watch-ci/SKILL.md +6 -3
- package/src/cli/commands/check-version.mjs +73 -1
- package/src/cli/commands/ci-watch.mjs +74 -5
- package/src/cli/commands/health-check.mjs +1 -1
- package/src/cli/commands/init.mjs +202 -10
- package/src/cli/commands/release-notes.mjs +187 -0
- package/src/cli/commands/route.mjs +38 -2
- package/src/cli/commands/task.mjs +49 -0
- package/src/cli/config/load.mjs +37 -0
- package/src/cli/config/upgrade-gitignore.mjs +6 -0
- package/src/cli/consistency/parse-review.mjs +3 -1
- package/src/cli/dispatch.mjs +2 -0
- package/src/cli/gate/gates/review-clean.mjs +4 -2
- package/src/cli/help.mjs +1 -0
- package/src/cli/host/detect.mjs +135 -0
- package/src/cli/install/legacy-paths.mjs +69 -0
- package/src/cli/install/runtime-migrate.mjs +252 -0
- package/src/cli/install/update-chips.mjs +57 -0
- package/src/cli/release-notes/compile.mjs +261 -0
- package/src/cli/release-notes/draft.mjs +133 -0
- package/src/cli/release-notes/output-dir.mjs +52 -0
- package/src/cli/release-notes/persona-filter.mjs +126 -0
- package/src/cli/release-notes/publish.mjs +128 -0
- package/src/cli/release-notes/ship-autodraft.mjs +106 -0
- package/src/cli/route/drift-detect.mjs +107 -0
- package/src/cli/util/aperant-section.mjs +136 -0
- package/src/cli/util/copy.mjs +43 -56
- package/src/cli/util/semver.mjs +28 -0
- package/src/cli/util/skill-installs.mjs +134 -21
- package/src/cli/util/version-preflight.mjs +65 -0
- package/templates/aperant-claude-md-appendix.md +37 -0
- package/templates/config.json +28 -3
- package/workflows/docs.md +12 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.mjs","sourceRoot":"","sources":["../../../src/cli/release-notes/compile.mjs"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the canonical fragment body. The shape is intentionally simple +
|
|
3
|
+
* stable so the compile step can parse it back without YAML / front-matter
|
|
4
|
+
* gymnastics. Date is the calling day in UTC, not a wall-clock timestamp —
|
|
5
|
+
* idempotency demands the same inputs produce the same bytes regardless of
|
|
6
|
+
* when the day they're called within.
|
|
7
|
+
*
|
|
8
|
+
* @param {{
|
|
9
|
+
* pr: number,
|
|
10
|
+
* note: string,
|
|
11
|
+
* persona: string | null,
|
|
12
|
+
* date: string,
|
|
13
|
+
* commits: string[],
|
|
14
|
+
* }} input
|
|
15
|
+
* @returns {string}
|
|
16
|
+
*/
|
|
17
|
+
export function renderFragment(input: {
|
|
18
|
+
pr: number;
|
|
19
|
+
note: string;
|
|
20
|
+
persona: string | null;
|
|
21
|
+
date: string;
|
|
22
|
+
commits: string[];
|
|
23
|
+
}): string;
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {Object} DraftInput
|
|
26
|
+
* @property {string} projectDir
|
|
27
|
+
* @property {number} pr
|
|
28
|
+
* @property {string} note
|
|
29
|
+
* @property {string | null} persona
|
|
30
|
+
* @property {string | null} outputDir // CLI flag override (relative to projectDir or absolute).
|
|
31
|
+
* @property {string[] | null} commits // explicit commit list; null → autodetect via git log.
|
|
32
|
+
* @property {string | null} date // ISO yyyy-mm-dd; null → today (UTC).
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {Object} DraftResult
|
|
36
|
+
* @property {boolean} written
|
|
37
|
+
* @property {string} path
|
|
38
|
+
* @property {string} reason // 'created' | 'no-change' | 'updated'
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* Write the PR fragment idempotently. Returns the result envelope.
|
|
42
|
+
*
|
|
43
|
+
* @param {DraftInput} input
|
|
44
|
+
* @returns {DraftResult}
|
|
45
|
+
*/
|
|
46
|
+
export function writeDraft(input: DraftInput): DraftResult;
|
|
47
|
+
export type DraftInput = {
|
|
48
|
+
projectDir: string;
|
|
49
|
+
pr: number;
|
|
50
|
+
note: string;
|
|
51
|
+
persona: string | null;
|
|
52
|
+
/**
|
|
53
|
+
* // CLI flag override (relative to projectDir or absolute).
|
|
54
|
+
*/
|
|
55
|
+
outputDir: string | null;
|
|
56
|
+
/**
|
|
57
|
+
* // explicit commit list; null → autodetect via git log.
|
|
58
|
+
*/
|
|
59
|
+
commits: string[] | null;
|
|
60
|
+
/**
|
|
61
|
+
* // ISO yyyy-mm-dd; null → today (UTC).
|
|
62
|
+
*/
|
|
63
|
+
date: string | null;
|
|
64
|
+
};
|
|
65
|
+
export type DraftResult = {
|
|
66
|
+
written: boolean;
|
|
67
|
+
path: string;
|
|
68
|
+
/**
|
|
69
|
+
* // 'created' | 'no-change' | 'updated'
|
|
70
|
+
*/
|
|
71
|
+
reason: string;
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=draft.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft.d.mts","sourceRoot":"","sources":["../../../src/cli/release-notes/draft.mjs"],"names":[],"mappings":"AAqBA;;;;;;;;;;;;;;;GAeG;AACH,sCATW;IACN,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,GACS,MAAM,CAmBlB;AA4BD;;;;;;;;;GASG;AAEH;;;;;GAKG;AAEH;;;;;GAKG;AACH,kCAHW,UAAU,GACR,WAAW,CA4BvB;;gBAhDa,MAAM;QACN,MAAM;UACN,MAAM;aACN,MAAM,GAAG,IAAI;;;;eACb,MAAM,GAAG,IAAI;;;;aACb,MAAM,EAAE,GAAG,IAAI;;;;UACf,MAAM,GAAG,IAAI;;;aAKb,OAAO;UACP,MAAM;;;;YACN,MAAM"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* release-notes/draft.mjs — towncrier-style per-PR fragment writer.
|
|
3
|
+
*
|
|
4
|
+
* Writes `<output_dir>/_unreleased/PR-<n>.md` for a single PR. Idempotent:
|
|
5
|
+
* if the target file already exists with byte-identical content, the call
|
|
6
|
+
* is a no-op and the envelope reports `written: false`. The same input
|
|
7
|
+
* always produces the same bytes on disk — that is the load-bearing
|
|
8
|
+
* invariant the news-fragment pattern depends on (zero merge conflicts
|
|
9
|
+
* on the unreleased file, AC2).
|
|
10
|
+
*
|
|
11
|
+
* Deterministic I/O only. No LLM calls. No prompt templates. The host
|
|
12
|
+
* LLM authors the note text and hands it in via --note.
|
|
13
|
+
*/
|
|
14
|
+
import { execFileSync } from 'node:child_process';
|
|
15
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { atomicWriteText } from '../util/atomic-write.mjs';
|
|
18
|
+
import { resolveOutputDir } from './output-dir.mjs';
|
|
19
|
+
/**
|
|
20
|
+
* Build the canonical fragment body. The shape is intentionally simple +
|
|
21
|
+
* stable so the compile step can parse it back without YAML / front-matter
|
|
22
|
+
* gymnastics. Date is the calling day in UTC, not a wall-clock timestamp —
|
|
23
|
+
* idempotency demands the same inputs produce the same bytes regardless of
|
|
24
|
+
* when the day they're called within.
|
|
25
|
+
*
|
|
26
|
+
* @param {{
|
|
27
|
+
* pr: number,
|
|
28
|
+
* note: string,
|
|
29
|
+
* persona: string | null,
|
|
30
|
+
* date: string,
|
|
31
|
+
* commits: string[],
|
|
32
|
+
* }} input
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
export function renderFragment(input) {
|
|
36
|
+
const personaLine = input.persona ? `persona: ${input.persona}` : 'persona: (none)';
|
|
37
|
+
const commitsLine = input.commits.length === 0 ? 'commits: (none)' : `commits: ${input.commits.join(', ')}`;
|
|
38
|
+
return [
|
|
39
|
+
`# PR-${input.pr}`,
|
|
40
|
+
'',
|
|
41
|
+
`date: ${input.date}`,
|
|
42
|
+
`pr: ${input.pr}`,
|
|
43
|
+
personaLine,
|
|
44
|
+
commitsLine,
|
|
45
|
+
'',
|
|
46
|
+
'## Release note',
|
|
47
|
+
'',
|
|
48
|
+
input.note.trim(),
|
|
49
|
+
'',
|
|
50
|
+
].join('\n');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the commits referencing a PR by number. Best-effort — if git is
|
|
54
|
+
* unavailable or no matching commits are found, returns an empty array.
|
|
55
|
+
* Caller can pass commits explicitly via --commits to bypass git inspection.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} projectDir
|
|
58
|
+
* @param {number} pr
|
|
59
|
+
* @returns {string[]}
|
|
60
|
+
*/
|
|
61
|
+
function discoverCommitsForPr(projectDir, pr) {
|
|
62
|
+
try {
|
|
63
|
+
const out = execFileSync('git', ['-C', projectDir, 'log', '--no-merges', `--grep=#${pr}\\b`, '--format=%H'], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
64
|
+
return out
|
|
65
|
+
.split('\n')
|
|
66
|
+
.map((s) => s.trim())
|
|
67
|
+
.filter((s) => s.length > 0)
|
|
68
|
+
.map((sha) => sha.slice(0, 12));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* @typedef {Object} DraftInput
|
|
76
|
+
* @property {string} projectDir
|
|
77
|
+
* @property {number} pr
|
|
78
|
+
* @property {string} note
|
|
79
|
+
* @property {string | null} persona
|
|
80
|
+
* @property {string | null} outputDir // CLI flag override (relative to projectDir or absolute).
|
|
81
|
+
* @property {string[] | null} commits // explicit commit list; null → autodetect via git log.
|
|
82
|
+
* @property {string | null} date // ISO yyyy-mm-dd; null → today (UTC).
|
|
83
|
+
*/
|
|
84
|
+
/**
|
|
85
|
+
* @typedef {Object} DraftResult
|
|
86
|
+
* @property {boolean} written
|
|
87
|
+
* @property {string} path
|
|
88
|
+
* @property {string} reason // 'created' | 'no-change' | 'updated'
|
|
89
|
+
*/
|
|
90
|
+
/**
|
|
91
|
+
* Write the PR fragment idempotently. Returns the result envelope.
|
|
92
|
+
*
|
|
93
|
+
* @param {DraftInput} input
|
|
94
|
+
* @returns {DraftResult}
|
|
95
|
+
*/
|
|
96
|
+
export function writeDraft(input) {
|
|
97
|
+
const outputDir = resolveOutputDir(input.projectDir, input.outputDir);
|
|
98
|
+
const unreleasedDir = join(outputDir, '_unreleased');
|
|
99
|
+
const target = join(unreleasedDir, `PR-${input.pr}.md`);
|
|
100
|
+
const date = input.date ?? new Date().toISOString().slice(0, 10);
|
|
101
|
+
const commits = input.commits ?? discoverCommitsForPr(input.projectDir, input.pr);
|
|
102
|
+
const body = renderFragment({
|
|
103
|
+
pr: input.pr,
|
|
104
|
+
note: input.note,
|
|
105
|
+
persona: input.persona,
|
|
106
|
+
date,
|
|
107
|
+
commits,
|
|
108
|
+
});
|
|
109
|
+
if (existsSync(target)) {
|
|
110
|
+
const existing = readFileSync(target, 'utf-8');
|
|
111
|
+
if (existing === body) {
|
|
112
|
+
return { written: false, path: target, reason: 'no-change' };
|
|
113
|
+
}
|
|
114
|
+
atomicWriteText(target, body);
|
|
115
|
+
return { written: true, path: target, reason: 'updated' };
|
|
116
|
+
}
|
|
117
|
+
atomicWriteText(target, body);
|
|
118
|
+
return { written: true, path: target, reason: 'created' };
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=draft.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft.mjs","sourceRoot":"","sources":["../../../src/cli/release-notes/draft.mjs"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate a release tag. Throws if the tag contains path-traversal
|
|
3
|
+
* sequences (`..`, `/`) or characters outside the semver-compatible
|
|
4
|
+
* allowlist. Defends path.join calls in compile.mjs and publish.mjs
|
|
5
|
+
* against directory escape (SEC-001).
|
|
6
|
+
*
|
|
7
|
+
* @param {string} tag
|
|
8
|
+
* @returns {void}
|
|
9
|
+
*/
|
|
10
|
+
export function validateTag(tag: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Resolve the canonical output directory from flag override → config → default.
|
|
13
|
+
* Flag override wins when present. Returns an absolute path.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} projectDir
|
|
16
|
+
* @param {string | null} flagOverride
|
|
17
|
+
* @returns {string}
|
|
18
|
+
*/
|
|
19
|
+
export function resolveOutputDir(projectDir: string, flagOverride: string | null): string;
|
|
20
|
+
//# sourceMappingURL=output-dir.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-dir.d.mts","sourceRoot":"","sources":["../../../src/cli/release-notes/output-dir.mjs"],"names":[],"mappings":"AAgBA;;;;;;;;GAQG;AACH,iCAHW,MAAM,GACJ,IAAI,CAWhB;AAED;;;;;;;GAOG;AACH,6CAJW,MAAM,gBACN,MAAM,GAAG,IAAI,GACX,MAAM,CASlB"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* release-notes/output-dir.mjs — shared output directory resolver.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from draft.mjs, compile.mjs, and publish.mjs to eliminate
|
|
5
|
+
* copy-paste duplication (QUA-003).
|
|
6
|
+
*/
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
import { loadProjectConfig } from '../config/load.mjs';
|
|
9
|
+
const DEFAULT_OUTPUT_DIR = 'docs/releases';
|
|
10
|
+
/** Allowlist for release tag values — semver-compatible subset. */
|
|
11
|
+
const TAG_PATTERN = /^[a-zA-Z0-9._-]+$/;
|
|
12
|
+
/**
|
|
13
|
+
* Validate a release tag. Throws if the tag contains path-traversal
|
|
14
|
+
* sequences (`..`, `/`) or characters outside the semver-compatible
|
|
15
|
+
* allowlist. Defends path.join calls in compile.mjs and publish.mjs
|
|
16
|
+
* against directory escape (SEC-001).
|
|
17
|
+
*
|
|
18
|
+
* @param {string} tag
|
|
19
|
+
* @returns {void}
|
|
20
|
+
*/
|
|
21
|
+
export function validateTag(tag) {
|
|
22
|
+
if (tag.includes('..') || tag.includes('/')) {
|
|
23
|
+
throw new Error(`Invalid --tag: "${tag}" contains path-traversal sequences (.. or /)`);
|
|
24
|
+
}
|
|
25
|
+
if (!TAG_PATTERN.test(tag)) {
|
|
26
|
+
throw new Error(`Invalid --tag: "${tag}" must match /^[a-zA-Z0-9._-]+$/ (no spaces or special characters)`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the canonical output directory from flag override → config → default.
|
|
31
|
+
* Flag override wins when present. Returns an absolute path.
|
|
32
|
+
*
|
|
33
|
+
* @param {string} projectDir
|
|
34
|
+
* @param {string | null} flagOverride
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
export function resolveOutputDir(projectDir, flagOverride) {
|
|
38
|
+
const config = loadProjectConfig(projectDir);
|
|
39
|
+
const configured = config?.changelog?.release_notes?.output_dir;
|
|
40
|
+
return resolve(projectDir, flagOverride ?? (typeof configured === 'string' ? configured : DEFAULT_OUTPUT_DIR));
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=output-dir.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-dir.mjs","sourceRoot":"","sources":["../../../src/cli/release-notes/output-dir.mjs"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAEtD,MAAM,kBAAkB,GAAG,eAAe,CAAA;AAE1C,mEAAmE;AACnE,MAAM,WAAW,GAAG,mBAAmB,CAAA;AAEvC;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,GAAG;IAC9B,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,+CAA+C,CAAC,CAAA;IACvF,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACd,mBAAmB,GAAG,oEAAoE,CAC1F,CAAA;IACF,CAAC;AACF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAU,EAAE,YAAY;IACxD,MAAM,MAAM,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAA;IAC5C,MAAM,UAAU,GAAG,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,CAAA;IAC/D,OAAO,OAAO,CACb,UAAU,EACV,YAAY,IAAI,CAAC,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAClF,CAAA;AACF,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when the raw persona-tag string from a fragment should be treated as
|
|
3
|
+
* "no tag" (and therefore routed to triage with reason `no-persona-tag`).
|
|
4
|
+
* Mirrors `NONE_SENTINELS` so callers — notably `compile.mjs`'s triage-reason
|
|
5
|
+
* labeller — stay in lockstep with the classifier. Case-insensitive.
|
|
6
|
+
*
|
|
7
|
+
* @param {string | null | undefined} raw
|
|
8
|
+
* @returns {boolean}
|
|
9
|
+
*/
|
|
10
|
+
export function isNoneSentinel(raw: string | null | undefined): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Read tier + name out of a persona envelope row (the shape personas.mjs
|
|
13
|
+
* persists). Tier lives inside `ui_json` (a JSON string). Returns null
|
|
14
|
+
* when the row is unusable.
|
|
15
|
+
*
|
|
16
|
+
* @param {{ ui_json?: unknown }} row
|
|
17
|
+
* @returns {{ name: string, tier: PersonaTier } | null}
|
|
18
|
+
*/
|
|
19
|
+
export function extractPersonaIdentity(row: {
|
|
20
|
+
ui_json?: unknown;
|
|
21
|
+
}): {
|
|
22
|
+
name: string;
|
|
23
|
+
tier: PersonaTier;
|
|
24
|
+
} | null;
|
|
25
|
+
/**
|
|
26
|
+
* Build a name → tier map from a personas envelope.
|
|
27
|
+
*
|
|
28
|
+
* @param {{ personas?: Array<{ ui_json?: unknown }> } | null | undefined} envelope
|
|
29
|
+
* @returns {Map<string, PersonaTier>}
|
|
30
|
+
*/
|
|
31
|
+
export function buildPersonaTierMap(envelope: {
|
|
32
|
+
personas?: Array<{
|
|
33
|
+
ui_json?: unknown;
|
|
34
|
+
}>;
|
|
35
|
+
} | null | undefined): Map<string, PersonaTier>;
|
|
36
|
+
/**
|
|
37
|
+
* Classify a fragment's persona tag against the roster + filter mode.
|
|
38
|
+
*
|
|
39
|
+
* @param {string | null | undefined} personaTag The raw tag from the fragment.
|
|
40
|
+
* @param {Map<string, PersonaTier>} tierMap From `buildPersonaTierMap`.
|
|
41
|
+
* @param {'primary' | 'primary+secondary' | 'all'} mode
|
|
42
|
+
* @returns {{ bucket: FilterBucket, resolvedName: string | null, resolvedTier: PersonaTier | null }}
|
|
43
|
+
*/
|
|
44
|
+
export function classifyFragment(personaTag: string | null | undefined, tierMap: Map<string, PersonaTier>, mode: "primary" | "primary+secondary" | "all"): {
|
|
45
|
+
bucket: FilterBucket;
|
|
46
|
+
resolvedName: string | null;
|
|
47
|
+
resolvedTier: PersonaTier | null;
|
|
48
|
+
};
|
|
49
|
+
export type PersonaTier = "primary" | "secondary" | "tertiary" | "unknown";
|
|
50
|
+
export type FilterBucket = "body" | "triage" | "filtered";
|
|
51
|
+
//# sourceMappingURL=persona-filter.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persona-filter.d.mts","sourceRoot":"","sources":["../../../src/cli/release-notes/persona-filter.mjs"],"names":[],"mappings":"AAsCA;;;;;;;;GAQG;AACH,oCAHW,MAAM,GAAG,IAAI,GAAG,SAAS,GACvB,OAAO,CAKnB;AAED;;;;;;;GAOG;AACH,4CAHW;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE,GAAG,IAAI,CAmBtD;AAED;;;;;GAKG;AACH,8CAHW;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAC5D,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAYpC;AAED;;;;;;;GAOG;AACH,6CALW,MAAM,GAAG,IAAI,GAAG,SAAS,WACzB,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,QACxB,SAAS,GAAG,mBAAmB,GAAG,KAAK,GACrC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,YAAY,EAAE,WAAW,GAAG,IAAI,CAAA;CAAE,CAsBnG;0BA5Fa,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS;2BAChD,MAAM,GAAG,QAAQ,GAAG,UAAU"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* release-notes/persona-filter.mjs — shared persona-filter logic.
|
|
3
|
+
*
|
|
4
|
+
* The compile + autodraft surfaces both need the same predicate: given a
|
|
5
|
+
* persona id (or name) extracted from a fragment, the project's
|
|
6
|
+
* `.aperant/personas.json` roster, and a filter mode, decide whether the
|
|
7
|
+
* fragment belongs in the user-facing body, in the "Needs Triage" bucket,
|
|
8
|
+
* or filtered out entirely.
|
|
9
|
+
*
|
|
10
|
+
* Three filter modes (config.changelog.release_notes.persona_filter):
|
|
11
|
+
*
|
|
12
|
+
* - 'primary' — only personas whose `tier === 'primary'`.
|
|
13
|
+
* - 'primary+secondary' — primary OR secondary. DEFAULT.
|
|
14
|
+
* - 'all' — every tier passes (no filter).
|
|
15
|
+
*
|
|
16
|
+
* Triage rules (ID-6 in spec.md, never-silent-drop invariant):
|
|
17
|
+
*
|
|
18
|
+
* - Fragment with no persona tag → bucket = 'triage'.
|
|
19
|
+
* - Fragment with a tag that doesn't
|
|
20
|
+
* resolve to a known persona in the
|
|
21
|
+
* roster → bucket = 'triage'.
|
|
22
|
+
* - Fragment whose persona DOES resolve
|
|
23
|
+
* but is filtered out by the mode → bucket = 'filtered'.
|
|
24
|
+
* - Fragment whose persona resolves AND
|
|
25
|
+
* passes the mode → bucket = 'body', with the
|
|
26
|
+
* resolved tier exposed for
|
|
27
|
+
* grouping in compile output.
|
|
28
|
+
*
|
|
29
|
+
* The matcher is case-insensitive on persona names. Empty string and the
|
|
30
|
+
* literal sentinel `(none)` are both treated as "no tag" — the draft
|
|
31
|
+
* renderer writes `persona: (none)` when no --persona flag is passed.
|
|
32
|
+
*/
|
|
33
|
+
/** @typedef {'primary' | 'secondary' | 'tertiary' | 'unknown'} PersonaTier */
|
|
34
|
+
/** @typedef {'body' | 'triage' | 'filtered'} FilterBucket */
|
|
35
|
+
const NONE_SENTINELS = new Set(['', '(none)', 'none', 'null', 'undefined']);
|
|
36
|
+
/**
|
|
37
|
+
* True when the raw persona-tag string from a fragment should be treated as
|
|
38
|
+
* "no tag" (and therefore routed to triage with reason `no-persona-tag`).
|
|
39
|
+
* Mirrors `NONE_SENTINELS` so callers — notably `compile.mjs`'s triage-reason
|
|
40
|
+
* labeller — stay in lockstep with the classifier. Case-insensitive.
|
|
41
|
+
*
|
|
42
|
+
* @param {string | null | undefined} raw
|
|
43
|
+
* @returns {boolean}
|
|
44
|
+
*/
|
|
45
|
+
export function isNoneSentinel(raw) {
|
|
46
|
+
const normalized = (raw ?? '').trim().toLowerCase();
|
|
47
|
+
return NONE_SENTINELS.has(normalized);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Read tier + name out of a persona envelope row (the shape personas.mjs
|
|
51
|
+
* persists). Tier lives inside `ui_json` (a JSON string). Returns null
|
|
52
|
+
* when the row is unusable.
|
|
53
|
+
*
|
|
54
|
+
* @param {{ ui_json?: unknown }} row
|
|
55
|
+
* @returns {{ name: string, tier: PersonaTier } | null}
|
|
56
|
+
*/
|
|
57
|
+
export function extractPersonaIdentity(row) {
|
|
58
|
+
if (!row || typeof row !== 'object')
|
|
59
|
+
return null;
|
|
60
|
+
if (typeof row.ui_json !== 'string')
|
|
61
|
+
return null;
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(row.ui_json);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
if (!parsed || typeof parsed !== 'object')
|
|
70
|
+
return null;
|
|
71
|
+
const name = typeof parsed.name === 'string' ? parsed.name.trim() : '';
|
|
72
|
+
const tierRaw = typeof parsed.tier === 'string' ? parsed.tier.trim().toLowerCase() : '';
|
|
73
|
+
if (name.length === 0)
|
|
74
|
+
return null;
|
|
75
|
+
/** @type {PersonaTier} */
|
|
76
|
+
const tier = tierRaw === 'primary' || tierRaw === 'secondary' || tierRaw === 'tertiary' ? tierRaw : 'unknown';
|
|
77
|
+
return { name, tier };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Build a name → tier map from a personas envelope.
|
|
81
|
+
*
|
|
82
|
+
* @param {{ personas?: Array<{ ui_json?: unknown }> } | null | undefined} envelope
|
|
83
|
+
* @returns {Map<string, PersonaTier>}
|
|
84
|
+
*/
|
|
85
|
+
export function buildPersonaTierMap(envelope) {
|
|
86
|
+
/** @type {Map<string, PersonaTier>} */
|
|
87
|
+
const map = new Map();
|
|
88
|
+
if (!envelope || !Array.isArray(envelope.personas))
|
|
89
|
+
return map;
|
|
90
|
+
for (const row of envelope.personas) {
|
|
91
|
+
const identity = extractPersonaIdentity(row);
|
|
92
|
+
if (!identity)
|
|
93
|
+
continue;
|
|
94
|
+
map.set(identity.name.toLowerCase(), identity.tier);
|
|
95
|
+
}
|
|
96
|
+
return map;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Classify a fragment's persona tag against the roster + filter mode.
|
|
100
|
+
*
|
|
101
|
+
* @param {string | null | undefined} personaTag The raw tag from the fragment.
|
|
102
|
+
* @param {Map<string, PersonaTier>} tierMap From `buildPersonaTierMap`.
|
|
103
|
+
* @param {'primary' | 'primary+secondary' | 'all'} mode
|
|
104
|
+
* @returns {{ bucket: FilterBucket, resolvedName: string | null, resolvedTier: PersonaTier | null }}
|
|
105
|
+
*/
|
|
106
|
+
export function classifyFragment(personaTag, tierMap, mode) {
|
|
107
|
+
const normalized = (personaTag ?? '').trim().toLowerCase();
|
|
108
|
+
if (normalized.length === 0 || NONE_SENTINELS.has(normalized)) {
|
|
109
|
+
return { bucket: 'triage', resolvedName: null, resolvedTier: null };
|
|
110
|
+
}
|
|
111
|
+
const tier = tierMap.get(normalized);
|
|
112
|
+
if (tier === undefined) {
|
|
113
|
+
// Tag was set but the roster doesn't recognize it — never silent-drop.
|
|
114
|
+
return { bucket: 'triage', resolvedName: personaTag ?? null, resolvedTier: null };
|
|
115
|
+
}
|
|
116
|
+
if (mode === 'all') {
|
|
117
|
+
return { bucket: 'body', resolvedName: personaTag ?? null, resolvedTier: tier };
|
|
118
|
+
}
|
|
119
|
+
if (mode === 'primary' && tier !== 'primary') {
|
|
120
|
+
return { bucket: 'filtered', resolvedName: personaTag ?? null, resolvedTier: tier };
|
|
121
|
+
}
|
|
122
|
+
if (mode === 'primary+secondary' && tier !== 'primary' && tier !== 'secondary') {
|
|
123
|
+
return { bucket: 'filtered', resolvedName: personaTag ?? null, resolvedTier: tier };
|
|
124
|
+
}
|
|
125
|
+
return { bucket: 'body', resolvedName: personaTag ?? null, resolvedTier: tier };
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=persona-filter.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persona-filter.mjs","sourceRoot":"","sources":["../../../src/cli/release-notes/persona-filter.mjs"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {PublishInput} input
|
|
3
|
+
* @returns {PublishResult}
|
|
4
|
+
*/
|
|
5
|
+
export function publishTag(input: PublishInput): PublishResult;
|
|
6
|
+
export type PublishInput = {
|
|
7
|
+
projectDir: string;
|
|
8
|
+
tag: string;
|
|
9
|
+
outputDir: string | null;
|
|
10
|
+
dryRun: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type PublishResult = {
|
|
13
|
+
status: "ok";
|
|
14
|
+
mode: "created" | "edited" | "dry-run";
|
|
15
|
+
notesPath: string;
|
|
16
|
+
} | {
|
|
17
|
+
status: "skipped";
|
|
18
|
+
reason: "gh-not-found" | "notes-not-found" | "gh-failed";
|
|
19
|
+
notesPath?: string;
|
|
20
|
+
exit_code?: number | null;
|
|
21
|
+
stderr?: string | null;
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=publish.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.d.mts","sourceRoot":"","sources":["../../../src/cli/release-notes/publish.mjs"],"names":[],"mappings":"AA0FA;;;GAGG;AACH,kCAHW,YAAY,GACV,aAAa,CAmCzB;;gBAzGa,MAAM;SACN,MAAM;eACN,MAAM,GAAG,IAAI;YACb,OAAO;;4BAIR;IACR,MAAM,EAAE,IAAI,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG;IACF,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,cAAc,GAAG,iBAAiB,GAAG,WAAW,CAAC;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* release-notes/publish.mjs — `gh release create/edit` shell.
|
|
3
|
+
*
|
|
4
|
+
* Reads `<output_dir>/<tag>.md` and shells `gh release create <tag>
|
|
5
|
+
* --notes-file <path>` (or `gh release edit` if a release already exists
|
|
6
|
+
* for that tag). When `gh` is unavailable on the host, returns a graceful
|
|
7
|
+
* `{status: 'skipped', reason: 'gh-not-found'}` envelope rather than
|
|
8
|
+
* crashing — AC4.
|
|
9
|
+
*
|
|
10
|
+
* Tests inject a custom shell via the GH_BIN env var (mocked to a no-op
|
|
11
|
+
* shell script) so the publish branch of TD-5 can run without making a
|
|
12
|
+
* real network call.
|
|
13
|
+
*/
|
|
14
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
15
|
+
import { existsSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { resolveOutputDir, validateTag } from './output-dir.mjs';
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {Object} PublishInput
|
|
20
|
+
* @property {string} projectDir
|
|
21
|
+
* @property {string} tag
|
|
22
|
+
* @property {string | null} outputDir
|
|
23
|
+
* @property {boolean} dryRun
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {{
|
|
27
|
+
* status: 'ok',
|
|
28
|
+
* mode: 'created' | 'edited' | 'dry-run',
|
|
29
|
+
* notesPath: string,
|
|
30
|
+
* } | {
|
|
31
|
+
* status: 'skipped',
|
|
32
|
+
* reason: 'gh-not-found' | 'notes-not-found' | 'gh-failed',
|
|
33
|
+
* notesPath?: string,
|
|
34
|
+
* exit_code?: number | null,
|
|
35
|
+
* stderr?: string | null,
|
|
36
|
+
* }} PublishResult
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* Resolve which `gh` binary to use. Defaults to `gh` on PATH; tests
|
|
40
|
+
* override via the GH_BIN env var (only honored in NODE_ENV=test).
|
|
41
|
+
*
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
function ghBin() {
|
|
45
|
+
if (process.env.NODE_ENV === 'test') {
|
|
46
|
+
const override = process.env.GH_BIN;
|
|
47
|
+
if (override && override.length > 0)
|
|
48
|
+
return override;
|
|
49
|
+
}
|
|
50
|
+
return 'gh';
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Detect whether `gh` is available on the host. Uses `which` semantics:
|
|
54
|
+
* `gh --version` exit-0 → available. Any error path → unavailable.
|
|
55
|
+
*
|
|
56
|
+
* @returns {boolean}
|
|
57
|
+
*/
|
|
58
|
+
function ghAvailable() {
|
|
59
|
+
try {
|
|
60
|
+
const result = spawnSync(ghBin(), ['--version'], { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
61
|
+
return result.status === 0;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check whether a release already exists for the given tag.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} projectDir
|
|
71
|
+
* @param {string} tag
|
|
72
|
+
* @returns {boolean}
|
|
73
|
+
*/
|
|
74
|
+
function releaseExists(projectDir, tag) {
|
|
75
|
+
try {
|
|
76
|
+
const result = spawnSync(ghBin(), ['release', 'view', tag, '--json', 'tagName'], {
|
|
77
|
+
cwd: projectDir,
|
|
78
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
79
|
+
});
|
|
80
|
+
return result.status === 0;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* @param {PublishInput} input
|
|
88
|
+
* @returns {PublishResult}
|
|
89
|
+
*/
|
|
90
|
+
export function publishTag(input) {
|
|
91
|
+
validateTag(input.tag);
|
|
92
|
+
const outputDir = resolveOutputDir(input.projectDir, input.outputDir);
|
|
93
|
+
const notesPath = join(outputDir, `${input.tag}.md`);
|
|
94
|
+
if (!existsSync(notesPath)) {
|
|
95
|
+
return { status: 'skipped', reason: 'notes-not-found', notesPath };
|
|
96
|
+
}
|
|
97
|
+
if (input.dryRun) {
|
|
98
|
+
return { status: 'ok', mode: 'dry-run', notesPath };
|
|
99
|
+
}
|
|
100
|
+
if (!ghAvailable()) {
|
|
101
|
+
return { status: 'skipped', reason: 'gh-not-found', notesPath };
|
|
102
|
+
}
|
|
103
|
+
const exists = releaseExists(input.projectDir, input.tag);
|
|
104
|
+
const verb = exists ? 'edit' : 'create';
|
|
105
|
+
const args = ['release', verb, input.tag, '--notes-file', notesPath];
|
|
106
|
+
// `gh release edit` only takes --notes-file; `gh release create` takes
|
|
107
|
+
// --notes-file too and a positional tag — same argv order works.
|
|
108
|
+
try {
|
|
109
|
+
execFileSync(ghBin(), args, {
|
|
110
|
+
cwd: input.projectDir,
|
|
111
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
return {
|
|
116
|
+
status: 'skipped',
|
|
117
|
+
reason: 'gh-failed',
|
|
118
|
+
notesPath,
|
|
119
|
+
exit_code: err.status ?? null,
|
|
120
|
+
stderr: err.stderr?.toString() ?? null,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return { status: 'ok', mode: exists ? 'edited' : 'created', notesPath };
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=publish.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.mjs","sourceRoot":"","sources":["../../../src/cli/release-notes/publish.mjs"],"names":[],"mappings":"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"}
|