@infracraft/pulumi 1.16.3 → 1.16.5
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.
Potentially problematic release.
This version of @infracraft/pulumi might be problematic. Click here for more details.
- package/dist/sandbox.cjs +8 -2
- package/dist/sandbox.cjs.map +1 -1
- package/dist/sandbox.d.cts +3 -1
- package/dist/sandbox.d.cts.map +1 -1
- package/dist/sandbox.d.mts +3 -1
- package/dist/sandbox.d.mts.map +1 -1
- package/dist/sandbox.mjs +8 -2
- package/dist/sandbox.mjs.map +1 -1
- package/package.json +1 -1
package/dist/sandbox.cjs
CHANGED
|
@@ -38,7 +38,11 @@ function buildSandboxScript(options) {
|
|
|
38
38
|
const head = `REPO=$(git rev-parse --show-toplevel)`;
|
|
39
39
|
const runSetupAndCli = [setup, cli].filter(Boolean).join("; ");
|
|
40
40
|
if (!sandbox) return `${head}; cd "$REPO"; ${runSetupAndCli}`;
|
|
41
|
-
const makeSandbox = [
|
|
41
|
+
const makeSandbox = [
|
|
42
|
+
`PROJECT=$(basename "$REPO")`,
|
|
43
|
+
`SANDBOX=$(mktemp -d "${SANDBOX_ROOT}/$PROJECT-${env ? `${env}-${appName}` : appName}.XXXXXX")`,
|
|
44
|
+
`trap 'rm -rf "$SANDBOX"' EXIT`
|
|
45
|
+
].join("; ");
|
|
42
46
|
if (gitGuard) return `${head}; ${makeSandbox}; ${`git -C "$REPO" ls-files | ${buildSandboxFileFilter(excludePaths)} | rsync -a --files-from=- "$REPO"/ "$SANDBOX"/`}; cd "\$SANDBOX" && git init -q && git add -A; ${runSetupAndCli}`;
|
|
43
47
|
return `${head}; ${makeSandbox}; git -C "\$REPO" ls-files | rsync -a --files-from=- "\$REPO"/ "\$SANDBOX"/; cp -c -R "\$REPO/.git" "\$SANDBOX/.git" 2>/dev/null || cp -R "\$REPO/.git" "\$SANDBOX/.git"; cd "$SANDBOX"; ${runSetupAndCli}`;
|
|
44
48
|
}
|
|
@@ -59,7 +63,9 @@ var DeploySandbox = class extends _pulumi_pulumi.ComponentResource {
|
|
|
59
63
|
if (!_pulumi_pulumi.runtime.isDryRun()) this.prepareWorkspace();
|
|
60
64
|
this.registerOutputs({});
|
|
61
65
|
}
|
|
62
|
-
/** mkdir the workspace root and GC stale sandboxes (best-effort).
|
|
66
|
+
/** mkdir the workspace root and GC stale sandboxes (best-effort). Sandboxes are
|
|
67
|
+
* flat under the root (`/tmp/infracraft/<project>-<env>-<app>.XXXX`), so this
|
|
68
|
+
* sweeps any entry older than the stale threshold. */
|
|
63
69
|
prepareWorkspace() {
|
|
64
70
|
node_fs.mkdirSync(SANDBOX_ROOT, { recursive: true });
|
|
65
71
|
let entries = [];
|
package/dist/sandbox.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.cjs","names":["pulumi","fs","path"],"sources":["../src/sandbox.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\n/** Escapes a path literal for embedding inside an awk ERE. */\nfunction escapeAwkRegex(path: string): string {\n\treturn path.replace(/[.[\\]{}()*+?^$|\\\\/]/g, \"\\\\$&\");\n}\n\n/**\n * Builds the shell filter applied to newline-delimited `git ls-files` output\n * before `rsync --files-from`. For `apps/<x>` it drops the app's files but keeps\n * `apps/<x>/package.json` (the monorepo workspace graph needs it during build);\n * any other entry drops the path and its subtree. Returns `cat` (passthrough)\n * when nothing is excluded. Uses awk so it is portable across macOS and Linux.\n */\nexport function buildSandboxFileFilter(excludePaths: string[] = []): string {\n\tif (excludePaths.length === 0) {\n\t\treturn \"cat\";\n\t}\n\n\tconst clauses = excludePaths.map((entry) => {\n\t\tconst escaped = escapeAwkRegex(entry);\n\n\t\tif (entry.startsWith(\"apps/\")) {\n\t\t\treturn `!(/^${escaped}\\\\// && !/^${escaped}\\\\/package\\\\.json$/)`;\n\t\t}\n\n\t\treturn `!/^${escaped}(\\\\/|$)/`;\n\t});\n\n\treturn `awk '${clauses.join(\" && \")}'`;\n}\n\n/** Root of the per-deploy sandbox tree. The DeploySandbox resource GCs this. */\nconst SANDBOX_ROOT = \"/tmp/infracraft\";\n\ninterface SandboxScriptOptions {\n\t/** Whether to isolate into a /tmp copy (false → run in the live tree). */\n\tsandbox: boolean;\n\t/** Whether the sandbox `.git` is a metadata-free stub (true) or the real one. */\n\tgitGuard: boolean;\n\t/** Resource-derived name, used in the sandbox dir prefix. */\n\tappName: string;\n\t/** Stack/environment name, prefixed to the sandbox dir so leftovers and\n\t * concurrent deploys are identifiable (e.g. `staging-vercel-deploy-nexus.XXXX`). */\n\tenv?: string;\n\t/** Upload-scoping excludes; applied only in stub mode (see design spec). */\n\texcludePaths?: string[];\n\t/** Shell run in the working dir before `cli` (e.g. write railpack.json). */\n\tsetup?: string;\n\t/** Fully-formed platform deploy command (its exit code becomes the script's). */\n\tcli: string;\n}\n\n/**\n * Builds the shell for a deploy's `command.local.Command.create`. See\n * docs/superpowers/specs/2026-06-05-deploy-sandbox-design.md for the modes.\n */\nexport function buildSandboxScript(options: SandboxScriptOptions): string {\n\tconst { sandbox, gitGuard, appName, env, excludePaths, setup, cli } = options;\n\n\tconst head = `REPO=$(git rev-parse --show-toplevel)`;\n\tconst runSetupAndCli = [setup, cli].filter(Boolean).join(\"; \");\n\n\tif (!sandbox) {\n\t\treturn `${head}; cd \"$REPO\"; ${runSetupAndCli}`;\n\t}\n\n\t// Prefix the dir with the env so leftovers
|
|
1
|
+
{"version":3,"file":"sandbox.cjs","names":["pulumi","fs","path"],"sources":["../src/sandbox.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\n/** Escapes a path literal for embedding inside an awk ERE. */\nfunction escapeAwkRegex(path: string): string {\n\treturn path.replace(/[.[\\]{}()*+?^$|\\\\/]/g, \"\\\\$&\");\n}\n\n/**\n * Builds the shell filter applied to newline-delimited `git ls-files` output\n * before `rsync --files-from`. For `apps/<x>` it drops the app's files but keeps\n * `apps/<x>/package.json` (the monorepo workspace graph needs it during build);\n * any other entry drops the path and its subtree. Returns `cat` (passthrough)\n * when nothing is excluded. Uses awk so it is portable across macOS and Linux.\n */\nexport function buildSandboxFileFilter(excludePaths: string[] = []): string {\n\tif (excludePaths.length === 0) {\n\t\treturn \"cat\";\n\t}\n\n\tconst clauses = excludePaths.map((entry) => {\n\t\tconst escaped = escapeAwkRegex(entry);\n\n\t\tif (entry.startsWith(\"apps/\")) {\n\t\t\treturn `!(/^${escaped}\\\\// && !/^${escaped}\\\\/package\\\\.json$/)`;\n\t\t}\n\n\t\treturn `!/^${escaped}(\\\\/|$)/`;\n\t});\n\n\treturn `awk '${clauses.join(\" && \")}'`;\n}\n\n/** Root of the per-deploy sandbox tree. The DeploySandbox resource GCs this. */\nconst SANDBOX_ROOT = \"/tmp/infracraft\";\n\ninterface SandboxScriptOptions {\n\t/** Whether to isolate into a /tmp copy (false → run in the live tree). */\n\tsandbox: boolean;\n\t/** Whether the sandbox `.git` is a metadata-free stub (true) or the real one. */\n\tgitGuard: boolean;\n\t/** Resource-derived name, used in the sandbox dir prefix. */\n\tappName: string;\n\t/** Stack/environment name, prefixed to the sandbox dir so leftovers and\n\t * concurrent deploys are identifiable (e.g. `staging-vercel-deploy-nexus.XXXX`). */\n\tenv?: string;\n\t/** Upload-scoping excludes; applied only in stub mode (see design spec). */\n\texcludePaths?: string[];\n\t/** Shell run in the working dir before `cli` (e.g. write railpack.json). */\n\tsetup?: string;\n\t/** Fully-formed platform deploy command (its exit code becomes the script's). */\n\tcli: string;\n}\n\n/**\n * Builds the shell for a deploy's `command.local.Command.create`. See\n * docs/superpowers/specs/2026-06-05-deploy-sandbox-design.md for the modes.\n */\nexport function buildSandboxScript(options: SandboxScriptOptions): string {\n\tconst { sandbox, gitGuard, appName, env, excludePaths, setup, cli } = options;\n\n\tconst head = `REPO=$(git rev-parse --show-toplevel)`;\n\tconst runSetupAndCli = [setup, cli].filter(Boolean).join(\"; \");\n\n\tif (!sandbox) {\n\t\treturn `${head}; cd \"$REPO\"; ${runSetupAndCli}`;\n\t}\n\n\t// Prefix the dir with the project (repo folder name) and env so leftovers and\n\t// concurrent deploys — even across repos and stacks — are identifiable at a\n\t// glance (e.g. `mlm-nvr-production-railway-deploy-mesh.XXXX`).\n\tconst dirPrefix = env ? `${env}-${appName}` : appName;\n\n\tconst makeSandbox = [\n\t\t`PROJECT=$(basename \"$REPO\")`,\n\t\t`SANDBOX=$(mktemp -d \"${SANDBOX_ROOT}/$PROJECT-${dirPrefix}.XXXXXX\")`,\n\t\t`trap 'rm -rf \"$SANDBOX\"' EXIT`,\n\t].join(\"; \");\n\n\tif (gitGuard) {\n\t\tconst filter = buildSandboxFileFilter(excludePaths);\n\n\t\tconst copy =\n\t\t\t`git -C \"$REPO\" ls-files | ${filter} | ` +\n\t\t\t`rsync -a --files-from=- \"$REPO\"/ \"$SANDBOX\"/`;\n\n\t\tconst stubGit = `cd \"$SANDBOX\" && git init -q && git add -A`;\n\n\t\treturn `${head}; ${makeSandbox}; ${copy}; ${stubGit}; ${runSetupAndCli}`;\n\t}\n\n\t// Original `.git`, full clean tree (no filter, no reconciliation needed).\n\tconst copy = `git -C \"$REPO\" ls-files | rsync -a --files-from=- \"$REPO\"/ \"$SANDBOX\"/`;\n\n\tconst copyGit =\n\t\t`cp -c -R \"$REPO/.git\" \"$SANDBOX/.git\" 2>/dev/null || ` +\n\t\t`cp -R \"$REPO/.git\" \"$SANDBOX/.git\"`;\n\n\treturn `${head}; ${makeSandbox}; ${copy}; ${copyGit}; cd \"$SANDBOX\"; ${runSetupAndCli}`;\n}\n\n/** Cross-bundle brand: `instanceof` is unreliable when the seam and the resource\n * come from different built entries, so the seam detects sandboxes by this. */\nconst DEPLOY_SANDBOX_BRAND = Symbol.for(\"@infracraft/pulumi/DeploySandbox\");\n\n/** Sweep sandboxes orphaned by a hard-killed run (older than this). */\nconst STALE_SANDBOX_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Isolation marker + workspace lifecycle. Listing it in a deploy's `dependsOn`\n * makes that deploy run in an isolated `/tmp/infracraft` copy. Carries no config;\n * the repo root is derived at runtime by the deploy command.\n */\nexport class DeploySandbox extends pulumi.ComponentResource {\n\tconstructor(name: string, opts?: pulumi.ComponentResourceOptions) {\n\t\tsuper(\"infracraft:sandbox:DeploySandbox\", name, {}, opts);\n\n\t\t(this as Record<symbol, unknown>)[DEPLOY_SANDBOX_BRAND] = true;\n\n\t\tif (!pulumi.runtime.isDryRun()) {\n\t\t\tthis.prepareWorkspace();\n\t\t}\n\n\t\tthis.registerOutputs({});\n\t}\n\n\t/** mkdir the workspace root and GC stale sandboxes (best-effort). Sandboxes are\n\t * flat under the root (`/tmp/infracraft/<project>-<env>-<app>.XXXX`), so this\n\t * sweeps any entry older than the stale threshold. */\n\tprivate prepareWorkspace(): void {\n\t\tfs.mkdirSync(SANDBOX_ROOT, { recursive: true });\n\n\t\tlet entries: string[] = [];\n\n\t\ttry {\n\t\t\tentries = fs.readdirSync(SANDBOX_ROOT) as string[];\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\n\t\tconst now = Date.now();\n\n\t\tfor (const entry of entries) {\n\t\t\tconst full = path.join(SANDBOX_ROOT, entry);\n\n\t\t\ttry {\n\t\t\t\tif (now - fs.statSync(full).mtimeMs > STALE_SANDBOX_MS) {\n\t\t\t\t\tfs.rmSync(full, { recursive: true, force: true });\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Racing with an in-flight deploy's cleanup — ignore.\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Bundle-safe check for a `DeploySandbox` in a `dependsOn` array. */\nexport function isDeploySandbox(value: unknown): value is DeploySandbox {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t(value as Record<symbol, unknown>)[DEPLOY_SANDBOX_BRAND] === true\n\t);\n}\n"],"mappings":";;;;;;;;;;;AAKA,SAAS,eAAe,MAAsB;CAC7C,OAAO,KAAK,QAAQ,wBAAwB,MAAM;AACnD;;;;;;;;AASA,SAAgB,uBAAuB,eAAyB,CAAC,GAAW;CAC3E,IAAI,aAAa,WAAW,GAC3B,OAAO;CAaR,OAAO,QAVS,aAAa,KAAK,UAAU;EAC3C,MAAM,UAAU,eAAe,KAAK;EAEpC,IAAI,MAAM,WAAW,OAAO,GAC3B,OAAO,OAAO,QAAQ,aAAa,QAAQ;EAG5C,OAAO,MAAM,QAAQ;CACtB,CAEqB,EAAE,KAAK,MAAM,EAAE;AACrC;;AAGA,MAAM,eAAe;;;;;AAwBrB,SAAgB,mBAAmB,SAAuC;CACzE,MAAM,EAAE,SAAS,UAAU,SAAS,KAAK,cAAc,OAAO,QAAQ;CAEtE,MAAM,OAAO;CACb,MAAM,iBAAiB,CAAC,OAAO,GAAG,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;CAE7D,IAAI,CAAC,SACJ,OAAO,GAAG,KAAK,gBAAgB;CAQhC,MAAM,cAAc;EACnB;EACA,wBAAwB,aAAa,YAJpB,MAAM,GAAG,IAAI,GAAG,YAAY,QAIc;EAC3D;CACD,EAAE,KAAK,IAAI;CAEX,IAAI,UASH,OAAO,GAAG,KAAK,IAAI,YAAY,IAAI,6BARpB,uBAAuB,YAGH,EAAE,iDAKG,iDAAgB;CAUzD,OAAO,GAAG,KAAK,IAAI,YAAY,2LAAwC;AACxE;;;AAIA,MAAM,uBAAuB,OAAO,IAAI,kCAAkC;;AAG1E,MAAM,mBAAmB,OAAU,KAAK;;;;;;AAOxC,IAAa,gBAAb,cAAmCA,eAAO,kBAAkB;CAC3D,YAAY,MAAc,MAAwC;EACjE,MAAM,oCAAoC,MAAM,CAAC,GAAG,IAAI;EAExD,AAAC,KAAiC,wBAAwB;EAE1D,IAAI,CAACA,eAAO,QAAQ,SAAS,GAC5B,KAAK,iBAAiB;EAGvB,KAAK,gBAAgB,CAAC,CAAC;CACxB;;;;CAKA,AAAQ,mBAAyB;EAChC,QAAG,UAAU,cAAc,EAAE,WAAW,KAAK,CAAC;EAE9C,IAAI,UAAoB,CAAC;EAEzB,IAAI;GACH,UAAUC,QAAG,YAAY,YAAY;EACtC,QAAQ;GACP;EACD;EAEA,MAAM,MAAM,KAAK,IAAI;EAErB,KAAK,MAAM,SAAS,SAAS;GAC5B,MAAM,OAAOC,UAAK,KAAK,cAAc,KAAK;GAE1C,IAAI;IACH,IAAI,MAAMD,QAAG,SAAS,IAAI,EAAE,UAAU,kBACrC,QAAG,OAAO,MAAM;KAAE,WAAW;KAAM,OAAO;IAAK,CAAC;GAElD,QAAQ,CAER;EACD;CACD;AACD;;AAGA,SAAgB,gBAAgB,OAAwC;CACvE,OACC,OAAO,UAAU,YACjB,UAAU,QACT,MAAkC,0BAA0B;AAE/D"}
|
package/dist/sandbox.d.cts
CHANGED
|
@@ -39,7 +39,9 @@ declare function buildSandboxScript(options: SandboxScriptOptions): string;
|
|
|
39
39
|
*/
|
|
40
40
|
declare class DeploySandbox extends pulumi.ComponentResource {
|
|
41
41
|
constructor(name: string, opts?: pulumi.ComponentResourceOptions);
|
|
42
|
-
/** mkdir the workspace root and GC stale sandboxes (best-effort).
|
|
42
|
+
/** mkdir the workspace root and GC stale sandboxes (best-effort). Sandboxes are
|
|
43
|
+
* flat under the root (`/tmp/infracraft/<project>-<env>-<app>.XXXX`), so this
|
|
44
|
+
* sweeps any entry older than the stale threshold. */
|
|
43
45
|
private prepareWorkspace;
|
|
44
46
|
}
|
|
45
47
|
/** Bundle-safe check for a `DeploySandbox` in a `dependsOn` array. */
|
package/dist/sandbox.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.cts","names":[],"sources":["../src/sandbox.ts"],"mappings":";;;;;;;AAgBA;;;;iBAAgB,sBAAA,CAAuB,YAA2B;AAAA,UAqBxD,oBAAA;EAAoB;EAE7B,OAAA;EAF6B;EAI7B,QAAA;EAAA;EAEA,OAAA;EAGA;;EAAA,GAAA;EAMA;EAJA,YAAA;EAIG;EAFH,KAAA;EASiC;EAPjC,GAAA;AAAA;AAO+D;
|
|
1
|
+
{"version":3,"file":"sandbox.d.cts","names":[],"sources":["../src/sandbox.ts"],"mappings":";;;;;;;AAgBA;;;;iBAAgB,sBAAA,CAAuB,YAA2B;AAAA,UAqBxD,oBAAA;EAAoB;EAE7B,OAAA;EAF6B;EAI7B,QAAA;EAAA;EAEA,OAAA;EAGA;;EAAA,GAAA;EAMA;EAJA,YAAA;EAIG;EAFH,KAAA;EASiC;EAPjC,GAAA;AAAA;AAO+D;AAuDhE;;;AAvDgE,iBAAhD,kBAAA,CAAmB,OAA6B,EAApB,oBAAoB;;;;;;cAuDnD,aAAA,SAAsB,MAAA,CAAO,iBAAiB;cAC9C,IAAA,UAAc,IAAA,GAAO,MAAA,CAAO,wBAAA;EAehC;;AAAgB;EAAhB,QAAA,gBAAA;AAAA;;iBA4BO,eAAA,CAAgB,KAAA,YAAiB,KAAA,IAAS,aAAa"}
|
package/dist/sandbox.d.mts
CHANGED
|
@@ -39,7 +39,9 @@ declare function buildSandboxScript(options: SandboxScriptOptions): string;
|
|
|
39
39
|
*/
|
|
40
40
|
declare class DeploySandbox extends pulumi.ComponentResource {
|
|
41
41
|
constructor(name: string, opts?: pulumi.ComponentResourceOptions);
|
|
42
|
-
/** mkdir the workspace root and GC stale sandboxes (best-effort).
|
|
42
|
+
/** mkdir the workspace root and GC stale sandboxes (best-effort). Sandboxes are
|
|
43
|
+
* flat under the root (`/tmp/infracraft/<project>-<env>-<app>.XXXX`), so this
|
|
44
|
+
* sweeps any entry older than the stale threshold. */
|
|
43
45
|
private prepareWorkspace;
|
|
44
46
|
}
|
|
45
47
|
/** Bundle-safe check for a `DeploySandbox` in a `dependsOn` array. */
|
package/dist/sandbox.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.mts","names":[],"sources":["../src/sandbox.ts"],"mappings":";;;;;;;AAgBA;;;;iBAAgB,sBAAA,CAAuB,YAA2B;AAAA,UAqBxD,oBAAA;EAAoB;EAE7B,OAAA;EAF6B;EAI7B,QAAA;EAAA;EAEA,OAAA;EAGA;;EAAA,GAAA;EAMA;EAJA,YAAA;EAIG;EAFH,KAAA;EASiC;EAPjC,GAAA;AAAA;AAO+D;
|
|
1
|
+
{"version":3,"file":"sandbox.d.mts","names":[],"sources":["../src/sandbox.ts"],"mappings":";;;;;;;AAgBA;;;;iBAAgB,sBAAA,CAAuB,YAA2B;AAAA,UAqBxD,oBAAA;EAAoB;EAE7B,OAAA;EAF6B;EAI7B,QAAA;EAAA;EAEA,OAAA;EAGA;;EAAA,GAAA;EAMA;EAJA,YAAA;EAIG;EAFH,KAAA;EASiC;EAPjC,GAAA;AAAA;AAO+D;AAuDhE;;;AAvDgE,iBAAhD,kBAAA,CAAmB,OAA6B,EAApB,oBAAoB;;;;;;cAuDnD,aAAA,SAAsB,MAAA,CAAO,iBAAiB;cAC9C,IAAA,UAAc,IAAA,GAAO,MAAA,CAAO,wBAAA;EAehC;;AAAgB;EAAhB,QAAA,gBAAA;AAAA;;iBA4BO,eAAA,CAAgB,KAAA,YAAiB,KAAA,IAAS,aAAa"}
|
package/dist/sandbox.mjs
CHANGED
|
@@ -34,7 +34,11 @@ function buildSandboxScript(options) {
|
|
|
34
34
|
const head = `REPO=$(git rev-parse --show-toplevel)`;
|
|
35
35
|
const runSetupAndCli = [setup, cli].filter(Boolean).join("; ");
|
|
36
36
|
if (!sandbox) return `${head}; cd "$REPO"; ${runSetupAndCli}`;
|
|
37
|
-
const makeSandbox = [
|
|
37
|
+
const makeSandbox = [
|
|
38
|
+
`PROJECT=$(basename "$REPO")`,
|
|
39
|
+
`SANDBOX=$(mktemp -d "${SANDBOX_ROOT}/$PROJECT-${env ? `${env}-${appName}` : appName}.XXXXXX")`,
|
|
40
|
+
`trap 'rm -rf "$SANDBOX"' EXIT`
|
|
41
|
+
].join("; ");
|
|
38
42
|
if (gitGuard) return `${head}; ${makeSandbox}; ${`git -C "$REPO" ls-files | ${buildSandboxFileFilter(excludePaths)} | rsync -a --files-from=- "$REPO"/ "$SANDBOX"/`}; cd "\$SANDBOX" && git init -q && git add -A; ${runSetupAndCli}`;
|
|
39
43
|
return `${head}; ${makeSandbox}; git -C "\$REPO" ls-files | rsync -a --files-from=- "\$REPO"/ "\$SANDBOX"/; cp -c -R "\$REPO/.git" "\$SANDBOX/.git" 2>/dev/null || cp -R "\$REPO/.git" "\$SANDBOX/.git"; cd "$SANDBOX"; ${runSetupAndCli}`;
|
|
40
44
|
}
|
|
@@ -55,7 +59,9 @@ var DeploySandbox = class extends pulumi.ComponentResource {
|
|
|
55
59
|
if (!pulumi.runtime.isDryRun()) this.prepareWorkspace();
|
|
56
60
|
this.registerOutputs({});
|
|
57
61
|
}
|
|
58
|
-
/** mkdir the workspace root and GC stale sandboxes (best-effort).
|
|
62
|
+
/** mkdir the workspace root and GC stale sandboxes (best-effort). Sandboxes are
|
|
63
|
+
* flat under the root (`/tmp/infracraft/<project>-<env>-<app>.XXXX`), so this
|
|
64
|
+
* sweeps any entry older than the stale threshold. */
|
|
59
65
|
prepareWorkspace() {
|
|
60
66
|
fs.mkdirSync(SANDBOX_ROOT, { recursive: true });
|
|
61
67
|
let entries = [];
|
package/dist/sandbox.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.mjs","names":[],"sources":["../src/sandbox.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\n/** Escapes a path literal for embedding inside an awk ERE. */\nfunction escapeAwkRegex(path: string): string {\n\treturn path.replace(/[.[\\]{}()*+?^$|\\\\/]/g, \"\\\\$&\");\n}\n\n/**\n * Builds the shell filter applied to newline-delimited `git ls-files` output\n * before `rsync --files-from`. For `apps/<x>` it drops the app's files but keeps\n * `apps/<x>/package.json` (the monorepo workspace graph needs it during build);\n * any other entry drops the path and its subtree. Returns `cat` (passthrough)\n * when nothing is excluded. Uses awk so it is portable across macOS and Linux.\n */\nexport function buildSandboxFileFilter(excludePaths: string[] = []): string {\n\tif (excludePaths.length === 0) {\n\t\treturn \"cat\";\n\t}\n\n\tconst clauses = excludePaths.map((entry) => {\n\t\tconst escaped = escapeAwkRegex(entry);\n\n\t\tif (entry.startsWith(\"apps/\")) {\n\t\t\treturn `!(/^${escaped}\\\\// && !/^${escaped}\\\\/package\\\\.json$/)`;\n\t\t}\n\n\t\treturn `!/^${escaped}(\\\\/|$)/`;\n\t});\n\n\treturn `awk '${clauses.join(\" && \")}'`;\n}\n\n/** Root of the per-deploy sandbox tree. The DeploySandbox resource GCs this. */\nconst SANDBOX_ROOT = \"/tmp/infracraft\";\n\ninterface SandboxScriptOptions {\n\t/** Whether to isolate into a /tmp copy (false → run in the live tree). */\n\tsandbox: boolean;\n\t/** Whether the sandbox `.git` is a metadata-free stub (true) or the real one. */\n\tgitGuard: boolean;\n\t/** Resource-derived name, used in the sandbox dir prefix. */\n\tappName: string;\n\t/** Stack/environment name, prefixed to the sandbox dir so leftovers and\n\t * concurrent deploys are identifiable (e.g. `staging-vercel-deploy-nexus.XXXX`). */\n\tenv?: string;\n\t/** Upload-scoping excludes; applied only in stub mode (see design spec). */\n\texcludePaths?: string[];\n\t/** Shell run in the working dir before `cli` (e.g. write railpack.json). */\n\tsetup?: string;\n\t/** Fully-formed platform deploy command (its exit code becomes the script's). */\n\tcli: string;\n}\n\n/**\n * Builds the shell for a deploy's `command.local.Command.create`. See\n * docs/superpowers/specs/2026-06-05-deploy-sandbox-design.md for the modes.\n */\nexport function buildSandboxScript(options: SandboxScriptOptions): string {\n\tconst { sandbox, gitGuard, appName, env, excludePaths, setup, cli } = options;\n\n\tconst head = `REPO=$(git rev-parse --show-toplevel)`;\n\tconst runSetupAndCli = [setup, cli].filter(Boolean).join(\"; \");\n\n\tif (!sandbox) {\n\t\treturn `${head}; cd \"$REPO\"; ${runSetupAndCli}`;\n\t}\n\n\t// Prefix the dir with the env so leftovers
|
|
1
|
+
{"version":3,"file":"sandbox.mjs","names":[],"sources":["../src/sandbox.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\n/** Escapes a path literal for embedding inside an awk ERE. */\nfunction escapeAwkRegex(path: string): string {\n\treturn path.replace(/[.[\\]{}()*+?^$|\\\\/]/g, \"\\\\$&\");\n}\n\n/**\n * Builds the shell filter applied to newline-delimited `git ls-files` output\n * before `rsync --files-from`. For `apps/<x>` it drops the app's files but keeps\n * `apps/<x>/package.json` (the monorepo workspace graph needs it during build);\n * any other entry drops the path and its subtree. Returns `cat` (passthrough)\n * when nothing is excluded. Uses awk so it is portable across macOS and Linux.\n */\nexport function buildSandboxFileFilter(excludePaths: string[] = []): string {\n\tif (excludePaths.length === 0) {\n\t\treturn \"cat\";\n\t}\n\n\tconst clauses = excludePaths.map((entry) => {\n\t\tconst escaped = escapeAwkRegex(entry);\n\n\t\tif (entry.startsWith(\"apps/\")) {\n\t\t\treturn `!(/^${escaped}\\\\// && !/^${escaped}\\\\/package\\\\.json$/)`;\n\t\t}\n\n\t\treturn `!/^${escaped}(\\\\/|$)/`;\n\t});\n\n\treturn `awk '${clauses.join(\" && \")}'`;\n}\n\n/** Root of the per-deploy sandbox tree. The DeploySandbox resource GCs this. */\nconst SANDBOX_ROOT = \"/tmp/infracraft\";\n\ninterface SandboxScriptOptions {\n\t/** Whether to isolate into a /tmp copy (false → run in the live tree). */\n\tsandbox: boolean;\n\t/** Whether the sandbox `.git` is a metadata-free stub (true) or the real one. */\n\tgitGuard: boolean;\n\t/** Resource-derived name, used in the sandbox dir prefix. */\n\tappName: string;\n\t/** Stack/environment name, prefixed to the sandbox dir so leftovers and\n\t * concurrent deploys are identifiable (e.g. `staging-vercel-deploy-nexus.XXXX`). */\n\tenv?: string;\n\t/** Upload-scoping excludes; applied only in stub mode (see design spec). */\n\texcludePaths?: string[];\n\t/** Shell run in the working dir before `cli` (e.g. write railpack.json). */\n\tsetup?: string;\n\t/** Fully-formed platform deploy command (its exit code becomes the script's). */\n\tcli: string;\n}\n\n/**\n * Builds the shell for a deploy's `command.local.Command.create`. See\n * docs/superpowers/specs/2026-06-05-deploy-sandbox-design.md for the modes.\n */\nexport function buildSandboxScript(options: SandboxScriptOptions): string {\n\tconst { sandbox, gitGuard, appName, env, excludePaths, setup, cli } = options;\n\n\tconst head = `REPO=$(git rev-parse --show-toplevel)`;\n\tconst runSetupAndCli = [setup, cli].filter(Boolean).join(\"; \");\n\n\tif (!sandbox) {\n\t\treturn `${head}; cd \"$REPO\"; ${runSetupAndCli}`;\n\t}\n\n\t// Prefix the dir with the project (repo folder name) and env so leftovers and\n\t// concurrent deploys — even across repos and stacks — are identifiable at a\n\t// glance (e.g. `mlm-nvr-production-railway-deploy-mesh.XXXX`).\n\tconst dirPrefix = env ? `${env}-${appName}` : appName;\n\n\tconst makeSandbox = [\n\t\t`PROJECT=$(basename \"$REPO\")`,\n\t\t`SANDBOX=$(mktemp -d \"${SANDBOX_ROOT}/$PROJECT-${dirPrefix}.XXXXXX\")`,\n\t\t`trap 'rm -rf \"$SANDBOX\"' EXIT`,\n\t].join(\"; \");\n\n\tif (gitGuard) {\n\t\tconst filter = buildSandboxFileFilter(excludePaths);\n\n\t\tconst copy =\n\t\t\t`git -C \"$REPO\" ls-files | ${filter} | ` +\n\t\t\t`rsync -a --files-from=- \"$REPO\"/ \"$SANDBOX\"/`;\n\n\t\tconst stubGit = `cd \"$SANDBOX\" && git init -q && git add -A`;\n\n\t\treturn `${head}; ${makeSandbox}; ${copy}; ${stubGit}; ${runSetupAndCli}`;\n\t}\n\n\t// Original `.git`, full clean tree (no filter, no reconciliation needed).\n\tconst copy = `git -C \"$REPO\" ls-files | rsync -a --files-from=- \"$REPO\"/ \"$SANDBOX\"/`;\n\n\tconst copyGit =\n\t\t`cp -c -R \"$REPO/.git\" \"$SANDBOX/.git\" 2>/dev/null || ` +\n\t\t`cp -R \"$REPO/.git\" \"$SANDBOX/.git\"`;\n\n\treturn `${head}; ${makeSandbox}; ${copy}; ${copyGit}; cd \"$SANDBOX\"; ${runSetupAndCli}`;\n}\n\n/** Cross-bundle brand: `instanceof` is unreliable when the seam and the resource\n * come from different built entries, so the seam detects sandboxes by this. */\nconst DEPLOY_SANDBOX_BRAND = Symbol.for(\"@infracraft/pulumi/DeploySandbox\");\n\n/** Sweep sandboxes orphaned by a hard-killed run (older than this). */\nconst STALE_SANDBOX_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Isolation marker + workspace lifecycle. Listing it in a deploy's `dependsOn`\n * makes that deploy run in an isolated `/tmp/infracraft` copy. Carries no config;\n * the repo root is derived at runtime by the deploy command.\n */\nexport class DeploySandbox extends pulumi.ComponentResource {\n\tconstructor(name: string, opts?: pulumi.ComponentResourceOptions) {\n\t\tsuper(\"infracraft:sandbox:DeploySandbox\", name, {}, opts);\n\n\t\t(this as Record<symbol, unknown>)[DEPLOY_SANDBOX_BRAND] = true;\n\n\t\tif (!pulumi.runtime.isDryRun()) {\n\t\t\tthis.prepareWorkspace();\n\t\t}\n\n\t\tthis.registerOutputs({});\n\t}\n\n\t/** mkdir the workspace root and GC stale sandboxes (best-effort). Sandboxes are\n\t * flat under the root (`/tmp/infracraft/<project>-<env>-<app>.XXXX`), so this\n\t * sweeps any entry older than the stale threshold. */\n\tprivate prepareWorkspace(): void {\n\t\tfs.mkdirSync(SANDBOX_ROOT, { recursive: true });\n\n\t\tlet entries: string[] = [];\n\n\t\ttry {\n\t\t\tentries = fs.readdirSync(SANDBOX_ROOT) as string[];\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\n\t\tconst now = Date.now();\n\n\t\tfor (const entry of entries) {\n\t\t\tconst full = path.join(SANDBOX_ROOT, entry);\n\n\t\t\ttry {\n\t\t\t\tif (now - fs.statSync(full).mtimeMs > STALE_SANDBOX_MS) {\n\t\t\t\t\tfs.rmSync(full, { recursive: true, force: true });\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Racing with an in-flight deploy's cleanup — ignore.\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Bundle-safe check for a `DeploySandbox` in a `dependsOn` array. */\nexport function isDeploySandbox(value: unknown): value is DeploySandbox {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t(value as Record<symbol, unknown>)[DEPLOY_SANDBOX_BRAND] === true\n\t);\n}\n"],"mappings":";;;;;;;AAKA,SAAS,eAAe,MAAsB;CAC7C,OAAO,KAAK,QAAQ,wBAAwB,MAAM;AACnD;;;;;;;;AASA,SAAgB,uBAAuB,eAAyB,CAAC,GAAW;CAC3E,IAAI,aAAa,WAAW,GAC3B,OAAO;CAaR,OAAO,QAVS,aAAa,KAAK,UAAU;EAC3C,MAAM,UAAU,eAAe,KAAK;EAEpC,IAAI,MAAM,WAAW,OAAO,GAC3B,OAAO,OAAO,QAAQ,aAAa,QAAQ;EAG5C,OAAO,MAAM,QAAQ;CACtB,CAEqB,EAAE,KAAK,MAAM,EAAE;AACrC;;AAGA,MAAM,eAAe;;;;;AAwBrB,SAAgB,mBAAmB,SAAuC;CACzE,MAAM,EAAE,SAAS,UAAU,SAAS,KAAK,cAAc,OAAO,QAAQ;CAEtE,MAAM,OAAO;CACb,MAAM,iBAAiB,CAAC,OAAO,GAAG,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;CAE7D,IAAI,CAAC,SACJ,OAAO,GAAG,KAAK,gBAAgB;CAQhC,MAAM,cAAc;EACnB;EACA,wBAAwB,aAAa,YAJpB,MAAM,GAAG,IAAI,GAAG,YAAY,QAIc;EAC3D;CACD,EAAE,KAAK,IAAI;CAEX,IAAI,UASH,OAAO,GAAG,KAAK,IAAI,YAAY,IAAI,6BARpB,uBAAuB,YAGH,EAAE,iDAKG,iDAAgB;CAUzD,OAAO,GAAG,KAAK,IAAI,YAAY,2LAAwC;AACxE;;;AAIA,MAAM,uBAAuB,OAAO,IAAI,kCAAkC;;AAG1E,MAAM,mBAAmB,OAAU,KAAK;;;;;;AAOxC,IAAa,gBAAb,cAAmC,OAAO,kBAAkB;CAC3D,YAAY,MAAc,MAAwC;EACjE,MAAM,oCAAoC,MAAM,CAAC,GAAG,IAAI;EAExD,AAAC,KAAiC,wBAAwB;EAE1D,IAAI,CAAC,OAAO,QAAQ,SAAS,GAC5B,KAAK,iBAAiB;EAGvB,KAAK,gBAAgB,CAAC,CAAC;CACxB;;;;CAKA,AAAQ,mBAAyB;EAChC,GAAG,UAAU,cAAc,EAAE,WAAW,KAAK,CAAC;EAE9C,IAAI,UAAoB,CAAC;EAEzB,IAAI;GACH,UAAU,GAAG,YAAY,YAAY;EACtC,QAAQ;GACP;EACD;EAEA,MAAM,MAAM,KAAK,IAAI;EAErB,KAAK,MAAM,SAAS,SAAS;GAC5B,MAAM,OAAO,KAAK,KAAK,cAAc,KAAK;GAE1C,IAAI;IACH,IAAI,MAAM,GAAG,SAAS,IAAI,EAAE,UAAU,kBACrC,GAAG,OAAO,MAAM;KAAE,WAAW;KAAM,OAAO;IAAK,CAAC;GAElD,QAAQ,CAER;EACD;CACD;AACD;;AAGA,SAAgB,gBAAgB,OAAwC;CACvE,OACC,OAAO,UAAU,YACjB,UAAU,QACT,MAAkC,0BAA0B;AAE/D"}
|