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.
- package/dist/index.js +99 -7
- 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:
|
|
43
|
-
console.log(chalk.dim(`
|
|
44
|
-
console.log(chalk.dim(`
|
|
45
|
-
console.log(chalk.dim(`
|
|
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.
|
|
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
|
+
}
|