demo-this-pr 0.1.2 → 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 CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="https://cdn.jsdelivr.net/npm/demo-this-pr@0.1.2/assets/demo-this-pr-logo.svg" alt="DEMO THIS PR animated wordmark" width="920">
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.1.2 ci
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.1.2 ci'
222
+ sh 'npx demo-this-pr@1.0.0 ci'
217
223
  }
218
224
  post {
219
225
  always {
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import pc from "picocolors";
6
6
  import { resolveAutoDemo } from "./auto.js";
7
7
  import { createPrKit } from "./prKit.js";
8
8
  import { parseDemoStep } from "./demoPlan.js";
9
- import { defaultRunOutputDir } from "./runs.js";
9
+ import { defaultRunOutputDir, deleteRuns } from "./runs.js";
10
10
  import { openReportViewer } from "./viewer.js";
11
11
  const program = new Command();
12
12
  const packageVersion = readPackageVersion();
@@ -22,7 +22,7 @@ program
22
22
  .action(async (repo, options) => {
23
23
  let cleanup;
24
24
  try {
25
- const targetCwd = resolve(options.cwd ?? repo ?? process.cwd());
25
+ const targetCwd = resolveTargetCwd(repo, options.cwd);
26
26
  const autoDemo = await resolveAutoDemo(targetCwd, {
27
27
  headless: options.headless ?? false,
28
28
  output: options.output,
@@ -50,7 +50,7 @@ program
50
50
  .action(async (repo, options) => {
51
51
  let cleanup;
52
52
  try {
53
- const targetCwd = resolve(options.cwd ?? repo ?? process.cwd());
53
+ const targetCwd = resolveTargetCwd(repo, options.cwd);
54
54
  const autoDemo = await resolveAutoDemo(targetCwd, {
55
55
  headless: !(options.headed ?? false),
56
56
  output: options.output,
@@ -68,6 +68,31 @@ program
68
68
  await cleanup?.();
69
69
  }
70
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
+ });
71
96
  program
72
97
  .command("demo")
73
98
  .description("Launch a local Chromium demo for the current changes and write the PR kit")
@@ -201,6 +226,10 @@ function printResult(result, label, notes) {
201
226
  function collect(value, previous) {
202
227
  return [...previous, value];
203
228
  }
229
+ function resolveTargetCwd(repo, commandCwd) {
230
+ const rootOptions = program.opts();
231
+ return resolve(commandCwd ?? rootOptions.cwd ?? repo ?? process.cwd());
232
+ }
204
233
  function readPackageVersion() {
205
234
  const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
206
235
  return packageJson.version ?? "0.0.0";
package/dist/runs.d.ts CHANGED
@@ -1 +1,6 @@
1
+ export interface DeleteRunsResult {
2
+ runsDir: string;
3
+ deletedCount: number;
4
+ }
1
5
  export declare function defaultRunOutputDir(label: string | undefined): string;
6
+ export declare function deleteRuns(cwd: string): Promise<DeleteRunsResult>;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "demo-this-pr",
3
- "version": "0.1.2",
3
+ "version": "1.0.0",
4
4
  "description": "Record local PR demos with isolated Playwright Chromium, MP4 evidence, test output, and review-ready reports.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",