@mastra/e2b 0.12.4-alpha.0 → 0.13.0-alpha.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.cjs +156 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +156 -51
- package/dist/index.js.map +1 -1
- package/dist/utils/repo-template.d.ts +65 -2
- package/dist/utils/repo-template.d.ts.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -127,7 +127,7 @@ function validatePrefix(prefix) {
|
|
|
127
127
|
* Arguments containing only safe characters are returned as-is.
|
|
128
128
|
* All others are wrapped in single quotes with embedded single quotes escaped.
|
|
129
129
|
*/
|
|
130
|
-
function shellQuote$
|
|
130
|
+
function shellQuote$2(arg) {
|
|
131
131
|
if (/^[a-zA-Z0-9._\-/@:=]+$/.test(arg)) return arg;
|
|
132
132
|
return "'" + arg.replace(/'/g, "'\\''") + "'";
|
|
133
133
|
}
|
|
@@ -180,12 +180,12 @@ Error details: ${installResult.stderr || installResult.stdout}`);
|
|
|
180
180
|
};
|
|
181
181
|
if (hasCredentials) {
|
|
182
182
|
const credentialsContent = useSessionToken ? [
|
|
183
|
-
`export AWSACCESSKEYID=${shellQuote$
|
|
184
|
-
`export AWSSECRETACCESSKEY=${shellQuote$
|
|
185
|
-
`export AWSSESSIONTOKEN=${shellQuote$
|
|
186
|
-
`export AWS_ACCESS_KEY_ID=${shellQuote$
|
|
187
|
-
`export AWS_SECRET_ACCESS_KEY=${shellQuote$
|
|
188
|
-
`export AWS_SESSION_TOKEN=${shellQuote$
|
|
183
|
+
`export AWSACCESSKEYID=${shellQuote$2(config.accessKeyId)}`,
|
|
184
|
+
`export AWSSECRETACCESSKEY=${shellQuote$2(config.secretAccessKey)}`,
|
|
185
|
+
`export AWSSESSIONTOKEN=${shellQuote$2(config.sessionToken)}`,
|
|
186
|
+
`export AWS_ACCESS_KEY_ID=${shellQuote$2(config.accessKeyId)}`,
|
|
187
|
+
`export AWS_SECRET_ACCESS_KEY=${shellQuote$2(config.secretAccessKey)}`,
|
|
188
|
+
`export AWS_SESSION_TOKEN=${shellQuote$2(config.sessionToken)}`
|
|
189
189
|
].join("\n") : `${config.accessKeyId}:${config.secretAccessKey}`;
|
|
190
190
|
await sandbox.commands.run(`sudo rm -f ${credentialsPath}`);
|
|
191
191
|
try {
|
|
@@ -213,8 +213,8 @@ Error details: ${installResult.stderr || installResult.stdout}`);
|
|
|
213
213
|
mountOptions.push("ro");
|
|
214
214
|
logger.debug(`${LOG_PREFIX} Mounting as read-only`);
|
|
215
215
|
}
|
|
216
|
-
const s3fsCmd = `s3fs ${shellQuote$
|
|
217
|
-
const mountCmd = useSessionToken ? `sudo sh -c ${shellQuote$
|
|
216
|
+
const s3fsCmd = `s3fs ${shellQuote$2(bucketArg)} ${shellQuote$2(mountPath)} -o ${mountOptions.join(" -o ")}`;
|
|
217
|
+
const mountCmd = useSessionToken ? `sudo sh -c ${shellQuote$2(`. ${credentialsPath} && exec ${s3fsCmd}`)}` : `sudo ${s3fsCmd}`;
|
|
218
218
|
logger.debug(`${LOG_PREFIX} Mounting S3:`, hasCredentials ? mountCmd.replace(credentialsPath, "***") : mountCmd);
|
|
219
219
|
try {
|
|
220
220
|
const result = await sandbox.commands.run(mountCmd, { timeoutMs: 6e4 });
|
|
@@ -237,7 +237,7 @@ Error details: ${installResult.stderr || installResult.stdout}`);
|
|
|
237
237
|
} finally {
|
|
238
238
|
if (useSessionToken) await removeSessionCredentials();
|
|
239
239
|
}
|
|
240
|
-
if ((await sandbox.commands.run(`mountpoint -q ${shellQuote$
|
|
240
|
+
if ((await sandbox.commands.run(`mountpoint -q ${shellQuote$2(mountPath)}`)).exitCode !== 0) throw new Error(`s3fs returned exit 0 but ${mountPath} is not a mountpoint. The s3fs daemon likely failed during FUSE init (common causes: region mismatch, invalid credentials, or an S3-compatible endpoint that rejects the signature). Re-run inside the sandbox with '-f -o dbglevel=info' to see the underlying error.`);
|
|
241
241
|
}
|
|
242
242
|
//#endregion
|
|
243
243
|
//#region src/sandbox/mounts/gcs.ts
|
|
@@ -257,7 +257,7 @@ async function mountGCS(mountPath, config, ctx) {
|
|
|
257
257
|
}
|
|
258
258
|
const [uid, gid] = (await sandbox.commands.run("id -u && id -g")).stdout.trim().split("\n");
|
|
259
259
|
const uidGidFlags = uid && gid ? `--uid=${uid} --gid=${gid}` : "";
|
|
260
|
-
const onlyDirFlag = config.prefix ? ` --only-dir=${shellQuote$
|
|
260
|
+
const onlyDirFlag = config.prefix ? ` --only-dir=${shellQuote$2(validatePrefix(config.prefix))}` : "";
|
|
261
261
|
const hasCredentials = !!config.serviceAccountKey;
|
|
262
262
|
let mountCmd;
|
|
263
263
|
if (hasCredentials) {
|
|
@@ -465,9 +465,9 @@ Error details: ${verifyResult.stderr || verifyResult.stdout || installResult?.st
|
|
|
465
465
|
await sandbox.commands.run(`sudo rm -f ${configPath}`);
|
|
466
466
|
await sandbox.files.write(configPath, yaml);
|
|
467
467
|
await sandbox.commands.run(`sudo chown root:root ${configPath} && sudo chmod 600 ${configPath}`);
|
|
468
|
-
await sandbox.commands.run(`sudo rm -rf ${shellQuote$
|
|
469
|
-
const prefixFlags = prefix ? ` --virtual-directory=true --subdirectory=${shellQuote$
|
|
470
|
-
const mountCmd = `sudo blobfuse2 mount ${shellQuote$
|
|
468
|
+
await sandbox.commands.run(`sudo rm -rf ${shellQuote$2(cachePath)} && sudo mkdir -p ${shellQuote$2(cachePath)}`);
|
|
469
|
+
const prefixFlags = prefix ? ` --virtual-directory=true --subdirectory=${shellQuote$2(prefix)}` : "";
|
|
470
|
+
const mountCmd = `sudo blobfuse2 mount ${shellQuote$2(mountPath)} --config-file=${shellQuote$2(configPath)}${prefixFlags}`;
|
|
471
471
|
logger.debug(`${LOG_PREFIX} Mounting Azure Blob:`, mountCmd);
|
|
472
472
|
try {
|
|
473
473
|
const result = await sandbox.commands.run(mountCmd, { timeoutMs: 6e4 });
|
|
@@ -1594,6 +1594,8 @@ var E2BSandbox = class E2BSandbox extends _mastra_core_workspace.MastraSandbox {
|
|
|
1594
1594
|
};
|
|
1595
1595
|
//#endregion
|
|
1596
1596
|
//#region ../../packages/_internals/workspace/dist/index.js
|
|
1597
|
+
/** Directory, relative to the template's build cwd, that holds every marker below. */
|
|
1598
|
+
const SETUP_MARKER_DIR = ".mastra-sandbox";
|
|
1597
1599
|
/**
|
|
1598
1600
|
* Setup completion marker shared by repo templates and their consumers.
|
|
1599
1601
|
*
|
|
@@ -1603,8 +1605,29 @@ var E2BSandbox = class E2BSandbox extends _mastra_core_workspace.MastraSandbox {
|
|
|
1603
1605
|
* booted from the image tell whether the setup it is about to run already
|
|
1604
1606
|
* happened. Relative to the template's build cwd, which is also the runtime
|
|
1605
1607
|
* working directory the repo was cloned into.
|
|
1608
|
+
*
|
|
1609
|
+
* This is the single-repository marker. Templates built from a repository
|
|
1610
|
+
* list write one marker per repository instead ({@link repoSetupMarkerPath}).
|
|
1606
1611
|
*/
|
|
1607
1612
|
const SETUP_MARKER_PATH = ".mastra-sandbox/setup";
|
|
1613
|
+
/**
|
|
1614
|
+
* Marker for the workspace-level setup of a multi-repository template: the
|
|
1615
|
+
* commands that ran at the working directory after every repository was set
|
|
1616
|
+
* up. Written even when there were no such commands, so a consumer can tell
|
|
1617
|
+
* "ran nothing" from "never built".
|
|
1618
|
+
*/
|
|
1619
|
+
const WORKSPACE_SETUP_MARKER_PATH = ".mastra-sandbox/workspace-setup";
|
|
1620
|
+
/**
|
|
1621
|
+
* One line per repository directory whose setup failed while the build was
|
|
1622
|
+
* told to continue. Absent when nothing failed. Consumers check this before
|
|
1623
|
+
* trusting a per-repository marker: that marker means every step ran, not
|
|
1624
|
+
* that every step succeeded.
|
|
1625
|
+
*/
|
|
1626
|
+
const SETUP_FAILED_MARKER_PATH = ".mastra-sandbox/setup-failed";
|
|
1627
|
+
/** Per-repository marker of a multi-repository template, keyed by the clone directory name. */
|
|
1628
|
+
function repoSetupMarkerPath(repoDir) {
|
|
1629
|
+
return `${SETUP_MARKER_DIR}/repos/${repoDir}`;
|
|
1630
|
+
}
|
|
1608
1631
|
/** Blank entries never become build steps, so they never count toward the digest either. */
|
|
1609
1632
|
function normalizeSetupCommands$1(setupCommand) {
|
|
1610
1633
|
return (setupCommand === void 0 ? [] : Array.isArray(setupCommand) ? setupCommand : [setupCommand]).filter((command) => command.trim() !== "");
|
|
@@ -1614,8 +1637,26 @@ function setupMarkerContent(setupCommand) {
|
|
|
1614
1637
|
return `sha256:${(0, crypto.createHash)("sha256").update(normalizeSetupCommands$1(setupCommand).join("\n")).digest("hex")}`;
|
|
1615
1638
|
}
|
|
1616
1639
|
/** Shell step that writes the marker relative to the cwd. `content` is a digest, so it is shell-safe. */
|
|
1617
|
-
function setupMarkerCommand(content) {
|
|
1618
|
-
return `mkdir -p "$(dirname "${
|
|
1640
|
+
function setupMarkerCommand(content, markerPath = SETUP_MARKER_PATH) {
|
|
1641
|
+
return `mkdir -p "$(dirname "${markerPath}")" && printf '%s' '${content}' > "${markerPath}"`;
|
|
1642
|
+
}
|
|
1643
|
+
/** A clone directory name is one path segment of word characters, dots and dashes, so it never needs quoting. */
|
|
1644
|
+
const REPO_DIR_PATTERN = /^[\w.-]+$/;
|
|
1645
|
+
/**
|
|
1646
|
+
* Shell step that runs one setup command inside a repository clone.
|
|
1647
|
+
*
|
|
1648
|
+
* Without the guard this is the plain `cd "<dir>" && <cmd>` step. With it,
|
|
1649
|
+
* the `cd` sits inside the parenthesized group so that the failure branch
|
|
1650
|
+
* runs back at the build cwd and the failure list lands at the workspace
|
|
1651
|
+
* level, never inside the repository.
|
|
1652
|
+
*/
|
|
1653
|
+
function guardedSetupCommand({ repoDir, command, continueOnFailure }) {
|
|
1654
|
+
if (!REPO_DIR_PATTERN.test(repoDir)) throw new TypeError(`Repository directory "${repoDir}" must match ${REPO_DIR_PATTERN}.`);
|
|
1655
|
+
if (!continueOnFailure) return `cd "${repoDir}" && ${command}`;
|
|
1656
|
+
return `( cd "${repoDir}" && sh -c ${shellQuote$1(command)} ) || { mkdir -p "${SETUP_MARKER_DIR}" && grep -qxF -- '${repoDir}' "${SETUP_FAILED_MARKER_PATH}" 2>/dev/null || printf '%s\\n' '${repoDir}' >> "${SETUP_FAILED_MARKER_PATH}"; }`;
|
|
1657
|
+
}
|
|
1658
|
+
function shellQuote$1(value) {
|
|
1659
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1619
1660
|
}
|
|
1620
1661
|
function repoCloneCommand({ cloneUrl, destination, branch, tokenEnv }) {
|
|
1621
1662
|
return `git ${tokenEnv ? `${gitAuthFlag$1(tokenEnv)} ` : ""}clone --depth=1 --single-branch ${branch ? `--branch ${shellQuote(branch)} ` : ""}${shellQuote(cloneUrl)} ${shellQuote(destination)}`;
|
|
@@ -1707,8 +1748,16 @@ function isValidCloneUrl(cloneUrl) {
|
|
|
1707
1748
|
*/
|
|
1708
1749
|
function repoTemplateRef(identity) {
|
|
1709
1750
|
const name = repoTemplateName(identity);
|
|
1751
|
+
if (identity.repos) {
|
|
1752
|
+
const shas = identity.repos.map((repo) => repo.sha);
|
|
1753
|
+
return shas.every(Boolean) ? `${name}:${shaTag(listSha(shas))}` : `${name}:${CURRENT_TAG}`;
|
|
1754
|
+
}
|
|
1710
1755
|
return identity.sha ? `${name}:${shaTag(identity.sha)}` : `${name}:${CURRENT_TAG}`;
|
|
1711
1756
|
}
|
|
1757
|
+
/** One digest over every repository's pinned sha, in caller order. */
|
|
1758
|
+
function listSha(shas) {
|
|
1759
|
+
return (0, crypto.createHash)("sha256").update(shas.map((sha) => sha.toLowerCase()).join(",")).digest("hex");
|
|
1760
|
+
}
|
|
1712
1761
|
function repoTemplateName(identity) {
|
|
1713
1762
|
const cloneUrl = normalizeCloneUrl(identity.cloneUrl);
|
|
1714
1763
|
const config = [
|
|
@@ -1718,7 +1767,13 @@ function repoTemplateName(identity) {
|
|
|
1718
1767
|
identity.buildEnv ? Object.entries(identity.buildEnv).sort(([a], [b]) => a.localeCompare(b)) : null,
|
|
1719
1768
|
identity.cpuCount ?? 2,
|
|
1720
1769
|
identity.memoryMB ?? 1024,
|
|
1721
|
-
...identity.workingDirectory !== void 0 ? [identity.workingDirectory] : []
|
|
1770
|
+
...identity.workingDirectory !== void 0 ? [identity.workingDirectory] : [],
|
|
1771
|
+
...identity.repos ? [
|
|
1772
|
+
"repos",
|
|
1773
|
+
identity.repos.map((repo) => [normalizeCloneUrl(repo.cloneUrl), repo.setupCommand ?? null]),
|
|
1774
|
+
normalizeSetupCommands(identity.workspaceSetupCommand),
|
|
1775
|
+
identity.continueOnSetupFailure ?? false
|
|
1776
|
+
] : []
|
|
1722
1777
|
];
|
|
1723
1778
|
const hash = (0, crypto.createHash)("sha256").update(JSON.stringify(config)).digest("hex").slice(0, 8);
|
|
1724
1779
|
const { owner, repo } = parseCloneUrl(cloneUrl);
|
|
@@ -1750,7 +1805,9 @@ function shaTag(sha) {
|
|
|
1750
1805
|
* setup performs the full clone, so a broken build never wedges a session.
|
|
1751
1806
|
*/
|
|
1752
1807
|
function createRepoTemplate(options) {
|
|
1753
|
-
if (
|
|
1808
|
+
if (options.getRepositoryAccess && options.repos) throw new TypeError("createRepoTemplate: pass either getRepositoryAccess or repos, not both");
|
|
1809
|
+
if (!options.repos && (options.workspaceSetupCommand !== void 0 || options.continueOnSetupFailure !== void 0)) throw new TypeError("createRepoTemplate: workspaceSetupCommand and continueOnSetupFailure require repos");
|
|
1810
|
+
if (!options.getRepositoryAccess && !options.repos?.length) return void 0;
|
|
1754
1811
|
return { resolveSpec: async () => (await resolveSpecAtHead(options)).spec };
|
|
1755
1812
|
}
|
|
1756
1813
|
/**
|
|
@@ -1762,24 +1819,58 @@ function createRepoTemplate(options) {
|
|
|
1762
1819
|
* turns into its default-template fallback rather than a failed start.
|
|
1763
1820
|
*/
|
|
1764
1821
|
async function resolveSpecAtHead(options) {
|
|
1765
|
-
const
|
|
1766
|
-
const
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1822
|
+
const isList = options.repos !== void 0;
|
|
1823
|
+
const entries = options.repos ? options.repos : options.getRepositoryAccess ? [{
|
|
1824
|
+
getRepositoryAccess: options.getRepositoryAccess,
|
|
1825
|
+
setupCommand: options.setupCommand
|
|
1826
|
+
}] : [];
|
|
1827
|
+
if (entries.length === 0) throw new Error("Repo template has no clone URL: repository access returned none.");
|
|
1770
1828
|
const buildEnv = typeof options.buildEnv === "function" ? await options.buildEnv() : options.buildEnv;
|
|
1771
|
-
const
|
|
1772
|
-
const
|
|
1773
|
-
|
|
1774
|
-
|
|
1829
|
+
const repos = [];
|
|
1830
|
+
const seenDirs = /* @__PURE__ */ new Map();
|
|
1831
|
+
for (const [index, entry] of entries.entries()) {
|
|
1832
|
+
const access = await entry.getRepositoryAccess().catch(() => void 0);
|
|
1833
|
+
const cloneUrl = access?.cloneUrl;
|
|
1834
|
+
if (!cloneUrl) throw new Error(`Repo template has no clone URL: repository access returned none${isList ? ` (repos[${index}])` : ""}.`);
|
|
1835
|
+
assertCloneUrl(cloneUrl);
|
|
1836
|
+
const repoDir = repoDirName(normalizeCloneUrl(cloneUrl));
|
|
1837
|
+
const other = seenDirs.get(repoDir);
|
|
1838
|
+
if (other) throw new Error(`Repo template: repositories ${other} and ${cloneUrl} would both clone into "${repoDir}"`);
|
|
1839
|
+
seenDirs.set(repoDir, cloneUrl);
|
|
1840
|
+
const token = access?.authorization?.token;
|
|
1841
|
+
const resolved = await resolveDefaultBranchHead(cloneUrl, token).catch(() => void 0);
|
|
1842
|
+
const sha = resolved && SHA_PATTERN.test(resolved) ? resolved : void 0;
|
|
1843
|
+
repos.push({
|
|
1775
1844
|
cloneUrl,
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1845
|
+
repoDir,
|
|
1846
|
+
token,
|
|
1847
|
+
tokenEnv: token ? isList ? `${BUILD_TOKEN_ENV}_${index}` : BUILD_TOKEN_ENV : void 0,
|
|
1848
|
+
sha,
|
|
1849
|
+
setupCommand: entry.setupCommand
|
|
1850
|
+
});
|
|
1851
|
+
}
|
|
1852
|
+
const first = repos[0];
|
|
1853
|
+
const spec = buildRepoTemplateSpec({
|
|
1854
|
+
cloneUrl: first.cloneUrl,
|
|
1855
|
+
...first.sha ? { sha: first.sha } : {},
|
|
1856
|
+
...normalizeSetupCommands(first.setupCommand).length > 0 ? { setupCommand: first.setupCommand } : {},
|
|
1857
|
+
...buildEnv ? { buildEnv } : {},
|
|
1858
|
+
...options.cpuCount !== void 0 ? { cpuCount: options.cpuCount } : {},
|
|
1859
|
+
...options.memoryMB !== void 0 ? { memoryMB: options.memoryMB } : {},
|
|
1860
|
+
...options.workingDirectory !== void 0 ? { workingDirectory: trimTrailingSlashes(assertWorkingDirectory(options.workingDirectory)) } : {},
|
|
1861
|
+
...isList ? {
|
|
1862
|
+
repos: repos.map((repo) => ({
|
|
1863
|
+
cloneUrl: repo.cloneUrl,
|
|
1864
|
+
...repo.sha ? { sha: repo.sha } : {},
|
|
1865
|
+
...normalizeSetupCommands(repo.setupCommand).length > 0 ? { setupCommand: repo.setupCommand } : {}
|
|
1866
|
+
})),
|
|
1867
|
+
...options.workspaceSetupCommand !== void 0 ? { workspaceSetupCommand: options.workspaceSetupCommand } : {},
|
|
1868
|
+
...options.continueOnSetupFailure !== void 0 ? { continueOnSetupFailure: options.continueOnSetupFailure } : {}
|
|
1869
|
+
} : {}
|
|
1870
|
+
}, repos, options.continueOnSetupFailure ?? false);
|
|
1871
|
+
const sha = isList ? repos.every((repo) => repo.sha) ? listSha(repos.map((repo) => repo.sha)) : void 0 : first.sha;
|
|
1872
|
+
return {
|
|
1873
|
+
spec,
|
|
1783
1874
|
...sha ? { sha } : {}
|
|
1784
1875
|
};
|
|
1785
1876
|
}
|
|
@@ -1829,31 +1920,45 @@ function assertCloneUrl(cloneUrl) {
|
|
|
1829
1920
|
* REFERENCE — the token value never appears in the command string, and no
|
|
1830
1921
|
* credential is written to the build filesystem.
|
|
1831
1922
|
*/
|
|
1832
|
-
function gitAuthFlag() {
|
|
1833
|
-
return `-c http.extraheader="AUTHORIZATION: basic $(printf 'x-access-token:%s' "$${
|
|
1923
|
+
function gitAuthFlag(tokenEnv) {
|
|
1924
|
+
return `-c http.extraheader="AUTHORIZATION: basic $(printf 'x-access-token:%s' "$${tokenEnv}" | base64 -w0)"`;
|
|
1834
1925
|
}
|
|
1835
|
-
function buildRepoTemplateSpec(identity,
|
|
1836
|
-
const {
|
|
1837
|
-
const
|
|
1838
|
-
const repoDir = repoDirName(cloneUrl);
|
|
1839
|
-
const auth = token ? `${gitAuthFlag()} ` : "";
|
|
1926
|
+
function buildRepoTemplateSpec(identity, repos, continueOnFailure) {
|
|
1927
|
+
const { buildEnv, workingDirectory } = identity;
|
|
1928
|
+
const isList = identity.repos !== void 0;
|
|
1840
1929
|
let template = createDefaultMountableTemplate().template;
|
|
1841
1930
|
const env = { ...buildEnv };
|
|
1842
|
-
if (token) env[
|
|
1931
|
+
for (const repo of repos) if (repo.tokenEnv && repo.token) env[repo.tokenEnv] = repo.token;
|
|
1843
1932
|
if (Object.keys(env).length > 0) template = template.setEnvs(env);
|
|
1844
1933
|
if (workingDirectory) {
|
|
1845
1934
|
const dir = trimTrailingSlashes(workingDirectory);
|
|
1846
1935
|
template = template.runCmd(`mkdir -p "${dir}"`).setWorkdir(dir);
|
|
1847
1936
|
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1937
|
+
const ordered = [...repos.filter((repo) => !repo.token), ...repos.filter((repo) => repo.token)];
|
|
1938
|
+
for (const repo of ordered) {
|
|
1939
|
+
const { repoDir, sha, tokenEnv } = repo;
|
|
1940
|
+
const cloneUrl = normalizeCloneUrl(repo.cloneUrl);
|
|
1941
|
+
const auth = tokenEnv ? `${gitAuthFlag(tokenEnv)} ` : "";
|
|
1942
|
+
const setupCommands = normalizeSetupCommands(repo.setupCommand);
|
|
1943
|
+
template = template.runCmd(repoCloneCommand({
|
|
1944
|
+
cloneUrl,
|
|
1945
|
+
destination: repoDir,
|
|
1946
|
+
...tokenEnv ? { tokenEnv } : {}
|
|
1947
|
+
}));
|
|
1948
|
+
if (sha) template = template.runCmd(`git -C "${repoDir}" ${auth}fetch origin ${sha}`).runCmd(`git -C "${repoDir}" checkout ${sha}`);
|
|
1949
|
+
for (const command of setupCommands) template = template.runCmd(guardedSetupCommand({
|
|
1950
|
+
repoDir,
|
|
1951
|
+
command,
|
|
1952
|
+
continueOnFailure
|
|
1953
|
+
}));
|
|
1954
|
+
const content = setupMarkerContent(setupCommands);
|
|
1955
|
+
template = template.runCmd(isList ? setupMarkerCommand(content, repoSetupMarkerPath(repoDir)) : setupMarkerCommand(content));
|
|
1956
|
+
}
|
|
1957
|
+
if (isList) {
|
|
1958
|
+
const workspaceCommands = normalizeSetupCommands(identity.workspaceSetupCommand);
|
|
1959
|
+
for (const command of workspaceCommands) template = template.runCmd(command);
|
|
1960
|
+
template = template.runCmd(setupMarkerCommand(setupMarkerContent(workspaceCommands), WORKSPACE_SETUP_MARKER_PATH));
|
|
1961
|
+
}
|
|
1857
1962
|
return {
|
|
1858
1963
|
ref: repoTemplateRef(identity),
|
|
1859
1964
|
template,
|