@mastra/platform-workspace 1.5.0-alpha.1 → 1.5.0-alpha.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/index.cjs +78 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +78 -25
- package/dist/index.js.map +1 -1
- package/dist/repo-template.d.ts +25 -5
- package/dist/repo-template.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1965,38 +1965,71 @@ const CLONE_URL_SEGMENT_PATTERN = /^[\w.-]+$/;
|
|
|
1965
1965
|
* pins repositories to their current default-branch commit. Private repository
|
|
1966
1966
|
* credentials are used for head resolution and sent to the provider as
|
|
1967
1967
|
* transient build envs; they never enter the serialized definition. If the
|
|
1968
|
-
* head cannot be resolved, the resolver
|
|
1969
|
-
*
|
|
1970
|
-
* the checkout
|
|
1968
|
+
* repository or its head cannot be resolved, the resolver keeps `cpuCount` and
|
|
1969
|
+
* `memoryMB` in a resources-only template so the sandbox still boots at the
|
|
1970
|
+
* requested size, and the caller's runtime setup materializes the checkout.
|
|
1971
1971
|
*/
|
|
1972
1972
|
function createRepoTemplate(options) {
|
|
1973
1973
|
const getRepositoryAccess = options.getRepositoryAccess;
|
|
1974
|
-
|
|
1974
|
+
const resourcesOnly = () => {
|
|
1975
|
+
if (options.cpuCount === void 0 && options.memoryMB === void 0) return void 0;
|
|
1976
|
+
return withResources(Template(), options);
|
|
1977
|
+
};
|
|
1978
|
+
if (!getRepositoryAccess) {
|
|
1979
|
+
const template = resourcesOnly();
|
|
1980
|
+
return template ? async () => template : void 0;
|
|
1981
|
+
}
|
|
1975
1982
|
const resolveHead = options.resolveHead ?? resolveDefaultBranchHead;
|
|
1976
1983
|
return async () => {
|
|
1977
|
-
|
|
1978
|
-
|
|
1984
|
+
let accessError;
|
|
1985
|
+
const access = await getRepositoryAccess().catch((error) => {
|
|
1986
|
+
accessError = error;
|
|
1987
|
+
});
|
|
1988
|
+
if (!access?.cloneUrl) {
|
|
1989
|
+
console.warn("[platform-workspace] repo template skipped: repository access unavailable", { error: redactSecrets(accessError) });
|
|
1990
|
+
return resourcesOnly();
|
|
1991
|
+
}
|
|
1979
1992
|
const cloneUrl = normalizeCloneUrl(access.cloneUrl);
|
|
1980
|
-
if (!isValidCloneUrl(cloneUrl))
|
|
1993
|
+
if (!isValidCloneUrl(cloneUrl)) {
|
|
1994
|
+
console.warn("[platform-workspace] repo template skipped: clone URL failed validation", { cloneUrl: redactSecrets(cloneUrl) });
|
|
1995
|
+
return resourcesOnly();
|
|
1996
|
+
}
|
|
1981
1997
|
const token = access.authorization?.token;
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1998
|
+
let headError;
|
|
1999
|
+
const sha = await (token ? resolveHead(cloneUrl, token) : resolveHead(cloneUrl)).catch((error) => {
|
|
2000
|
+
headError = error;
|
|
2001
|
+
});
|
|
2002
|
+
if (!sha || !SHA_PATTERN.test(sha)) {
|
|
2003
|
+
console.warn("[platform-workspace] repo template skipped: could not resolve default-branch head", {
|
|
2004
|
+
cloneUrl,
|
|
2005
|
+
sha,
|
|
2006
|
+
error: redactSecrets(headError)
|
|
2007
|
+
});
|
|
2008
|
+
return resourcesOnly();
|
|
2009
|
+
}
|
|
2010
|
+
const workingDirectory = options.workingDirectory === void 0 ? void 0 : trimTrailingSlashes(assertWorkingDirectory(options.workingDirectory));
|
|
2011
|
+
const repoDir = repoDirName(cloneUrl);
|
|
1985
2012
|
const auth = token ? `${gitAuthFlag()} ` : "";
|
|
1986
|
-
const
|
|
1987
|
-
|
|
1988
|
-
`git -C "${workdir}" ${auth}fetch origin ${sha}`,
|
|
1989
|
-
`git -C "${workdir}" checkout ${sha}`,
|
|
1990
|
-
...options.setupCommand ? [`cd "${workdir}" && ${options.setupCommand}`] : []
|
|
1991
|
-
];
|
|
1992
|
-
const family = `repo:${cloneUrl}:${workdir}`;
|
|
2013
|
+
const setupCommands = (options.setupCommand === void 0 ? [] : Array.isArray(options.setupCommand) ? options.setupCommand : [options.setupCommand]).filter((command) => command.trim() !== "");
|
|
2014
|
+
const family = `repo:${cloneUrl}:${workingDirectory ?? ""}/${repoDir}`;
|
|
1993
2015
|
let template = Template();
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2016
|
+
const buildEnv = {
|
|
2017
|
+
...options.buildEnv,
|
|
2018
|
+
...token ? { [BUILD_TOKEN_ENV]: token } : {}
|
|
2019
|
+
};
|
|
2020
|
+
if (Object.keys(buildEnv).length > 0) template = template.setEnvs(buildEnv, { ephemeral: true });
|
|
2021
|
+
template = withResources(template, options);
|
|
2022
|
+
if (workingDirectory) template = template.runCmd(`mkdir -p "${workingDirectory}"`).setWorkdir(workingDirectory);
|
|
2023
|
+
template = template.runCmd(`git ${auth}clone ${cloneUrl} "${repoDir}"`).runCmd(`git -C "${repoDir}" ${auth}fetch origin ${sha}`).runCmd(`git -C "${repoDir}" checkout ${sha}`);
|
|
2024
|
+
for (const command of setupCommands) template = template.runCmd(`cd "${repoDir}" && ${command}`);
|
|
2025
|
+
return template.withFamily(family);
|
|
1998
2026
|
};
|
|
1999
2027
|
}
|
|
2028
|
+
function withResources(template, options) {
|
|
2029
|
+
if (options.cpuCount !== void 0) template = template.cpuCount(options.cpuCount);
|
|
2030
|
+
if (options.memoryMB !== void 0) template = template.memoryMB(options.memoryMB);
|
|
2031
|
+
return template;
|
|
2032
|
+
}
|
|
2000
2033
|
function isValidCloneUrl(cloneUrl) {
|
|
2001
2034
|
if (cloneUrl.length > 2048 || !CLONE_URL_ALLOWED_CHARS.test(cloneUrl)) return false;
|
|
2002
2035
|
let url;
|
|
@@ -2022,8 +2055,26 @@ function normalizeCloneUrl(cloneUrl) {
|
|
|
2022
2055
|
return `${scheme.toLowerCase()}${host.toLowerCase()}`;
|
|
2023
2056
|
});
|
|
2024
2057
|
}
|
|
2025
|
-
function
|
|
2026
|
-
return
|
|
2058
|
+
function repoDirName(cloneUrl) {
|
|
2059
|
+
return (normalizeCloneUrl(cloneUrl).split("/").at(-1) ?? "").replace(/[^\w.-]/g, "-").replace(/^\.+/, "") || "repo";
|
|
2060
|
+
}
|
|
2061
|
+
function trimTrailingSlashes(path) {
|
|
2062
|
+
let end = path.length;
|
|
2063
|
+
while (end > 1 && path[end - 1] === "/") end--;
|
|
2064
|
+
return path.slice(0, end);
|
|
2065
|
+
}
|
|
2066
|
+
/** Validate a literal absolute path before embedding it in shell build steps. */
|
|
2067
|
+
function assertWorkingDirectory(dir) {
|
|
2068
|
+
if (!(/^\/[A-Za-z0-9._/-]*$/.test(dir) && !dir.split("/").includes(".."))) throw new Error(`Repo template workingDirectory must be an absolute path of plain path characters (got ${JSON.stringify(dir)}); ~ and $HOME are not expanded.`);
|
|
2069
|
+
return dir;
|
|
2070
|
+
}
|
|
2071
|
+
/**
|
|
2072
|
+
* Render a caught value for a warning without leaking credentials: URL
|
|
2073
|
+
* userinfo, HTTP authorization values, and GitHub token shapes are masked.
|
|
2074
|
+
*/
|
|
2075
|
+
function redactSecrets(value) {
|
|
2076
|
+
if (value === void 0 || value === null) return void 0;
|
|
2077
|
+
return (value instanceof Error ? value.message : typeof value === "string" ? value : String(value)).replace(/\/\/[^/@\s]+@/g, "//***@").replace(/\b(bearer|basic)\s+[^\s"']+/gi, "$1 ***").replace(/\b(gh[pousr]_|github_pat_)[A-Za-z0-9_]+/g, "$1***");
|
|
2027
2078
|
}
|
|
2028
2079
|
function gitAuthFlag() {
|
|
2029
2080
|
return `-c http.extraheader="AUTHORIZATION: basic $(printf 'x-access-token:%s' "$${BUILD_TOKEN_ENV}" | base64 -w0)"`;
|
|
@@ -2051,8 +2102,10 @@ async function resolveDefaultBranchHead(cloneUrl, token, execute = execFileAsync
|
|
|
2051
2102
|
});
|
|
2052
2103
|
const sha = stdout.trim().split(/\s+/, 1)[0];
|
|
2053
2104
|
return sha && SHA_PATTERN.test(sha) ? sha : void 0;
|
|
2054
|
-
} catch {
|
|
2055
|
-
|
|
2105
|
+
} catch (error) {
|
|
2106
|
+
const stderr = error.stderr;
|
|
2107
|
+
const detail = typeof stderr === "string" && stderr.trim() ? stderr.trim() : String(error);
|
|
2108
|
+
throw new Error(`git ls-remote failed: ${detail}`);
|
|
2056
2109
|
}
|
|
2057
2110
|
}
|
|
2058
2111
|
//#endregion
|