@gfxlabs/third-eye-cli 3.25.0 → 3.25.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/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,
@@ -1773,6 +1800,21 @@ const createTypedClient = (options) => {
1773
1800
  };
1774
1801
  //#endregion
1775
1802
  //#region src/api.ts
1803
+ var api_exports = /* @__PURE__ */ __exportAll({
1804
+ getAffectedStories: () => getAffectedStories,
1805
+ getApiToken: () => getApiToken,
1806
+ getGitInfoFromAPI: () => getGitInfoFromAPI,
1807
+ prepareUpload: () => prepareUpload,
1808
+ processShots: () => processShots,
1809
+ sendCheckCacheToAPI: () => sendCheckCacheToAPI,
1810
+ sendFinalizeToAPI: () => sendFinalizeToAPI,
1811
+ sendFindSquashMergeParentsToAPI: () => sendFindSquashMergeParentsToAPI,
1812
+ sendHasBuildsWithCommitsToAPI: () => sendHasBuildsWithCommitsToAPI,
1813
+ sendInitToAPI: () => sendInitToAPI,
1814
+ sendRecordLogsToAPI: () => sendRecordLogsToAPI,
1815
+ uploadShot: () => uploadShot,
1816
+ uploadStorybookArchive: () => uploadStorybookArchive
1817
+ });
1776
1818
  const createClient = (platformUrl, apiKey, apiToken) => createTypedClient({
1777
1819
  url: platformUrl,
1778
1820
  apiKey,
@@ -2253,6 +2295,16 @@ const platformRunner = async (config, apiToken) => {
2253
2295
  initialCommitsWithBuilds.push(lastCommit);
2254
2296
  } else log.process("info", "general", `📌 Last build on branch ${lastCommit.slice(0, 7)} not in local git (rebase?), skipping`);
2255
2297
  }
2298
+ if (config.baseBranch) {
2299
+ const mergeBase = getMergeBaseInfo(config.baseBranch);
2300
+ if (mergeBase) {
2301
+ const mbCommitsWithBuilds = await sendHasBuildsWithCommitsToAPI(config, apiToken, mergeBase.mergeBaseAncestors);
2302
+ for (const c of mbCommitsWithBuilds) if (!initialCommitsWithBuilds.includes(c)) {
2303
+ log.process("info", "general", `📌 Seeding with base branch build: ${c.slice(0, 7)}`);
2304
+ initialCommitsWithBuilds.push(c);
2305
+ }
2306
+ }
2307
+ }
2256
2308
  log.process("info", "general", "🔍 Resolving ancestor builds from git history...");
2257
2309
  const { parentCommits, visitedCommitsWithoutBuilds } = await getParentCommits((commits) => sendHasBuildsWithCommitsToAPI(config, apiToken, commits), {
2258
2310
  firstCommittedAtSeconds: gitInfo.firstBuildCreatedAt ?? void 0,
@@ -2372,6 +2424,90 @@ const platformRunner = async (config, apiToken) => {
2372
2424
  process.exit(1);
2373
2425
  }
2374
2426
  };
2427
+ const patchBuildRunner = async (patchBuildArg, config, apiToken) => {
2428
+ const { execSync } = await import("node:child_process");
2429
+ const parts = patchBuildArg.split("...");
2430
+ if (parts.length !== 2) {
2431
+ log.process("error", "general", `Invalid --patch-build format. Expected: head...base (e.g. feature...main)`);
2432
+ process.exit(1);
2433
+ }
2434
+ const [headRef, baseRef] = parts;
2435
+ log.process("info", "general", `🩹 Patch build: computing merge base between ${headRef} and ${baseRef}...`);
2436
+ let mergeBaseCommit;
2437
+ try {
2438
+ mergeBaseCommit = execSync(`git merge-base ${headRef} ${baseRef}`, { encoding: "utf-8" }).trim();
2439
+ } catch {
2440
+ log.process("error", "general", `Failed to compute merge base between ${headRef} and ${baseRef}`);
2441
+ process.exit(1);
2442
+ }
2443
+ log.process("info", "general", `📍 Merge base commit: ${mergeBaseCommit.slice(0, 7)}`);
2444
+ const { sendHasBuildsWithCommitsToAPI: checkCommits } = await Promise.resolve().then(() => api_exports);
2445
+ if ((await checkCommits(config, apiToken, [mergeBaseCommit])).length > 0) {
2446
+ log.process("info", "general", `✅ Build already exists for merge base ${mergeBaseCommit.slice(0, 7)}, no patch build needed.`);
2447
+ return;
2448
+ }
2449
+ const originalRef = execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
2450
+ const originalBranch = execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8" }).trim();
2451
+ log.process("info", "general", `📦 Saving current state (${originalBranch} @ ${originalRef.slice(0, 7)})...`);
2452
+ let hasStash = false;
2453
+ try {
2454
+ hasStash = !execSync("git stash", { encoding: "utf-8" }).trim().includes("No local changes");
2455
+ } catch {}
2456
+ try {
2457
+ log.process("info", "general", `🔄 Checking out merge base ${mergeBaseCommit.slice(0, 7)}...`);
2458
+ execSync(`git checkout ${mergeBaseCommit}`, { stdio: "pipe" });
2459
+ log.process("info", "general", "📦 Installing dependencies...");
2460
+ try {
2461
+ execSync("pnpm install --frozen-lockfile", {
2462
+ stdio: "pipe",
2463
+ timeout: 12e4
2464
+ });
2465
+ } catch {
2466
+ try {
2467
+ execSync("npm ci", {
2468
+ stdio: "pipe",
2469
+ timeout: 12e4
2470
+ });
2471
+ } catch {
2472
+ execSync("npm install", {
2473
+ stdio: "pipe",
2474
+ timeout: 12e4
2475
+ });
2476
+ }
2477
+ }
2478
+ log.process("info", "general", "📖 Building Storybook...");
2479
+ if (config.storybookShots?.storybookUrl) try {
2480
+ execSync("npx storybook build", {
2481
+ stdio: "pipe",
2482
+ timeout: 3e5
2483
+ });
2484
+ } catch {
2485
+ log.process("info", "general", "Storybook build command failed, trying build-storybook...");
2486
+ execSync("npx build-storybook", {
2487
+ stdio: "pipe",
2488
+ timeout: 3e5
2489
+ });
2490
+ }
2491
+ const originalCommitHash = config.commitHash;
2492
+ const originalRefName = config.commitRefName;
2493
+ config.commitHash = mergeBaseCommit;
2494
+ config.commitRefName = baseRef;
2495
+ log.process("info", "general", "🚀 Running Third Eye for merge base...");
2496
+ await platformRunner(config, apiToken);
2497
+ config.commitHash = originalCommitHash;
2498
+ config.commitRefName = originalRefName;
2499
+ log.process("info", "general", `✅ Patch build complete for merge base ${mergeBaseCommit.slice(0, 7)}`);
2500
+ } finally {
2501
+ log.process("info", "general", `🔄 Restoring original state...`);
2502
+ try {
2503
+ if (originalBranch === "HEAD") execSync(`git checkout ${originalRef}`, { stdio: "pipe" });
2504
+ else execSync(`git checkout ${originalBranch}`, { stdio: "pipe" });
2505
+ if (hasStash) execSync("git stash pop", { stdio: "pipe" });
2506
+ } catch (err) {
2507
+ log.process("error", "general", `Failed to restore git state: ${err instanceof Error ? err.message : String(err)}`);
2508
+ }
2509
+ }
2510
+ };
2375
2511
  //#endregion
2376
2512
  //#region src/docker-runner/utils.ts
2377
2513
  const executeDockerRun = async ({ version }) => {
@@ -2446,7 +2582,11 @@ const generatePagesFromSitemap = async () => {
2446
2582
  //#endregion
2447
2583
  //#region src/bin.ts
2448
2584
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
2449
- const commandArgs = yargs(hideBin(process.argv)).parseSync()._;
2585
+ const args = yargs(hideBin(process.argv)).option("patch-build", {
2586
+ type: "string",
2587
+ describe: "Create a patch build for the merge base. Format: head...base (e.g. feature...main)"
2588
+ }).parseSync();
2589
+ const commandArgs = args._;
2450
2590
  const version = getVersion();
2451
2591
  if (version) log.process("info", "general", `Version: ${version}`);
2452
2592
  (async () => {
@@ -2475,6 +2615,7 @@ if (version) log.process("info", "general", `Version: ${version}`);
2475
2615
  log.process("info", "general", `🚀 Starting Lost Pixel in 'platform' mode`);
2476
2616
  const apiToken = await getPlatformApiToken(config);
2477
2617
  if (commandArgs.includes("finalize")) await sendFinalizeToAPI(config, apiToken);
2618
+ else if (args["patch-build"]) await patchBuildRunner(args["patch-build"], config, apiToken);
2478
2619
  else await platformRunner(config, apiToken);
2479
2620
  } else {
2480
2621
  log.process("info", "general", `🚀 Starting Lost Pixel in 'generateOnly' mode`);