@gfxlabs/third-eye-cli 3.25.0 → 3.25.2
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/bin.mjs +142 -7
- package/dist/bin.mjs.map +1 -1
- package/package.json +1 -1
package/dist/bin.mjs
CHANGED
|
@@ -27,6 +27,16 @@ import { RPCLink } from "@orpc/client/fetch";
|
|
|
27
27
|
import { execa } from "execa";
|
|
28
28
|
import { XMLParser } from "fast-xml-parser";
|
|
29
29
|
//#region \0rolldown/runtime.js
|
|
30
|
+
var __defProp = Object.defineProperty;
|
|
31
|
+
var __exportAll = (all, no_symbols) => {
|
|
32
|
+
let target = {};
|
|
33
|
+
for (var name in all) __defProp(target, name, {
|
|
34
|
+
get: all[name],
|
|
35
|
+
enumerable: true
|
|
36
|
+
});
|
|
37
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
38
|
+
return target;
|
|
39
|
+
};
|
|
30
40
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
31
41
|
//#endregion
|
|
32
42
|
//#region src/log.ts
|
|
@@ -1231,6 +1241,22 @@ const getPagesFromExternalLoader = async () => {
|
|
|
1231
1241
|
};
|
|
1232
1242
|
//#endregion
|
|
1233
1243
|
//#region src/shots/shots.ts
|
|
1244
|
+
/**
|
|
1245
|
+
* Wait for Storybook play functions to complete before screenshotting.
|
|
1246
|
+
* Checks __STORYBOOK_PREVIEW__.currentRender.phase and data-play-fn-done attribute.
|
|
1247
|
+
*/
|
|
1248
|
+
const waitForPlayFunction = async (page, shotItem, logger) => {
|
|
1249
|
+
if (shotItem.shotMode !== "storybook") return;
|
|
1250
|
+
try {
|
|
1251
|
+
await page.waitForFunction(() => {
|
|
1252
|
+
if (window.__STORYBOOK_PREVIEW__?.currentRender?.phase === "playing") return false;
|
|
1253
|
+
if (document.getElementById("storybook-root")?.dataset.playFnDone === "false") return false;
|
|
1254
|
+
return true;
|
|
1255
|
+
}, { timeout: config.timeouts?.loadState ?? 3e4 });
|
|
1256
|
+
} catch {
|
|
1257
|
+
logger.process("info", "general", `Play function wait timed out for ${shotItem.id} — proceeding with screenshot`);
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1234
1260
|
const takeScreenShot = async ({ browser, shotItem, logger }) => {
|
|
1235
1261
|
const context = await browser.newContext(shotItem.browserConfig);
|
|
1236
1262
|
const page = await context.newPage();
|
|
@@ -1275,6 +1301,7 @@ const takeScreenShot = async ({ browser, shotItem, logger }) => {
|
|
|
1275
1301
|
} catch (error) {
|
|
1276
1302
|
logger.process("error", "timeout", `Timeout while waiting for all network requests: ${shotItem.url}`, error);
|
|
1277
1303
|
}
|
|
1304
|
+
await waitForPlayFunction(page, shotItem, logger);
|
|
1278
1305
|
if (config.beforeScreenshot) await config.beforeScreenshot(page, {
|
|
1279
1306
|
shotMode: shotItem.shotMode,
|
|
1280
1307
|
id: shotItem.id,
|
|
@@ -1702,12 +1729,6 @@ const getParentCommits = async (hasBuildsWithCommits, options) => {
|
|
|
1702
1729
|
visitedCommitsWithoutBuilds: commitsWithoutBuilds
|
|
1703
1730
|
};
|
|
1704
1731
|
}
|
|
1705
|
-
if (commitsWithBuilds.length > 1) {
|
|
1706
|
-
const parentArgs = commitsWithBuilds.map((c) => `"${c}^@"`).join(" ");
|
|
1707
|
-
const maxOutput = execGit(`git rev-list ${commitsWithBuilds.join(" ")} --not ${parentArgs}`);
|
|
1708
|
-
const maxCommits = maxOutput ? maxOutput.split("\n").filter(Boolean) : [];
|
|
1709
|
-
if (maxCommits.length > 0) commitsWithBuilds = maxCommits;
|
|
1710
|
-
}
|
|
1711
1732
|
log.process("info", "general", `📌 Found ${commitsWithBuilds.length} ancestor build(s): ${commitsWithBuilds.map((c) => c.slice(0, 7)).join(", ")}`);
|
|
1712
1733
|
return {
|
|
1713
1734
|
parentCommits: commitsWithBuilds,
|
|
@@ -1773,6 +1794,21 @@ const createTypedClient = (options) => {
|
|
|
1773
1794
|
};
|
|
1774
1795
|
//#endregion
|
|
1775
1796
|
//#region src/api.ts
|
|
1797
|
+
var api_exports = /* @__PURE__ */ __exportAll({
|
|
1798
|
+
getAffectedStories: () => getAffectedStories,
|
|
1799
|
+
getApiToken: () => getApiToken,
|
|
1800
|
+
getGitInfoFromAPI: () => getGitInfoFromAPI,
|
|
1801
|
+
prepareUpload: () => prepareUpload,
|
|
1802
|
+
processShots: () => processShots,
|
|
1803
|
+
sendCheckCacheToAPI: () => sendCheckCacheToAPI,
|
|
1804
|
+
sendFinalizeToAPI: () => sendFinalizeToAPI,
|
|
1805
|
+
sendFindSquashMergeParentsToAPI: () => sendFindSquashMergeParentsToAPI,
|
|
1806
|
+
sendHasBuildsWithCommitsToAPI: () => sendHasBuildsWithCommitsToAPI,
|
|
1807
|
+
sendInitToAPI: () => sendInitToAPI,
|
|
1808
|
+
sendRecordLogsToAPI: () => sendRecordLogsToAPI,
|
|
1809
|
+
uploadShot: () => uploadShot,
|
|
1810
|
+
uploadStorybookArchive: () => uploadStorybookArchive
|
|
1811
|
+
});
|
|
1776
1812
|
const createClient = (platformUrl, apiKey, apiToken) => createTypedClient({
|
|
1777
1813
|
url: platformUrl,
|
|
1778
1814
|
apiKey,
|
|
@@ -2253,6 +2289,16 @@ const platformRunner = async (config, apiToken) => {
|
|
|
2253
2289
|
initialCommitsWithBuilds.push(lastCommit);
|
|
2254
2290
|
} else log.process("info", "general", `📌 Last build on branch ${lastCommit.slice(0, 7)} not in local git (rebase?), skipping`);
|
|
2255
2291
|
}
|
|
2292
|
+
if (config.baseBranch) {
|
|
2293
|
+
const mergeBase = getMergeBaseInfo(config.baseBranch);
|
|
2294
|
+
if (mergeBase) {
|
|
2295
|
+
const mbCommitsWithBuilds = await sendHasBuildsWithCommitsToAPI(config, apiToken, mergeBase.mergeBaseAncestors);
|
|
2296
|
+
for (const c of mbCommitsWithBuilds) if (!initialCommitsWithBuilds.includes(c)) {
|
|
2297
|
+
log.process("info", "general", `📌 Seeding with base branch build: ${c.slice(0, 7)}`);
|
|
2298
|
+
initialCommitsWithBuilds.push(c);
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2256
2302
|
log.process("info", "general", "🔍 Resolving ancestor builds from git history...");
|
|
2257
2303
|
const { parentCommits, visitedCommitsWithoutBuilds } = await getParentCommits((commits) => sendHasBuildsWithCommitsToAPI(config, apiToken, commits), {
|
|
2258
2304
|
firstCommittedAtSeconds: gitInfo.firstBuildCreatedAt ?? void 0,
|
|
@@ -2372,6 +2418,90 @@ const platformRunner = async (config, apiToken) => {
|
|
|
2372
2418
|
process.exit(1);
|
|
2373
2419
|
}
|
|
2374
2420
|
};
|
|
2421
|
+
const patchBuildRunner = async (patchBuildArg, config, apiToken) => {
|
|
2422
|
+
const { execSync } = await import("node:child_process");
|
|
2423
|
+
const parts = patchBuildArg.split("...");
|
|
2424
|
+
if (parts.length !== 2) {
|
|
2425
|
+
log.process("error", "general", `Invalid --patch-build format. Expected: head...base (e.g. feature...main)`);
|
|
2426
|
+
process.exit(1);
|
|
2427
|
+
}
|
|
2428
|
+
const [headRef, baseRef] = parts;
|
|
2429
|
+
log.process("info", "general", `🩹 Patch build: computing merge base between ${headRef} and ${baseRef}...`);
|
|
2430
|
+
let mergeBaseCommit;
|
|
2431
|
+
try {
|
|
2432
|
+
mergeBaseCommit = execSync(`git merge-base ${headRef} ${baseRef}`, { encoding: "utf-8" }).trim();
|
|
2433
|
+
} catch {
|
|
2434
|
+
log.process("error", "general", `Failed to compute merge base between ${headRef} and ${baseRef}`);
|
|
2435
|
+
process.exit(1);
|
|
2436
|
+
}
|
|
2437
|
+
log.process("info", "general", `📍 Merge base commit: ${mergeBaseCommit.slice(0, 7)}`);
|
|
2438
|
+
const { sendHasBuildsWithCommitsToAPI: checkCommits } = await Promise.resolve().then(() => api_exports);
|
|
2439
|
+
if ((await checkCommits(config, apiToken, [mergeBaseCommit])).length > 0) {
|
|
2440
|
+
log.process("info", "general", `✅ Build already exists for merge base ${mergeBaseCommit.slice(0, 7)}, no patch build needed.`);
|
|
2441
|
+
return;
|
|
2442
|
+
}
|
|
2443
|
+
const originalRef = execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
|
|
2444
|
+
const originalBranch = execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8" }).trim();
|
|
2445
|
+
log.process("info", "general", `📦 Saving current state (${originalBranch} @ ${originalRef.slice(0, 7)})...`);
|
|
2446
|
+
let hasStash = false;
|
|
2447
|
+
try {
|
|
2448
|
+
hasStash = !execSync("git stash", { encoding: "utf-8" }).trim().includes("No local changes");
|
|
2449
|
+
} catch {}
|
|
2450
|
+
try {
|
|
2451
|
+
log.process("info", "general", `🔄 Checking out merge base ${mergeBaseCommit.slice(0, 7)}...`);
|
|
2452
|
+
execSync(`git checkout ${mergeBaseCommit}`, { stdio: "pipe" });
|
|
2453
|
+
log.process("info", "general", "📦 Installing dependencies...");
|
|
2454
|
+
try {
|
|
2455
|
+
execSync("pnpm install --frozen-lockfile", {
|
|
2456
|
+
stdio: "pipe",
|
|
2457
|
+
timeout: 12e4
|
|
2458
|
+
});
|
|
2459
|
+
} catch {
|
|
2460
|
+
try {
|
|
2461
|
+
execSync("npm ci", {
|
|
2462
|
+
stdio: "pipe",
|
|
2463
|
+
timeout: 12e4
|
|
2464
|
+
});
|
|
2465
|
+
} catch {
|
|
2466
|
+
execSync("npm install", {
|
|
2467
|
+
stdio: "pipe",
|
|
2468
|
+
timeout: 12e4
|
|
2469
|
+
});
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
log.process("info", "general", "📖 Building Storybook...");
|
|
2473
|
+
if (config.storybookShots?.storybookUrl) try {
|
|
2474
|
+
execSync("npx storybook build", {
|
|
2475
|
+
stdio: "pipe",
|
|
2476
|
+
timeout: 3e5
|
|
2477
|
+
});
|
|
2478
|
+
} catch {
|
|
2479
|
+
log.process("info", "general", "Storybook build command failed, trying build-storybook...");
|
|
2480
|
+
execSync("npx build-storybook", {
|
|
2481
|
+
stdio: "pipe",
|
|
2482
|
+
timeout: 3e5
|
|
2483
|
+
});
|
|
2484
|
+
}
|
|
2485
|
+
const originalCommitHash = config.commitHash;
|
|
2486
|
+
const originalRefName = config.commitRefName;
|
|
2487
|
+
config.commitHash = mergeBaseCommit;
|
|
2488
|
+
config.commitRefName = baseRef;
|
|
2489
|
+
log.process("info", "general", "🚀 Running Third Eye for merge base...");
|
|
2490
|
+
await platformRunner(config, apiToken);
|
|
2491
|
+
config.commitHash = originalCommitHash;
|
|
2492
|
+
config.commitRefName = originalRefName;
|
|
2493
|
+
log.process("info", "general", `✅ Patch build complete for merge base ${mergeBaseCommit.slice(0, 7)}`);
|
|
2494
|
+
} finally {
|
|
2495
|
+
log.process("info", "general", `🔄 Restoring original state...`);
|
|
2496
|
+
try {
|
|
2497
|
+
if (originalBranch === "HEAD") execSync(`git checkout ${originalRef}`, { stdio: "pipe" });
|
|
2498
|
+
else execSync(`git checkout ${originalBranch}`, { stdio: "pipe" });
|
|
2499
|
+
if (hasStash) execSync("git stash pop", { stdio: "pipe" });
|
|
2500
|
+
} catch (err) {
|
|
2501
|
+
log.process("error", "general", `Failed to restore git state: ${err instanceof Error ? err.message : String(err)}`);
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
};
|
|
2375
2505
|
//#endregion
|
|
2376
2506
|
//#region src/docker-runner/utils.ts
|
|
2377
2507
|
const executeDockerRun = async ({ version }) => {
|
|
@@ -2446,7 +2576,11 @@ const generatePagesFromSitemap = async () => {
|
|
|
2446
2576
|
//#endregion
|
|
2447
2577
|
//#region src/bin.ts
|
|
2448
2578
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
2449
|
-
const
|
|
2579
|
+
const args = yargs(hideBin(process.argv)).option("patch-build", {
|
|
2580
|
+
type: "string",
|
|
2581
|
+
describe: "Create a patch build for the merge base. Format: head...base (e.g. feature...main)"
|
|
2582
|
+
}).parseSync();
|
|
2583
|
+
const commandArgs = args._;
|
|
2450
2584
|
const version = getVersion();
|
|
2451
2585
|
if (version) log.process("info", "general", `Version: ${version}`);
|
|
2452
2586
|
(async () => {
|
|
@@ -2475,6 +2609,7 @@ if (version) log.process("info", "general", `Version: ${version}`);
|
|
|
2475
2609
|
log.process("info", "general", `🚀 Starting Lost Pixel in 'platform' mode`);
|
|
2476
2610
|
const apiToken = await getPlatformApiToken(config);
|
|
2477
2611
|
if (commandArgs.includes("finalize")) await sendFinalizeToAPI(config, apiToken);
|
|
2612
|
+
else if (args["patch-build"]) await patchBuildRunner(args["patch-build"], config, apiToken);
|
|
2478
2613
|
else await platformRunner(config, apiToken);
|
|
2479
2614
|
} else {
|
|
2480
2615
|
log.process("info", "general", `🚀 Starting Lost Pixel in 'generateOnly' mode`);
|