@anselmdk/feature-spec-md 0.2.1 → 0.3.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/README.md +68 -2
- package/dist/cli.js +22 -2
- package/dist/cli.js.map +1 -1
- package/dist/githubActionReport.d.ts +3 -0
- package/dist/githubActionReport.d.ts.map +1 -0
- package/dist/githubActionReport.js +269 -0
- package/dist/githubActionReport.js.map +1 -0
- package/docs/releasing.md +31 -69
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -92,9 +92,9 @@ npx feature-spec-md check \
|
|
|
92
92
|
--tests "e2e/**/*.spec.ts"
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
The `check` command validates spec structure, references between documents, and test coverage. Scenario coverage is required by default when tests are scanned. Use `--require-scenario-coverage=false` while drafting.
|
|
95
|
+
The `check` command validates spec structure, references between documents, and test coverage. Scenario coverage is required by default when tests are scanned. Use `--require-scenario-coverage=false` while drafting. Use `--require-rule-coverage` and `--require-model-coverage` when rules and model items must also fail validation if they have no test references.
|
|
96
96
|
|
|
97
|
-
The `coverage` command prints a terminal implementation report. Use `--fail-on-missing` when missing scenario
|
|
97
|
+
The `coverage` command prints a terminal implementation report. Use `--fail-on-missing` when missing model item, rule, or scenario coverage should fail CI.
|
|
98
98
|
|
|
99
99
|
The `report` command writes an HTML report. It can include screenshot evidence from Playwright or another test runner by passing one or more screenshot manifest files:
|
|
100
100
|
|
|
@@ -103,6 +103,72 @@ npx feature-spec-md report \
|
|
|
103
103
|
--screenshots "test-results/spec-report/screenshots-*.json"
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
+
## GitHub Actions report publishing
|
|
107
|
+
|
|
108
|
+
`feature-spec-md github-report` writes the GitHub Actions job summary and can either prepare outputs for a GitHub artifact upload or publish the generated report to FTP.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npx feature-spec-md github-report \
|
|
112
|
+
--report-dir test-results/spec-report \
|
|
113
|
+
--publish ftp
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
For FTP publishing, configure repository secrets in GitHub under **Settings → Secrets and variables → Actions → Repository secrets**. For this repository, the direct settings URL is:
|
|
117
|
+
|
|
118
|
+
```txt
|
|
119
|
+
https://github.com/anselmdk/feature-spec-md/settings/secrets/actions
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
GitHub's documentation for repository secrets is here:
|
|
123
|
+
|
|
124
|
+
```txt
|
|
125
|
+
https://docs.github.com/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Required secrets or environment variables:
|
|
129
|
+
|
|
130
|
+
```txt
|
|
131
|
+
FEATURE_SPEC_FTP_HOST=ftp.example.com
|
|
132
|
+
FEATURE_SPEC_FTP_USER=feature-spec-md
|
|
133
|
+
FEATURE_SPEC_FTP_PASSWORD=<your FTP password>
|
|
134
|
+
FEATURE_SPEC_REPORT_BASE_URL=http://feature-spec-md.anselm.dk/
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Optional values:
|
|
138
|
+
|
|
139
|
+
```txt
|
|
140
|
+
FEATURE_SPEC_FTP_REMOTE_DIR=/public_html/feature-spec-md
|
|
141
|
+
FEATURE_SPEC_FTP_PORT=21
|
|
142
|
+
FEATURE_SPEC_FTP_SECURE=false
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Example GitHub Actions step:
|
|
146
|
+
|
|
147
|
+
```yaml
|
|
148
|
+
- name: Publish feature spec report
|
|
149
|
+
if: always() && hashFiles('test-results/spec-report/index.html') != ''
|
|
150
|
+
run: npx feature-spec-md github-report --publish ftp --report-dir test-results/spec-report
|
|
151
|
+
env:
|
|
152
|
+
FEATURE_SPEC_FTP_HOST: ${{ secrets.FEATURE_SPEC_FTP_HOST }}
|
|
153
|
+
FEATURE_SPEC_FTP_USER: ${{ secrets.FEATURE_SPEC_FTP_USER }}
|
|
154
|
+
FEATURE_SPEC_FTP_PASSWORD: ${{ secrets.FEATURE_SPEC_FTP_PASSWORD }}
|
|
155
|
+
FEATURE_SPEC_REPORT_BASE_URL: ${{ secrets.FEATURE_SPEC_REPORT_BASE_URL }}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
With `FEATURE_SPEC_REPORT_BASE_URL=http://feature-spec-md.anselm.dk/` and GitHub Actions build number `42`, the report is uploaded to a build-numbered directory and linked as:
|
|
159
|
+
|
|
160
|
+
```txt
|
|
161
|
+
http://feature-spec-md.anselm.dk/42/
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The build index is created or updated at:
|
|
165
|
+
|
|
166
|
+
```txt
|
|
167
|
+
http://feature-spec-md.anselm.dk/
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
FTP reports are uploaded into a build-numbered directory using `GITHUB_RUN_NUMBER`, and an `index.html` file is created or updated at the public base URL so all uploaded builds can be browsed. When FTP publishing is used, the command emits `upload-github-artifact=false` and links the GitHub Actions job summary directly to the hosted report URL.
|
|
171
|
+
|
|
106
172
|
## Library API
|
|
107
173
|
|
|
108
174
|
Most integrations can use the top-level document API:
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { collectSpecScreenshots, renderHtmlReport, writeTextFile, } from "./index.js";
|
|
9
|
+
import { publishGithubActionReport } from "./githubActionReport.js";
|
|
9
10
|
import { checkSpecDocuments } from "./specDocuments.js";
|
|
10
11
|
import { buildSpecImplementationReport, formatSpecImplementationReport, } from "./testImplementationReport.js";
|
|
11
12
|
const defaultSpecPattern = "specs/**/*.model.md,specs/**/*.feature.md,specs/**/*.stack.md,specs/**/*.design.md";
|
|
@@ -23,6 +24,8 @@ async function main() {
|
|
|
23
24
|
return runReport(options);
|
|
24
25
|
if (command === "coverage")
|
|
25
26
|
return runCoverage(options);
|
|
27
|
+
if (command === "github-report")
|
|
28
|
+
return publishGithubActionReport(options);
|
|
26
29
|
if (command === "init")
|
|
27
30
|
return runInit(options);
|
|
28
31
|
printHelp();
|
|
@@ -32,6 +35,7 @@ async function runCheck(options) {
|
|
|
32
35
|
const result = await checkSpecDocuments({
|
|
33
36
|
specs: optionList(options.specs, defaultSpecPattern),
|
|
34
37
|
tests: options.tests === "" ? [] : optionList(options.tests, defaultTestPattern),
|
|
38
|
+
requireModelCoverage: options["require-model-coverage"] === "true",
|
|
35
39
|
requireRuleCoverage: options["require-rule-coverage"] === "true",
|
|
36
40
|
requireScenarioCoverage: options["require-scenario-coverage"] !== "false",
|
|
37
41
|
});
|
|
@@ -60,7 +64,7 @@ async function runCoverage(options) {
|
|
|
60
64
|
}
|
|
61
65
|
const report = buildSpecImplementationReport(result.features, result.coverage, result.models);
|
|
62
66
|
console.log(formatSpecImplementationReport(report));
|
|
63
|
-
if (options["fail-on-missing"] === "true" && report
|
|
67
|
+
if (options["fail-on-missing"] === "true" && hasMissingCoverage(report)) {
|
|
64
68
|
process.exit(1);
|
|
65
69
|
}
|
|
66
70
|
}
|
|
@@ -129,6 +133,11 @@ function printIssues(issues) {
|
|
|
129
133
|
console.error(`${issue.severity.toUpperCase()} ${issue.code}${location ? ` ${location}` : ""}: ${issue.message}`);
|
|
130
134
|
}
|
|
131
135
|
}
|
|
136
|
+
function hasMissingCoverage(report) {
|
|
137
|
+
return (report.missingScenarios > 0 ||
|
|
138
|
+
report.missingRules > 0 ||
|
|
139
|
+
report.missingModelItems > 0);
|
|
140
|
+
}
|
|
132
141
|
async function reportTitle() {
|
|
133
142
|
const packageName = await readPackageName();
|
|
134
143
|
return packageName
|
|
@@ -155,13 +164,24 @@ Usage:
|
|
|
155
164
|
feature-spec-md check [--specs "specs/**/*.model.md,specs/**/*.feature.md,specs/**/*.stack.md,specs/**/*.design.md"] [--tests "tests/**/*.spec.ts"]
|
|
156
165
|
feature-spec-md coverage [--specs "specs/**/*.model.md,specs/**/*.feature.md,specs/**/*.stack.md,specs/**/*.design.md"] [--tests "tests/**/*.spec.ts"] [--fail-on-missing]
|
|
157
166
|
feature-spec-md report [--specs "specs/**/*.model.md,specs/**/*.feature.md,specs/**/*.stack.md,specs/**/*.design.md"] [--tests "tests/**/*.spec.ts"] [--screenshots "test-results/spec-report/screenshots.json"] [--out test-results/feature-spec-report/index.html]
|
|
167
|
+
feature-spec-md github-report [--report-dir test-results/spec-report] [--publish artifact|ftp]
|
|
158
168
|
|
|
159
169
|
Options:
|
|
170
|
+
--require-model-coverage Fail when model items have no matching test references.
|
|
160
171
|
--require-rule-coverage Fail when rules have no matching test references.
|
|
161
172
|
--require-scenario-coverage Defaults to true for check. Use --require-scenario-coverage=false to disable.
|
|
162
|
-
--fail-on-missing Exit with status 1 when coverage finds missing
|
|
173
|
+
--fail-on-missing Exit with status 1 when coverage finds missing model items, rules, or scenarios.
|
|
163
174
|
--screenshots Screenshot manifest JSON glob for report evidence.
|
|
164
175
|
--tests "" Disable test coverage lookup.
|
|
176
|
+
|
|
177
|
+
GitHub report publishing:
|
|
178
|
+
--publish artifact Write summary and outputs for GitHub artifact upload.
|
|
179
|
+
--publish ftp Upload report files and an index.html to FTP; no GitHub artifact is needed.
|
|
180
|
+
--ftp-host FTP host, or FEATURE_SPEC_FTP_HOST.
|
|
181
|
+
--ftp-user FTP username, or FEATURE_SPEC_FTP_USER.
|
|
182
|
+
--ftp-password FTP password, or FEATURE_SPEC_FTP_PASSWORD.
|
|
183
|
+
--ftp-remote-dir FTP root directory for reports, or FEATURE_SPEC_FTP_REMOTE_DIR.
|
|
184
|
+
--base-url Public base URL, or FEATURE_SPEC_REPORT_BASE_URL.
|
|
165
185
|
`);
|
|
166
186
|
}
|
|
167
187
|
function fileNameForKind(kind) {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"githubActionReport.d.ts","sourceRoot":"","sources":["../src/githubActionReport.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAe3E,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,yBAAyB,iBA8CnC"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
export async function publishGithubActionReport(options) {
|
|
6
|
+
const reportDir = value(options, "report-dir", "FEATURE_SPEC_REPORT_DIR") ??
|
|
7
|
+
"test-results/spec-report";
|
|
8
|
+
const reportName = value(options, "name", "FEATURE_SPEC_REPORT_NAME") ??
|
|
9
|
+
"feature-spec-report";
|
|
10
|
+
const mode = publishMode(value(options, "publish", "FEATURE_SPEC_REPORT_PUBLISH") ?? "artifact");
|
|
11
|
+
if (mode === "ftp") {
|
|
12
|
+
const config = ftpConfig(options);
|
|
13
|
+
const reportUrl = await publishReportToFtp(reportDir, config);
|
|
14
|
+
await writeGithubSummary([
|
|
15
|
+
"## Feature Spec Report",
|
|
16
|
+
"",
|
|
17
|
+
`<p><strong>Hosted HTML report:</strong> <a href=\"${escapeHtml(reportUrl)}\">${escapeHtml(reportName)}</a></p>`,
|
|
18
|
+
`<p><strong>Report index:</strong> <a href=\"${escapeHtml(indexUrl(config.baseUrl))}\">all builds</a></p>`,
|
|
19
|
+
"",
|
|
20
|
+
].join("\n"));
|
|
21
|
+
await writeGithubOutput({
|
|
22
|
+
"upload-github-artifact": "false",
|
|
23
|
+
"report-url": reportUrl,
|
|
24
|
+
"index-url": indexUrl(config.baseUrl),
|
|
25
|
+
});
|
|
26
|
+
console.log(`Feature spec report uploaded to ${reportUrl}`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
await writeGithubSummary([
|
|
30
|
+
"## Feature Spec Report",
|
|
31
|
+
"",
|
|
32
|
+
`<p>Feature spec report is ready for GitHub artifact upload as <strong>${escapeHtml(reportName)}</strong>.</p>`,
|
|
33
|
+
"",
|
|
34
|
+
].join("\n"));
|
|
35
|
+
await writeGithubOutput({
|
|
36
|
+
"upload-github-artifact": "true",
|
|
37
|
+
"artifact-name": reportName,
|
|
38
|
+
"artifact-path": reportDir,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function publishReportToFtp(reportDir, config) {
|
|
42
|
+
const buildRemoteDir = pathJoin(config.remoteDir, config.buildNumber);
|
|
43
|
+
await uploadDirectory(reportDir, buildRemoteDir, config);
|
|
44
|
+
const builds = await listBuildNumbers(config.remoteDir, config);
|
|
45
|
+
if (!builds.includes(config.buildNumber))
|
|
46
|
+
builds.push(config.buildNumber);
|
|
47
|
+
builds.sort((a, b) => Number(b) - Number(a));
|
|
48
|
+
const localIndex = join(tmpdir(), `feature-spec-md-build-index-${process.pid}.html`);
|
|
49
|
+
await writeFile(localIndex, renderBuildIndex(config.baseUrl, builds), "utf8");
|
|
50
|
+
await uploadFile(localIndex, pathJoin(config.remoteDir, "index.html"), config);
|
|
51
|
+
return buildUrl(config.baseUrl, config.buildNumber);
|
|
52
|
+
}
|
|
53
|
+
async function uploadDirectory(localDir, remoteDir, config) {
|
|
54
|
+
const files = await walkFiles(localDir);
|
|
55
|
+
if (!files.length) {
|
|
56
|
+
throw new Error(`Report directory contains no files: ${localDir}`);
|
|
57
|
+
}
|
|
58
|
+
for (const file of files) {
|
|
59
|
+
const remotePath = pathJoin(remoteDir, relative(localDir, file).split(sep).join("/"));
|
|
60
|
+
await uploadFile(file, remotePath, config);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function uploadFile(localFile, remotePath, config) {
|
|
64
|
+
await runCurl([
|
|
65
|
+
"--silent",
|
|
66
|
+
"--show-error",
|
|
67
|
+
"--fail",
|
|
68
|
+
"--ftp-create-dirs",
|
|
69
|
+
"-u",
|
|
70
|
+
`${config.user}:${config.password}`,
|
|
71
|
+
"-T",
|
|
72
|
+
localFile,
|
|
73
|
+
ftpUrl(config, remotePath),
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
76
|
+
async function listBuildNumbers(remoteDir, config) {
|
|
77
|
+
try {
|
|
78
|
+
const listing = await listRemoteDirectory(remoteDir, config);
|
|
79
|
+
return Array.from(new Set(parseBuildNumbersFromDirectoryListing(listing)));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function listRemoteDirectory(remoteDir, config) {
|
|
86
|
+
const directoryUrl = ftpUrl(config, asDirectoryPath(remoteDir));
|
|
87
|
+
const commonArgs = [
|
|
88
|
+
"--silent",
|
|
89
|
+
"--show-error",
|
|
90
|
+
"--fail",
|
|
91
|
+
"-u",
|
|
92
|
+
`${config.user}:${config.password}`,
|
|
93
|
+
];
|
|
94
|
+
try {
|
|
95
|
+
return await runCurl([...commonArgs, "--list-only", directoryUrl]);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return runCurl([...commonArgs, directoryUrl]);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function parseBuildNumbersFromDirectoryListing(listing) {
|
|
102
|
+
return listing
|
|
103
|
+
.split(/\r?\n/)
|
|
104
|
+
.flatMap((line) => {
|
|
105
|
+
const hrefBuilds = Array.from(line.matchAll(/href=["'][^"']*?(\d+)\/?["']/gi), (match) => match[1]);
|
|
106
|
+
const lastToken = line.trim().split(/\s+/).at(-1) ?? "";
|
|
107
|
+
return /^\d+$/.test(lastToken) ? [...hrefBuilds, lastToken] : hrefBuilds;
|
|
108
|
+
})
|
|
109
|
+
.filter((name) => /^\d+$/.test(name));
|
|
110
|
+
}
|
|
111
|
+
async function runCurl(args) {
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
const child = spawn("curl", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
114
|
+
let stdout = "";
|
|
115
|
+
let stderr = "";
|
|
116
|
+
child.stdout.on("data", (chunk) => {
|
|
117
|
+
stdout += chunk.toString();
|
|
118
|
+
});
|
|
119
|
+
child.stderr.on("data", (chunk) => {
|
|
120
|
+
stderr += chunk.toString();
|
|
121
|
+
});
|
|
122
|
+
child.on("error", reject);
|
|
123
|
+
child.on("close", (code) => {
|
|
124
|
+
if (code === 0) {
|
|
125
|
+
resolve(stdout);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
reject(new Error(stderr.trim() || `curl exited with status ${code}`));
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async function walkFiles(dir) {
|
|
133
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
134
|
+
const files = await Promise.all(entries.map(async (entry) => {
|
|
135
|
+
const fullPath = join(dir, entry.name);
|
|
136
|
+
if (entry.isDirectory())
|
|
137
|
+
return walkFiles(fullPath);
|
|
138
|
+
if (entry.isFile())
|
|
139
|
+
return [fullPath];
|
|
140
|
+
return [];
|
|
141
|
+
}));
|
|
142
|
+
return files.flat();
|
|
143
|
+
}
|
|
144
|
+
function ftpConfig(options) {
|
|
145
|
+
const host = required(options, "ftp-host", "FEATURE_SPEC_FTP_HOST");
|
|
146
|
+
const user = required(options, "ftp-user", "FEATURE_SPEC_FTP_USER");
|
|
147
|
+
const password = required(options, "ftp-password", "FEATURE_SPEC_FTP_PASSWORD");
|
|
148
|
+
const baseUrl = required(options, "base-url", "FEATURE_SPEC_REPORT_BASE_URL");
|
|
149
|
+
const remoteDir = value(options, "ftp-remote-dir", "FEATURE_SPEC_FTP_REMOTE_DIR") ?? "";
|
|
150
|
+
const buildNumber = value(options, "build-number", "GITHUB_RUN_NUMBER") ??
|
|
151
|
+
value(options, "build-number", "FEATURE_SPEC_BUILD_NUMBER") ??
|
|
152
|
+
"local";
|
|
153
|
+
const secure = booleanValue(value(options, "ftp-secure", "FEATURE_SPEC_FTP_SECURE"));
|
|
154
|
+
const port = value(options, "ftp-port", "FEATURE_SPEC_FTP_PORT");
|
|
155
|
+
if (!/^\d+$/.test(buildNumber)) {
|
|
156
|
+
throw new Error(`Build number must be numeric for FTP publishing: ${buildNumber}`);
|
|
157
|
+
}
|
|
158
|
+
return { host, user, password, port, secure, remoteDir, baseUrl, buildNumber };
|
|
159
|
+
}
|
|
160
|
+
function publishMode(value) {
|
|
161
|
+
if (value === "ftp" || value === "artifact")
|
|
162
|
+
return value;
|
|
163
|
+
throw new Error(`Unknown report publish mode: ${value}`);
|
|
164
|
+
}
|
|
165
|
+
function required(options, key, envKey) {
|
|
166
|
+
const result = value(options, key, envKey);
|
|
167
|
+
if (!result)
|
|
168
|
+
throw new Error(`Missing required option --${key} or ${envKey}.`);
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
function value(options, key, envKey) {
|
|
172
|
+
return options[key] ?? process.env[envKey];
|
|
173
|
+
}
|
|
174
|
+
function booleanValue(value) {
|
|
175
|
+
return value === "true" || value === "1" || value === "yes";
|
|
176
|
+
}
|
|
177
|
+
function ftpUrl(config, remotePath) {
|
|
178
|
+
const protocol = config.secure ? "ftps" : "ftp";
|
|
179
|
+
const port = config.port ? `:${config.port}` : "";
|
|
180
|
+
const hasTrailingSlash = remotePath.endsWith("/");
|
|
181
|
+
const encodedPath = remotePath
|
|
182
|
+
.split("/")
|
|
183
|
+
.filter(Boolean)
|
|
184
|
+
.map(encodeURIComponent)
|
|
185
|
+
.join("/");
|
|
186
|
+
const directorySlash = hasTrailingSlash ? "/" : "";
|
|
187
|
+
return `${protocol}://${config.host}${port}/${encodedPath}${directorySlash}`;
|
|
188
|
+
}
|
|
189
|
+
function asDirectoryPath(remotePath) {
|
|
190
|
+
const path = pathJoin(remotePath);
|
|
191
|
+
return path ? `${path}/` : "";
|
|
192
|
+
}
|
|
193
|
+
function pathJoin(...parts) {
|
|
194
|
+
const joined = parts
|
|
195
|
+
.flatMap((part) => part.split("/"))
|
|
196
|
+
.map((part) => part.trim())
|
|
197
|
+
.filter((part) => part && part !== ".")
|
|
198
|
+
.join("/");
|
|
199
|
+
return joined;
|
|
200
|
+
}
|
|
201
|
+
function renderBuildIndex(baseUrl, builds) {
|
|
202
|
+
const generatedAt = new Date().toISOString();
|
|
203
|
+
const rows = builds
|
|
204
|
+
.map((build) => `<li><a href=\"${escapeHtml(buildUrl(baseUrl, build))}\">Build ${escapeHtml(build)}</a></li>`)
|
|
205
|
+
.join("\n");
|
|
206
|
+
return `<!doctype html>
|
|
207
|
+
<html lang=\"en\">
|
|
208
|
+
<head>
|
|
209
|
+
<meta charset=\"utf-8\">
|
|
210
|
+
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
|
|
211
|
+
<title>Feature spec reports</title>
|
|
212
|
+
<style>
|
|
213
|
+
body { font-family: system-ui, sans-serif; max-width: 48rem; margin: 3rem auto; padding: 0 1rem; line-height: 1.5; }
|
|
214
|
+
li { margin: 0.35rem 0; }
|
|
215
|
+
</style>
|
|
216
|
+
</head>
|
|
217
|
+
<body>
|
|
218
|
+
<h1>Feature spec reports</h1>
|
|
219
|
+
<p>Generated ${escapeHtml(generatedAt)}.</p>
|
|
220
|
+
<ol>
|
|
221
|
+
${rows}
|
|
222
|
+
</ol>
|
|
223
|
+
</body>
|
|
224
|
+
</html>
|
|
225
|
+
`;
|
|
226
|
+
}
|
|
227
|
+
function buildUrl(baseUrl, build) {
|
|
228
|
+
return `${trimTrailingSlash(baseUrl)}/${encodeURIComponent(build)}/`;
|
|
229
|
+
}
|
|
230
|
+
function indexUrl(baseUrl) {
|
|
231
|
+
return `${trimTrailingSlash(baseUrl)}/`;
|
|
232
|
+
}
|
|
233
|
+
function trimTrailingSlash(value) {
|
|
234
|
+
return value.replace(/\/+$/, "");
|
|
235
|
+
}
|
|
236
|
+
function escapeHtml(value) {
|
|
237
|
+
return value
|
|
238
|
+
.replace(/&/g, "&")
|
|
239
|
+
.replace(/</g, "<")
|
|
240
|
+
.replace(/>/g, ">")
|
|
241
|
+
.replace(/\"/g, """);
|
|
242
|
+
}
|
|
243
|
+
async function writeGithubSummary(markdown) {
|
|
244
|
+
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
245
|
+
if (!summaryPath)
|
|
246
|
+
return;
|
|
247
|
+
await mkdir(dirname(summaryPath), { recursive: true });
|
|
248
|
+
const existing = await readTextIfExists(summaryPath);
|
|
249
|
+
await writeFile(summaryPath, `${existing}${markdown}`, "utf8");
|
|
250
|
+
}
|
|
251
|
+
async function writeGithubOutput(values) {
|
|
252
|
+
const outputPath = process.env.GITHUB_OUTPUT;
|
|
253
|
+
if (!outputPath)
|
|
254
|
+
return;
|
|
255
|
+
const lines = Object.entries(values)
|
|
256
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
257
|
+
.join("\n");
|
|
258
|
+
const existing = await readTextIfExists(outputPath);
|
|
259
|
+
await writeFile(outputPath, `${existing}${lines}\n`, "utf8");
|
|
260
|
+
}
|
|
261
|
+
async function readTextIfExists(filePath) {
|
|
262
|
+
try {
|
|
263
|
+
return await readFile(filePath, "utf8");
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return "";
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=githubActionReport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"githubActionReport.js","sourceRoot":"","sources":["../src/githubActionReport.ts"],"names":[],"mappings":"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"}
|
package/docs/releasing.md
CHANGED
|
@@ -6,15 +6,15 @@ Bare semver tags are valid git tags and match the package version exactly. The `
|
|
|
6
6
|
|
|
7
7
|
## Release Candidate
|
|
8
8
|
|
|
9
|
-
Use an RC when you want a testable npm package for the next release without updating the `latest` npm dist-tag.
|
|
9
|
+
Use an RC when you want a testable npm package for the next release without updating the `latest` npm dist-tag or permanently changing the repository version.
|
|
10
10
|
|
|
11
|
-
1. Make sure `main` contains the intended
|
|
12
|
-
- For
|
|
13
|
-
- For
|
|
11
|
+
1. Make sure `main` contains the intended stable base version in `package.json` and `package-lock.json`.
|
|
12
|
+
- For a release candidate for `0.2.1`, the base version should be `0.2.1`.
|
|
13
|
+
- For a release candidate for `0.3.0`, the base version should be `0.3.0`.
|
|
14
14
|
2. Open GitHub Actions.
|
|
15
|
-
3. Run the `Publish to npm` workflow from `main`.
|
|
16
|
-
4.
|
|
17
|
-
5. The workflow runs verification, changes the package version only inside the workflow to `<base>-rc.<
|
|
15
|
+
3. Run the `Publish RC to npm` workflow from `main`.
|
|
16
|
+
4. Keep the RC number at `1` for the first release candidate, or increase it manually for later candidates.
|
|
17
|
+
5. The workflow runs verification, changes the package version only inside the workflow to `<base>-rc.<number>`, publishes it with the npm `rc` dist-tag, and writes the published version to the run summary.
|
|
18
18
|
|
|
19
19
|
Install the latest RC with:
|
|
20
20
|
|
|
@@ -25,83 +25,45 @@ npm install @anselmdk/feature-spec-md@rc
|
|
|
25
25
|
Install a specific RC with:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
npm install @anselmdk/feature-spec-md@0.2.
|
|
28
|
+
npm install @anselmdk/feature-spec-md@0.2.1-rc.1
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
Repeat the workflow as needed.
|
|
31
|
+
Repeat the workflow as needed. Increase the RC number for each published candidate so npm receives a unique version, for example `0.2.1-rc.2`, and the workflow moves the `rc` dist-tag to that version.
|
|
32
32
|
|
|
33
|
-
## Stable
|
|
33
|
+
## Stable Release
|
|
34
34
|
|
|
35
|
-
Use
|
|
35
|
+
Use a stable release when you want to publish the next package version to the npm `latest` dist-tag.
|
|
36
36
|
|
|
37
|
-
1.
|
|
38
|
-
2.
|
|
37
|
+
1. Open GitHub Actions.
|
|
38
|
+
2. Run the `Publish Stable to npm` workflow from `main`.
|
|
39
|
+
3. Choose the version bump:
|
|
40
|
+
- `patch`, for example `0.2.1` -> `0.2.2`.
|
|
41
|
+
- `minor`, for example `0.2.1` -> `0.3.0`.
|
|
42
|
+
- `major`, for example `0.2.1` -> `1.0.0`.
|
|
43
|
+
4. The workflow runs verification, bumps `package.json` and `package-lock.json`, commits the release version, creates a bare semver git tag, pushes the commit and tag, publishes the package with the npm `latest` dist-tag, and writes the published version to the run summary.
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
npm version minor --no-git-tag-version
|
|
42
|
-
```
|
|
45
|
+
You do not need to manually run `npm version`, edit `package.json`, edit `package-lock.json`, or create a git tag for stable releases. The workflow does that for you.
|
|
43
46
|
|
|
44
|
-
|
|
47
|
+
Install the latest stable version with:
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
4. Open and merge a pull request containing the version bump and any release notes.
|
|
52
|
-
5. After the PR is merged, update local `main` and create a bare semver tag that exactly matches `package.json`:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
git checkout main
|
|
56
|
-
git pull --ff-only
|
|
57
|
-
git tag 0.2.0
|
|
58
|
-
git push origin 0.2.0
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
6. Confirm the `Publish to npm` workflow succeeds.
|
|
62
|
-
|
|
63
|
-
The workflow validates that the tag is a stable semver version and exactly matches `package.json` before publishing to npm with the `latest` dist-tag.
|
|
64
|
-
|
|
65
|
-
## Stable Major Release
|
|
66
|
-
|
|
67
|
-
Use this for breaking changes.
|
|
68
|
-
|
|
69
|
-
1. Document the breaking changes in the release PR.
|
|
70
|
-
2. Bump the package version:
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
npm version major --no-git-tag-version
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
3. Run verification locally:
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
npm run verify
|
|
80
|
-
npm run format
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
4. Open and merge the release PR.
|
|
84
|
-
5. Tag the merged commit with the exact version, for example:
|
|
49
|
+
```bash
|
|
50
|
+
npm install @anselmdk/feature-spec-md@latest
|
|
51
|
+
```
|
|
85
52
|
|
|
86
|
-
|
|
87
|
-
git checkout main
|
|
88
|
-
git pull --ff-only
|
|
89
|
-
git tag 1.0.0
|
|
90
|
-
git push origin 1.0.0
|
|
91
|
-
```
|
|
53
|
+
Install the exact version reported by the workflow summary, for example:
|
|
92
54
|
|
|
93
|
-
|
|
55
|
+
```bash
|
|
56
|
+
npm install @anselmdk/feature-spec-md@0.2.2
|
|
57
|
+
```
|
|
94
58
|
|
|
95
59
|
## npm Publishing Setup
|
|
96
60
|
|
|
97
|
-
The publish workflows are ready for npm provenance.
|
|
61
|
+
The publish workflows are ready for npm provenance. If you publish with an npm token, keep the `NPM_TOKEN` repository secret configured.
|
|
98
62
|
|
|
99
|
-
|
|
63
|
+
If you use npm trusted publishing instead of `NPM_TOKEN`, configure npmjs.com for the workflow files that publish this package:
|
|
100
64
|
|
|
101
65
|
- Publisher: GitHub Actions
|
|
102
66
|
- Repository owner: `anselmdk`
|
|
103
67
|
- Repository name: `feature-spec-md`
|
|
104
|
-
- Workflow filename: `publish.yml`
|
|
105
|
-
-
|
|
106
|
-
|
|
107
|
-
If trusted publishing is not configured yet, the workflow can still use the `NPM_TOKEN` repository secret. Keep RC and stable publishing in the same workflow because npm trusted publishing allows only one trusted publisher workflow per package.
|
|
68
|
+
- Workflow filename: `publish-rc.yml` for RC publishing
|
|
69
|
+
- Workflow filename: `publish-stable.yml` for stable publishing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anselmdk/feature-spec-md",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Markdown specs for AI-assisted, testable spec driven development across model, feature, stack, and design documents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"url": "git+https://github.com/anselmdk/feature-spec-md.git"
|
|
9
9
|
},
|
|
10
10
|
"bin": {
|
|
11
|
-
"feature-spec-md": "
|
|
11
|
+
"feature-spec-md": "dist/cli.js"
|
|
12
12
|
},
|
|
13
13
|
"exports": {
|
|
14
14
|
".": {
|