git-shots-cli 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.
Files changed (2) hide show
  1. package/dist/index.js +99 -7
  2. package/package.json +26 -26
package/dist/index.js CHANGED
@@ -39,10 +39,11 @@ async function upload(config, options) {
39
39
  }
40
40
  const branch = options.branch ?? execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8" }).trim();
41
41
  const sha = options.sha ?? execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
42
- console.log(chalk.dim(`Project: ${config.project}`));
43
- console.log(chalk.dim(`Branch: ${branch}`));
44
- console.log(chalk.dim(`SHA: ${sha.slice(0, 7)}`));
45
- console.log(chalk.dim(`Dir: ${dir}`));
42
+ console.log(chalk.dim(`Project: ${config.project}`));
43
+ if (config.platform) console.log(chalk.dim(`Platform: ${config.platform}`));
44
+ console.log(chalk.dim(`Branch: ${branch}`));
45
+ console.log(chalk.dim(`SHA: ${sha.slice(0, 7)}`));
46
+ console.log(chalk.dim(`Dir: ${dir}`));
46
47
  console.log();
47
48
  const files = await glob("**/*.png", { cwd: dir });
48
49
  if (files.length === 0) {
@@ -54,6 +55,7 @@ async function upload(config, options) {
54
55
  formData.append("project", config.project);
55
56
  formData.append("branch", branch);
56
57
  formData.append("gitSha", sha);
58
+ if (config.platform) formData.append("platform", config.platform);
57
59
  for (const file of files) {
58
60
  const fullPath = resolve2(dir, file);
59
61
  const buffer = readFileSync2(fullPath);
@@ -443,24 +445,102 @@ async function hookUninstall(cwd = process.cwd()) {
443
445
  console.log(chalk6.green("Removed git-shots pre-push hook."));
444
446
  }
445
447
 
448
+ // src/flows.ts
449
+ import chalk7 from "chalk";
450
+ async function syncFlows(config) {
451
+ if (!config.flows || config.flows.length === 0) {
452
+ console.log(chalk7.dim("No flows defined in config, skipping sync."));
453
+ return;
454
+ }
455
+ console.log(chalk7.dim(`Syncing ${config.flows.length} flow(s)...`));
456
+ const listUrl = `${config.server}/api/projects/${config.project}/flows`;
457
+ let existingFlows = [];
458
+ try {
459
+ const res = await fetch(listUrl, { headers: { Origin: config.server } });
460
+ if (res.ok) {
461
+ const data = await res.json();
462
+ existingFlows = data.flows ?? [];
463
+ }
464
+ } catch {
465
+ }
466
+ const existingSlugs = new Set(existingFlows.map((f) => f.slug));
467
+ for (let i = 0; i < config.flows.length; i++) {
468
+ const flow = config.flows[i];
469
+ if (existingSlugs.has(flow.slug)) {
470
+ const patchUrl = `${config.server}/api/projects/${config.project}/flows/${flow.slug}`;
471
+ const patchRes = await fetch(patchUrl, {
472
+ method: "PATCH",
473
+ headers: { "Content-Type": "application/json", Origin: config.server },
474
+ body: JSON.stringify({
475
+ name: flow.name,
476
+ description: flow.description ?? null,
477
+ sort_order: i
478
+ })
479
+ });
480
+ if (!patchRes.ok) {
481
+ const err = await patchRes.text();
482
+ console.error(chalk7.red(` Failed to update flow ${flow.slug}: ${err}`));
483
+ continue;
484
+ }
485
+ const stepsUrl = `${config.server}/api/projects/${config.project}/flows/${flow.slug}/steps`;
486
+ const stepsRes = await fetch(stepsUrl, {
487
+ method: "PUT",
488
+ headers: { "Content-Type": "application/json", Origin: config.server },
489
+ body: JSON.stringify({ steps: flow.steps })
490
+ });
491
+ if (stepsRes.ok) {
492
+ console.log(chalk7.green(` Updated flow: ${flow.name} (${flow.steps.length} steps)`));
493
+ } else {
494
+ const err = await stepsRes.text();
495
+ console.error(chalk7.red(` Failed to update steps for ${flow.slug}: ${err}`));
496
+ }
497
+ } else {
498
+ const createUrl = `${config.server}/api/projects/${config.project}/flows`;
499
+ const res = await fetch(createUrl, {
500
+ method: "POST",
501
+ headers: { "Content-Type": "application/json", Origin: config.server },
502
+ body: JSON.stringify({
503
+ slug: flow.slug,
504
+ name: flow.name,
505
+ description: flow.description ?? null,
506
+ sort_order: i,
507
+ steps: flow.steps
508
+ })
509
+ });
510
+ if (res.ok) {
511
+ console.log(chalk7.green(` Created flow: ${flow.name} (${flow.steps.length} steps)`));
512
+ } else {
513
+ const err = await res.text();
514
+ console.error(chalk7.red(` Failed to create flow ${flow.slug}: ${err}`));
515
+ }
516
+ }
517
+ }
518
+ console.log(chalk7.green("Flows synced."));
519
+ }
520
+
446
521
  // src/index.ts
447
522
  var program = new Command();
448
523
  program.name("git-shots").description("CLI for git-shots visual regression platform").version("0.1.0");
449
- program.command("upload").description("Upload screenshots to git-shots").option("-p, --project <slug>", "Project slug").option("-s, --server <url>", "Server URL").option("-d, --directory <path>", "Screenshots directory").option("-b, --branch <name>", "Git branch (auto-detected)").option("--sha <hash>", "Git SHA (auto-detected)").action(async (options) => {
524
+ program.command("upload").description("Upload screenshots to git-shots").option("-p, --project <slug>", "Project slug").option("-s, --server <url>", "Server URL").option("-d, --directory <path>", "Screenshots directory").option("-b, --branch <name>", "Git branch (auto-detected)").option("--sha <hash>", "Git SHA (auto-detected)").option("--platform <name>", "Platform tag (e.g., android, web)").action(async (options) => {
450
525
  const config = loadConfig();
451
526
  if (options.project) config.project = options.project;
452
527
  if (options.server) config.server = options.server;
453
528
  if (options.directory) config.directory = options.directory;
529
+ if (options.platform) config.platform = options.platform;
454
530
  if (!config.project) {
455
531
  console.error("Error: project slug required. Use --project or .git-shots.json");
456
532
  process.exit(1);
457
533
  }
458
534
  await upload(config, { branch: options.branch, sha: options.sha });
535
+ if (config.flows && config.flows.length > 0) {
536
+ await syncFlows(config);
537
+ }
459
538
  });
460
- program.command("compare").description("Compare screenshots between branches").requiredOption("--head <branch>", "Head branch to compare").option("-p, --project <slug>", "Project slug").option("-s, --server <url>", "Server URL").option("--base <branch>", "Base branch (default: main)").option("-t, --threshold <number>", "Mismatch threshold 0-1", parseFloat).action(async (options) => {
539
+ program.command("compare").description("Compare screenshots between branches").requiredOption("--head <branch>", "Head branch to compare").option("-p, --project <slug>", "Project slug").option("-s, --server <url>", "Server URL").option("--base <branch>", "Base branch (default: main)").option("-t, --threshold <number>", "Mismatch threshold 0-1", parseFloat).option("--platform <name>", "Platform tag (e.g., android, web)").action(async (options) => {
461
540
  const config = loadConfig();
462
541
  if (options.project) config.project = options.project;
463
542
  if (options.server) config.server = options.server;
543
+ if (options.platform) config.platform = options.platform;
464
544
  if (!config.project) {
465
545
  console.error("Error: project slug required. Use --project or .git-shots.json");
466
546
  process.exit(1);
@@ -487,11 +567,12 @@ program.command("pull-baselines").description("Download baseline screenshots fro
487
567
  }
488
568
  await pullBaselines(config, { branch: options.branch, output: options.output });
489
569
  });
490
- program.command("review").description("Upload screenshots, create review session, and poll for verdict").option("-p, --project <slug>", "Project slug").option("-s, --server <url>", "Server URL").option("-d, --directory <path>", "Screenshots directory").option("-b, --branch <name>", "Git branch (auto-detected)").option("--sha <hash>", "Git SHA (auto-detected)").option("--open", "Open review URL in browser", true).option("--no-open", "Do not open review URL in browser").option("--poll", "Poll for verdict and exit with code", true).option("--no-poll", "Do not poll for verdict").option("--timeout <seconds>", "Polling timeout in seconds", parseInt, 300).action(async (options) => {
570
+ program.command("review").description("Upload screenshots, create review session, and poll for verdict").option("-p, --project <slug>", "Project slug").option("-s, --server <url>", "Server URL").option("-d, --directory <path>", "Screenshots directory").option("-b, --branch <name>", "Git branch (auto-detected)").option("--sha <hash>", "Git SHA (auto-detected)").option("--platform <name>", "Platform tag (e.g., android, web)").option("--open", "Open review URL in browser", true).option("--no-open", "Do not open review URL in browser").option("--poll", "Poll for verdict and exit with code", true).option("--no-poll", "Do not poll for verdict").option("--timeout <seconds>", "Polling timeout in seconds", parseInt, 300).action(async (options) => {
491
571
  const config = loadConfig();
492
572
  if (options.project) config.project = options.project;
493
573
  if (options.server) config.server = options.server;
494
574
  if (options.directory) config.directory = options.directory;
575
+ if (options.platform) config.platform = options.platform;
495
576
  if (!config.project) {
496
577
  console.error("Error: project slug required. Use --project or .git-shots.json");
497
578
  process.exit(1);
@@ -511,4 +592,15 @@ hook.command("install").description("Install a pre-push hook that runs git-shots
511
592
  hook.command("uninstall").description("Remove the git-shots pre-push hook").action(async () => {
512
593
  await hookUninstall();
513
594
  });
595
+ var flowsCmd = program.command("flows").description("Manage user flows");
596
+ flowsCmd.command("sync").description("Sync flows from .git-shots.json to the server").option("-p, --project <slug>", "Project slug").option("-s, --server <url>", "Server URL").action(async (options) => {
597
+ const config = loadConfig();
598
+ if (options.project) config.project = options.project;
599
+ if (options.server) config.server = options.server;
600
+ if (!config.project) {
601
+ console.error("Error: project slug required. Use --project or .git-shots.json");
602
+ process.exit(1);
603
+ }
604
+ await syncFlows(config);
605
+ });
514
606
  program.parse();
package/package.json CHANGED
@@ -1,26 +1,26 @@
1
- {
2
- "name": "git-shots-cli",
3
- "version": "0.2.1",
4
- "description": "CLI for git-shots visual regression platform",
5
- "type": "module",
6
- "bin": {
7
- "git-shots": "./dist/index.js"
8
- },
9
- "files": [
10
- "dist"
11
- ],
12
- "scripts": {
13
- "build": "tsup src/index.ts --format esm --dts",
14
- "dev": "tsup src/index.ts --format esm --watch"
15
- },
16
- "dependencies": {
17
- "commander": "^12.0.0",
18
- "chalk": "^5.3.0",
19
- "glob": "^11.0.0"
20
- },
21
- "devDependencies": {
22
- "tsup": "^8.0.0",
23
- "typescript": "^5.0.0",
24
- "@types/node": "^22.0.0"
25
- }
26
- }
1
+ {
2
+ "name": "git-shots-cli",
3
+ "version": "0.3.1",
4
+ "description": "CLI for git-shots visual regression platform",
5
+ "type": "module",
6
+ "bin": {
7
+ "git-shots": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup src/index.ts --format esm --dts",
14
+ "dev": "tsup src/index.ts --format esm --watch"
15
+ },
16
+ "dependencies": {
17
+ "commander": "^12.0.0",
18
+ "chalk": "^5.3.0",
19
+ "glob": "^11.0.0"
20
+ },
21
+ "devDependencies": {
22
+ "tsup": "^8.0.0",
23
+ "typescript": "^5.0.0",
24
+ "@types/node": "^22.0.0"
25
+ }
26
+ }