demo-this-pr 0.1.1 → 1.0.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/README.md +9 -3
- package/dist/cli.js +39 -4
- package/dist/runs.d.ts +5 -0
- package/dist/runs.js +29 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://cdn.jsdelivr.net/npm/demo-this-pr@0.
|
|
2
|
+
<img src="https://cdn.jsdelivr.net/npm/demo-this-pr@1.0.0/assets/demo-this-pr-logo.svg" alt="DEMO THIS PR animated wordmark" width="920">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
# demo-this-pr
|
|
@@ -70,7 +70,7 @@ For CI or agent runners, prefer a pinned package version and a deterministic
|
|
|
70
70
|
artifact directory:
|
|
71
71
|
|
|
72
72
|
```bash
|
|
73
|
-
npx demo-this-pr@0.
|
|
73
|
+
npx demo-this-pr@1.0.0 ci
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
The command exits non-zero when Git context or a reachable local app is missing.
|
|
@@ -207,13 +207,19 @@ Generate in CI without a visible browser or report viewer:
|
|
|
207
207
|
demo-this-pr ci
|
|
208
208
|
```
|
|
209
209
|
|
|
210
|
+
Delete all generated run artifacts for the current repository:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
demo-this-pr runs --delete
|
|
214
|
+
```
|
|
215
|
+
|
|
210
216
|
Jenkins example:
|
|
211
217
|
|
|
212
218
|
```groovy
|
|
213
219
|
stage('PR demo') {
|
|
214
220
|
steps {
|
|
215
221
|
sh 'npm ci'
|
|
216
|
-
sh 'npx demo-this-pr@0.
|
|
222
|
+
sh 'npx demo-this-pr@1.0.0 ci'
|
|
217
223
|
}
|
|
218
224
|
post {
|
|
219
225
|
always {
|
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
3
4
|
import { resolve } from "node:path";
|
|
4
5
|
import pc from "picocolors";
|
|
5
6
|
import { resolveAutoDemo } from "./auto.js";
|
|
6
7
|
import { createPrKit } from "./prKit.js";
|
|
7
8
|
import { parseDemoStep } from "./demoPlan.js";
|
|
8
|
-
import { defaultRunOutputDir } from "./runs.js";
|
|
9
|
+
import { defaultRunOutputDir, deleteRuns } from "./runs.js";
|
|
9
10
|
import { openReportViewer } from "./viewer.js";
|
|
10
11
|
const program = new Command();
|
|
12
|
+
const packageVersion = readPackageVersion();
|
|
11
13
|
program
|
|
12
14
|
.name("demo-this-pr")
|
|
13
15
|
.description("Generate reviewable PR demos from current unpushed changes.")
|
|
14
|
-
.version(
|
|
16
|
+
.version(packageVersion)
|
|
15
17
|
.argument("[repo]", "repository path; defaults to the current working directory")
|
|
16
18
|
.option("--cwd <path>", "repository path; useful when running from outside the target repo")
|
|
17
19
|
.option("--headless", "run Chromium without a visible window", false)
|
|
@@ -20,7 +22,7 @@ program
|
|
|
20
22
|
.action(async (repo, options) => {
|
|
21
23
|
let cleanup;
|
|
22
24
|
try {
|
|
23
|
-
const targetCwd =
|
|
25
|
+
const targetCwd = resolveTargetCwd(repo, options.cwd);
|
|
24
26
|
const autoDemo = await resolveAutoDemo(targetCwd, {
|
|
25
27
|
headless: options.headless ?? false,
|
|
26
28
|
output: options.output,
|
|
@@ -48,7 +50,7 @@ program
|
|
|
48
50
|
.action(async (repo, options) => {
|
|
49
51
|
let cleanup;
|
|
50
52
|
try {
|
|
51
|
-
const targetCwd =
|
|
53
|
+
const targetCwd = resolveTargetCwd(repo, options.cwd);
|
|
52
54
|
const autoDemo = await resolveAutoDemo(targetCwd, {
|
|
53
55
|
headless: !(options.headed ?? false),
|
|
54
56
|
output: options.output,
|
|
@@ -66,6 +68,31 @@ program
|
|
|
66
68
|
await cleanup?.();
|
|
67
69
|
}
|
|
68
70
|
});
|
|
71
|
+
program
|
|
72
|
+
.command("runs")
|
|
73
|
+
.description("Manage generated demo-this-pr run artifacts")
|
|
74
|
+
.argument("[repo]", "repository path; defaults to the current working directory")
|
|
75
|
+
.option("--cwd <path>", "repository path; useful when running from outside the target repo")
|
|
76
|
+
.option("--delete", "delete all run directories under .demo-this-pr/runs", false)
|
|
77
|
+
.action(async (repo, options) => {
|
|
78
|
+
if (!options.delete) {
|
|
79
|
+
process.stderr.write(`${pc.red("demo-this-pr:")} runs needs --delete\n`);
|
|
80
|
+
process.exitCode = 1;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const targetCwd = resolveTargetCwd(repo, options.cwd);
|
|
85
|
+
const result = await deleteRuns(targetCwd);
|
|
86
|
+
process.stdout.write(`${pc.green("Runs deleted")}\n`);
|
|
87
|
+
process.stdout.write(`Directory: ${result.runsDir}\n`);
|
|
88
|
+
process.stdout.write(`Deleted: ${result.deletedCount}\n`);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
92
|
+
process.stderr.write(`${pc.red("demo-this-pr:")} ${message}\n`);
|
|
93
|
+
process.exitCode = 1;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
69
96
|
program
|
|
70
97
|
.command("demo")
|
|
71
98
|
.description("Launch a local Chromium demo for the current changes and write the PR kit")
|
|
@@ -199,3 +226,11 @@ function printResult(result, label, notes) {
|
|
|
199
226
|
function collect(value, previous) {
|
|
200
227
|
return [...previous, value];
|
|
201
228
|
}
|
|
229
|
+
function resolveTargetCwd(repo, commandCwd) {
|
|
230
|
+
const rootOptions = program.opts();
|
|
231
|
+
return resolve(commandCwd ?? rootOptions.cwd ?? repo ?? process.cwd());
|
|
232
|
+
}
|
|
233
|
+
function readPackageVersion() {
|
|
234
|
+
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
235
|
+
return packageJson.version ?? "0.0.0";
|
|
236
|
+
}
|
package/dist/runs.d.ts
CHANGED
package/dist/runs.js
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
|
+
import { lstat, readdir, rm } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
1
3
|
export function defaultRunOutputDir(label) {
|
|
2
4
|
return `.demo-this-pr/runs/${timestamp()}-${slug(normalizeLabel(label))}`;
|
|
3
5
|
}
|
|
6
|
+
export async function deleteRuns(cwd) {
|
|
7
|
+
const runsDir = resolve(cwd, ".demo-this-pr", "runs");
|
|
8
|
+
try {
|
|
9
|
+
const stats = await lstat(runsDir);
|
|
10
|
+
if (stats.isSymbolicLink()) {
|
|
11
|
+
throw new Error(`Refusing to delete symlinked runs directory: ${runsDir}`);
|
|
12
|
+
}
|
|
13
|
+
if (!stats.isDirectory()) {
|
|
14
|
+
throw new Error(`Runs path exists but is not a directory: ${runsDir}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (isNotFoundError(error)) {
|
|
19
|
+
return { runsDir, deletedCount: 0 };
|
|
20
|
+
}
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
const entries = await readdir(runsDir);
|
|
24
|
+
await Promise.all(entries.map((entry) => rm(join(runsDir, entry), { recursive: true, force: true })));
|
|
25
|
+
return {
|
|
26
|
+
runsDir,
|
|
27
|
+
deletedCount: entries.length
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function isNotFoundError(error) {
|
|
31
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
32
|
+
}
|
|
4
33
|
function timestamp() {
|
|
5
34
|
const now = new Date();
|
|
6
35
|
const pad = (value, size = 2) => String(value).padStart(size, "0");
|
package/package.json
CHANGED