@gfxlabs/third-eye-cli 3.24.3 → 3.25.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/dist/bin.mjs +100 -26
- package/dist/bin.mjs.map +1 -1
- package/package.json +1 -1
package/dist/bin.mjs
CHANGED
|
@@ -184,7 +184,8 @@ z$1.object({
|
|
|
184
184
|
storyName: z$1.string().optional(),
|
|
185
185
|
storyId: z$1.string().optional(),
|
|
186
186
|
storyArgs: z$1.record(z$1.string(), z$1.unknown()).optional(),
|
|
187
|
-
tags: z$1.array(z$1.string()).optional()
|
|
187
|
+
tags: z$1.array(z$1.string()).optional(),
|
|
188
|
+
designUrl: z$1.string().optional()
|
|
188
189
|
});
|
|
189
190
|
//#endregion
|
|
190
191
|
//#region src/config.ts
|
|
@@ -1035,7 +1036,8 @@ const generateStorybookShotItems = (baseUrl, stories, mask, modeBreakpoints, bro
|
|
|
1035
1036
|
componentPath: story.kind,
|
|
1036
1037
|
storyName: story.story,
|
|
1037
1038
|
storyId: story.id,
|
|
1038
|
-
storyArgs: story.parameters?.thirdeye?.args
|
|
1039
|
+
storyArgs: story.parameters?.thirdeye?.args,
|
|
1040
|
+
designUrl: (story.parameters?.design)?.url
|
|
1039
1041
|
};
|
|
1040
1042
|
const storyLevelBreakpoints = story.parameters?.thirdeye?.breakpoints ?? [];
|
|
1041
1043
|
const breakpoints = selectBreakpoints(config.breakpoints, modeBreakpoints, storyLevelBreakpoints);
|
|
@@ -1640,6 +1642,12 @@ const createShots = async (turboSnapFilter) => {
|
|
|
1640
1642
|
//#region src/git.ts
|
|
1641
1643
|
const INITIAL_BATCH_SIZE = 20;
|
|
1642
1644
|
/**
|
|
1645
|
+
* Check if a commit exists in the local git index.
|
|
1646
|
+
*/
|
|
1647
|
+
const commitExists = (commit) => {
|
|
1648
|
+
return execGit(`git cat-file -t ${commit}`) === "commit";
|
|
1649
|
+
};
|
|
1650
|
+
/**
|
|
1643
1651
|
* Execute a git command and return the trimmed output.
|
|
1644
1652
|
*/
|
|
1645
1653
|
const execGit = (command) => {
|
|
@@ -1652,34 +1660,28 @@ const execGit = (command) => {
|
|
|
1652
1660
|
return "";
|
|
1653
1661
|
}
|
|
1654
1662
|
};
|
|
1655
|
-
|
|
1656
|
-
* Find the "covering" set of ancestor commits that have builds on the server.
|
|
1657
|
-
*
|
|
1658
|
-
* This mirrors Chromatic's approach:
|
|
1659
|
-
* 1. Walk git history with `git rev-list`
|
|
1660
|
-
* 2. Ask the server which commits have builds
|
|
1661
|
-
* 3. Use `--not` to exclude ancestors of commits with builds
|
|
1662
|
-
* 4. Repeat with exponentially larger batches until no more uncovered commits
|
|
1663
|
-
*
|
|
1664
|
-
* The result is the minimal set of ancestor commits with builds such that
|
|
1665
|
-
* every ancestor of HEAD either has a build or is an ancestor of a commit
|
|
1666
|
-
* with a build.
|
|
1667
|
-
*/
|
|
1668
|
-
const getParentCommits = async (hasBuildsWithCommits) => {
|
|
1663
|
+
const getParentCommits = async (hasBuildsWithCommits, options) => {
|
|
1669
1664
|
if (!execGit("git rev-parse HEAD")) {
|
|
1670
1665
|
log.process("info", "general", "Not a git repository, skipping ancestor detection");
|
|
1671
|
-
return
|
|
1666
|
+
return {
|
|
1667
|
+
parentCommits: [],
|
|
1668
|
+
visitedCommitsWithoutBuilds: []
|
|
1669
|
+
};
|
|
1672
1670
|
}
|
|
1673
1671
|
if (!execGit("git --no-pager log -n 1 --skip=1 --format=\"%H\"")) {
|
|
1674
1672
|
log.process("info", "general", "Initial commit, no ancestors");
|
|
1675
|
-
return
|
|
1673
|
+
return {
|
|
1674
|
+
parentCommits: [],
|
|
1675
|
+
visitedCommitsWithoutBuilds: []
|
|
1676
|
+
};
|
|
1676
1677
|
}
|
|
1677
|
-
|
|
1678
|
+
const sinceArg = options?.firstCommittedAtSeconds ? `--since ${options.firstCommittedAtSeconds}` : "";
|
|
1679
|
+
let commitsWithBuilds = [...options?.initialCommitsWithBuilds ?? []];
|
|
1678
1680
|
let commitsWithoutBuilds = [];
|
|
1679
1681
|
let limit = INITIAL_BATCH_SIZE;
|
|
1680
1682
|
for (;;) {
|
|
1681
1683
|
const notArgs = commitsWithBuilds.map((c) => c.trim()).join(" ");
|
|
1682
|
-
const output = execGit(`git rev-list HEAD -n ${limit + commitsWithoutBuilds.length}${notArgs ? ` --not ${notArgs}` : ""}`);
|
|
1684
|
+
const output = execGit(`git rev-list HEAD ${sinceArg} -n ${limit + commitsWithoutBuilds.length}${notArgs ? ` --not ${notArgs}` : ""}`);
|
|
1683
1685
|
const candidates = (output ? output.split("\n").filter(Boolean) : []).filter((c) => !commitsWithBuilds.includes(c)).filter((c) => !commitsWithoutBuilds.includes(c)).slice(0, limit);
|
|
1684
1686
|
if (candidates.length === 0) break;
|
|
1685
1687
|
log.process("info", "general", `🔍 Checking ${candidates.length} commits for builds (batch size ${limit})`);
|
|
@@ -1695,7 +1697,10 @@ const getParentCommits = async (hasBuildsWithCommits) => {
|
|
|
1695
1697
|
}
|
|
1696
1698
|
if (commitsWithBuilds.length === 0) {
|
|
1697
1699
|
log.process("info", "general", "No ancestor builds found — this may be the first build");
|
|
1698
|
-
return
|
|
1700
|
+
return {
|
|
1701
|
+
parentCommits: [],
|
|
1702
|
+
visitedCommitsWithoutBuilds: commitsWithoutBuilds
|
|
1703
|
+
};
|
|
1699
1704
|
}
|
|
1700
1705
|
if (commitsWithBuilds.length > 1) {
|
|
1701
1706
|
const parentArgs = commitsWithBuilds.map((c) => `"${c}^@"`).join(" ");
|
|
@@ -1704,7 +1709,31 @@ const getParentCommits = async (hasBuildsWithCommits) => {
|
|
|
1704
1709
|
if (maxCommits.length > 0) commitsWithBuilds = maxCommits;
|
|
1705
1710
|
}
|
|
1706
1711
|
log.process("info", "general", `📌 Found ${commitsWithBuilds.length} ancestor build(s): ${commitsWithBuilds.map((c) => c.slice(0, 7)).join(", ")}`);
|
|
1707
|
-
return
|
|
1712
|
+
return {
|
|
1713
|
+
parentCommits: commitsWithBuilds,
|
|
1714
|
+
visitedCommitsWithoutBuilds: commitsWithoutBuilds
|
|
1715
|
+
};
|
|
1716
|
+
};
|
|
1717
|
+
/**
|
|
1718
|
+
* Compute the merge base commit between the current HEAD and a base branch,
|
|
1719
|
+
* plus a list of ancestor commits on the base branch from that point backwards.
|
|
1720
|
+
* This gives the server enough git history to walk back and find a build
|
|
1721
|
+
* even if the exact merge base commit doesn't have one.
|
|
1722
|
+
*/
|
|
1723
|
+
const getMergeBaseInfo = (baseBranch) => {
|
|
1724
|
+
const mergeBaseCommit = execGit(`git merge-base HEAD ${execGit(`git rev-parse origin/${baseBranch}`) ? `origin/${baseBranch}` : baseBranch}`);
|
|
1725
|
+
if (!mergeBaseCommit) {
|
|
1726
|
+
log.process("info", "general", `Could not compute merge base with ${baseBranch}`);
|
|
1727
|
+
return null;
|
|
1728
|
+
}
|
|
1729
|
+
log.process("info", "general", `📍 Merge base with ${baseBranch}: ${mergeBaseCommit.slice(0, 7)}`);
|
|
1730
|
+
const ancestorsOutput = execGit(`git rev-list ${mergeBaseCommit} -n 50`);
|
|
1731
|
+
const mergeBaseAncestors = ancestorsOutput ? ancestorsOutput.split("\n").filter(Boolean) : [];
|
|
1732
|
+
log.process("info", "general", `📍 Got ${mergeBaseAncestors.length} merge base ancestors for build lookup`);
|
|
1733
|
+
return {
|
|
1734
|
+
mergeBaseCommit,
|
|
1735
|
+
mergeBaseAncestors
|
|
1736
|
+
};
|
|
1708
1737
|
};
|
|
1709
1738
|
//#endregion
|
|
1710
1739
|
//#region ../shared/dist/client.js
|
|
@@ -1775,7 +1804,7 @@ const getApiToken = async (config) => {
|
|
|
1775
1804
|
process.exit(1);
|
|
1776
1805
|
}
|
|
1777
1806
|
};
|
|
1778
|
-
const sendInitToAPI = async (config, apiToken, parentCommits) => {
|
|
1807
|
+
const sendInitToAPI = async (config, apiToken, parentCommits, mergeBaseInfo) => {
|
|
1779
1808
|
const client = createClient(config.thirdEyePlatform, void 0, apiToken);
|
|
1780
1809
|
return withRetry("init", () => client.orgs.projects.builds.init({
|
|
1781
1810
|
orgId: config.thirdEyeOrgId,
|
|
@@ -1785,7 +1814,9 @@ const sendInitToAPI = async (config, apiToken, parentCommits) => {
|
|
|
1785
1814
|
buildNumber: config.ciBuildNumber,
|
|
1786
1815
|
baseBranch: config.baseBranch || void 0,
|
|
1787
1816
|
prNumber: config.prNumber,
|
|
1788
|
-
parentCommits
|
|
1817
|
+
parentCommits,
|
|
1818
|
+
mergeBaseCommit: mergeBaseInfo?.mergeBaseCommit,
|
|
1819
|
+
mergeBaseAncestors: mergeBaseInfo?.mergeBaseAncestors
|
|
1789
1820
|
}));
|
|
1790
1821
|
};
|
|
1791
1822
|
const sendHasBuildsWithCommitsToAPI = async (config, apiToken, commits) => {
|
|
@@ -1796,6 +1827,22 @@ const sendHasBuildsWithCommitsToAPI = async (config, apiToken, commits) => {
|
|
|
1796
1827
|
commits
|
|
1797
1828
|
}))).commits;
|
|
1798
1829
|
};
|
|
1830
|
+
const getGitInfoFromAPI = async (config, apiToken, branch) => {
|
|
1831
|
+
const client = createClient(config.thirdEyePlatform, void 0, apiToken);
|
|
1832
|
+
return withRetry("getGitInfo", () => client.orgs.projects.builds.getGitInfo({
|
|
1833
|
+
orgId: config.thirdEyeOrgId,
|
|
1834
|
+
projectId: config.thirdEyeProjectId,
|
|
1835
|
+
branch
|
|
1836
|
+
}));
|
|
1837
|
+
};
|
|
1838
|
+
const sendFindSquashMergeParentsToAPI = async (config, apiToken, commits) => {
|
|
1839
|
+
const client = createClient(config.thirdEyePlatform, void 0, apiToken);
|
|
1840
|
+
return (await withRetry("findSquashMergeParents", () => client.orgs.projects.builds.findSquashMergeParents({
|
|
1841
|
+
orgId: config.thirdEyeOrgId,
|
|
1842
|
+
projectId: config.thirdEyeProjectId,
|
|
1843
|
+
commits
|
|
1844
|
+
}))).parents;
|
|
1845
|
+
};
|
|
1799
1846
|
const sendFinalizeToAPI = async (config, apiToken) => {
|
|
1800
1847
|
const client = createClient(config.thirdEyePlatform, void 0, apiToken);
|
|
1801
1848
|
return withRetry("finalize", () => client.orgs.projects.builds.finalize({
|
|
@@ -1918,7 +1965,8 @@ const uploadRequiredShots = async ({ config, apiToken, uploadToken, requiredFile
|
|
|
1918
1965
|
...shotItem.viewport ? { viewport: shotItem.viewport } : {},
|
|
1919
1966
|
...shotItem.breakpoint !== void 0 ? { breakpoint: shotItem.breakpoint } : {},
|
|
1920
1967
|
...domHtml ? { dom_html: domHtml } : {},
|
|
1921
|
-
...dependencyMap && shotItem.importPath && dependencyMap.has(shotItem.importPath) ? { dependencies: dependencyMap.get(shotItem.importPath) } : {}
|
|
1968
|
+
...dependencyMap && shotItem.importPath && dependencyMap.has(shotItem.importPath) ? { dependencies: dependencyMap.get(shotItem.importPath) } : {},
|
|
1969
|
+
...shotItem.designUrl ? { designUrl: shotItem.designUrl } : {}
|
|
1922
1970
|
},
|
|
1923
1971
|
logger
|
|
1924
1972
|
});
|
|
@@ -2195,8 +2243,34 @@ const platformRunner = async (config, apiToken) => {
|
|
|
2195
2243
|
`commitRefName = ${config.commitRefName}`,
|
|
2196
2244
|
`commitHash = ${config.commitHash}`
|
|
2197
2245
|
].join("\n - "));
|
|
2246
|
+
log.process("info", "general", "🔍 Querying server for git info...");
|
|
2247
|
+
const gitInfo = await getGitInfoFromAPI(config, apiToken, config.commitRefName);
|
|
2248
|
+
const initialCommitsWithBuilds = [];
|
|
2249
|
+
if (gitInfo.lastBuildOnBranch) {
|
|
2250
|
+
const lastCommit = gitInfo.lastBuildOnBranch.commit;
|
|
2251
|
+
if (commitExists(lastCommit)) {
|
|
2252
|
+
log.process("info", "general", `📌 Seeding with last build on branch: ${lastCommit.slice(0, 7)}`);
|
|
2253
|
+
initialCommitsWithBuilds.push(lastCommit);
|
|
2254
|
+
} else log.process("info", "general", `📌 Last build on branch ${lastCommit.slice(0, 7)} not in local git (rebase?), skipping`);
|
|
2255
|
+
}
|
|
2198
2256
|
log.process("info", "general", "🔍 Resolving ancestor builds from git history...");
|
|
2199
|
-
|
|
2257
|
+
const { parentCommits, visitedCommitsWithoutBuilds } = await getParentCommits((commits) => sendHasBuildsWithCommitsToAPI(config, apiToken, commits), {
|
|
2258
|
+
firstCommittedAtSeconds: gitInfo.firstBuildCreatedAt ?? void 0,
|
|
2259
|
+
initialCommitsWithBuilds: initialCommitsWithBuilds.length > 0 ? initialCommitsWithBuilds : void 0
|
|
2260
|
+
});
|
|
2261
|
+
if (visitedCommitsWithoutBuilds.length > 0) {
|
|
2262
|
+
log.process("info", "general", `🔀 Checking ${Math.min(visitedCommitsWithoutBuilds.length, 100)} commits for squash merges...`);
|
|
2263
|
+
try {
|
|
2264
|
+
const squashResult = await sendFindSquashMergeParentsToAPI(config, apiToken, visitedCommitsWithoutBuilds.slice(0, 100));
|
|
2265
|
+
for (const { buildCommit } of squashResult) if (commitExists(buildCommit) && !parentCommits.includes(buildCommit)) {
|
|
2266
|
+
log.process("info", "general", `🔀 Found squash merge parent: ${buildCommit.slice(0, 7)}`);
|
|
2267
|
+
parentCommits.push(buildCommit);
|
|
2268
|
+
}
|
|
2269
|
+
} catch (err) {
|
|
2270
|
+
log.process("info", "general", `Squash merge detection failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
await sendInitToAPI(config, apiToken, parentCommits, config.baseBranch ? getMergeBaseInfo(config.baseBranch) : null);
|
|
2200
2274
|
if (!await checkForCachedBuild(config, apiToken)) {
|
|
2201
2275
|
log.process("info", "general", "📂 Creating shot folders");
|
|
2202
2276
|
const createShotsStart = process.hrtime();
|