openship 0.6.6 → 0.6.8
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/bare-FA66N7GC.js +29 -0
- package/dist/{chunk-77QDF6UD.js → chunk-3MP6ACKM.js} +587 -156
- package/dist/{chunk-YIMOCZBZ.js → chunk-5DJOB7IX.js} +3 -3
- package/dist/{chunk-WVAML4H6.js → chunk-5I3COAFH.js} +274 -65
- package/dist/{chunk-5DMWSTOM.js → chunk-5UXHT6ED.js} +940 -224
- package/dist/{chunk-LUKYPVJ4.js → chunk-6GU6JSK6.js} +329 -466
- package/dist/{chunk-RSR7PFIQ.js → chunk-7PEXM2UI.js} +213 -45
- package/dist/{chunk-VTGPEC6B.js → chunk-7YBWXEM6.js} +6 -4
- package/dist/chunk-AHJYP7DG.js +938 -0
- package/dist/chunk-BO6CY4V2.js +265 -0
- package/dist/{chunk-FE7ZZQSE.js → chunk-D3OEMXYP.js} +3 -3
- package/dist/chunk-D6HU4VUZ.js +249 -0
- package/dist/{chunk-LFE5KQ7Y.js → chunk-ENKJ7XIF.js} +152 -57
- package/dist/{chunk-YMMQRE5J.js → chunk-GNOMEMNI.js} +1 -1
- package/dist/{chunk-QVW76WDL.js → chunk-IAMOUDHY.js} +190 -64
- package/dist/{chunk-OBPKYXQL.js → chunk-MUWVC5R4.js} +219 -58
- package/dist/chunk-N5YZMO33.js +111 -0
- package/dist/{chunk-A4GXYE4L.js → chunk-NIYD7MD2.js} +206 -52
- package/dist/{chunk-UVNMQM7W.js → chunk-NPSRGIHV.js} +8 -6
- package/dist/{chunk-2GWYYHOR.js → chunk-NRU2EBYZ.js} +2 -2
- package/dist/{chunk-CBLD2HS5.js → chunk-NUT6HKK6.js} +8 -5
- package/dist/{chunk-ZFJXPCQP.js → chunk-P7RCYELG.js} +4 -3
- package/dist/chunk-R4VDPFLS.js +106 -0
- package/dist/{chunk-HKRQXPTI.js → chunk-R5DC5PEF.js} +1 -1
- package/dist/{chunk-NK5N5LXN.js → chunk-RCMERVBL.js} +24 -9
- package/dist/{chunk-RXQBD4ZY.js → chunk-RHLNVZMS.js} +2 -2
- package/dist/chunk-SK4FFCFR.js +70 -0
- package/dist/{chunk-4I2BM2DT.js → chunk-T46BQOLY.js} +3 -3
- package/dist/{chunk-6FH4AXVY.js → chunk-T6DVUX4F.js} +1 -1
- package/dist/{chunk-PCPVTH6M.js → chunk-TAZZZKYO.js} +6 -4
- package/dist/{chunk-MLYSEZQB.js → chunk-UAWY6X7V.js} +43 -72
- package/dist/{chunk-Q7WGTQQ5.js → chunk-VAI5VOOI.js} +1 -1
- package/dist/{chunk-UJ6JZM65.js → chunk-WEZFTL4V.js} +1325 -513
- package/dist/{chunk-2YM3NI2E.js → chunk-X53JXRS4.js} +38 -44
- package/dist/{cloud-KRTZ2IL5.js → cloud-2CRDKOWQ.js} +1 -1
- package/dist/cloud-REV3DPJH.js +42 -0
- package/dist/{detect-OIE5V3VZ.js → detect-BBFT5GEU.js} +9 -6
- package/dist/{docker-VTR2GK5Y.js → docker-EG3QSE6I.js} +11 -8
- package/dist/{docker-edge-executor-22IBXNYJ.js → docker-edge-executor-E5TOTLMR.js} +8 -6
- package/dist/edge-import-YJVJUGYC.js +56 -0
- package/dist/{edge-preflight-GBNCPNVU.js → edge-preflight-QUW4BM7N.js} +15 -12
- package/dist/{ensure-container-edge-KPI2CVXJ.js → ensure-container-edge-FOEFQ3PI.js} +15 -10
- package/dist/{executor-7LXXZPBR.js → executor-NTWDJ3DB.js} +8 -6
- package/dist/host-channel-preflight-OMCZMMKR.js +54 -0
- package/dist/index.js +198 -76
- package/dist/{local-executor-WOFZD72X.js → local-executor-OQ2GRN7K.js} +3 -2
- package/dist/{nginx-KUFMLMAC.js → nginx-4U4C2XUS.js} +11 -8
- package/dist/{noop-NJ72F46T.js → noop-CLLJHCPL.js} +1 -1
- package/dist/{openresty-lua-ATTH35BE.js → openresty-lua-LLWT5XQW.js} +8 -5
- package/dist/{privilege-I4W3ZNZK.js → privilege-MNDVMDST.js} +6 -5
- package/dist/server/index.js +40049 -30378
- package/dist/server/migrations/0109_service_build_args.sql +9 -0
- package/dist/server/migrations/0110_service_deployment_host_ports.sql +18 -0
- package/dist/server/migrations/0111_host_port_claim.sql +381 -0
- package/dist/server/migrations/0112_backup_run_execution_lease.sql +12 -0
- package/dist/server/migrations/0113_docker_migration_execution_lease.sql +11 -0
- package/dist/server/migrations/0114_build_session_execution_lease_index.sql +6 -0
- package/dist/server/migrations/0115_orphan_target_key.sql +3 -0
- package/dist/server/migrations/0116_domain_ssl_challenge.sql +2 -0
- package/dist/server/migrations/meta/_journal.json +56 -0
- package/dist/setup-IFIMPQZJ.js +25 -0
- package/dist/{ssh-client-JBMA7SN4.js → ssh-client-2XWZCMGI.js} +3 -2
- package/dist/up-HQS7IR7H.js +61 -0
- package/package.json +1 -1
- package/dist/bare-OXJVW3AK.js +0 -25
- package/dist/chunk-LH2PLRQO.js +0 -434
- package/dist/chunk-SM26HDS2.js +0 -185
- package/dist/cloud-75VIT4KV.js +0 -37
- package/dist/edge-import-WJUB4M7F.js +0 -52
- package/dist/host-channel-preflight-KE6OONYK.js +0 -50
- package/dist/setup-XGLGUUFW.js +0 -22
- package/dist/up-K4BV7466.js +0 -57
|
@@ -5,38 +5,46 @@ const require = __ospCreateRequire(import.meta.url);
|
|
|
5
5
|
import {
|
|
6
6
|
checkGit,
|
|
7
7
|
installGit
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NIYD7MD2.js";
|
|
9
9
|
import {
|
|
10
10
|
createDockerBuildContext,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
resolveWithinDirectory
|
|
14
|
-
} from "./chunk-LUKYPVJ4.js";
|
|
11
|
+
resolveDockerBuildArgs
|
|
12
|
+
} from "./chunk-6GU6JSK6.js";
|
|
15
13
|
import {
|
|
16
14
|
prepareStackOutput,
|
|
17
15
|
resolveProjectDir,
|
|
18
16
|
resolveStaticOutputPath,
|
|
19
17
|
runLocalBuild
|
|
20
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-5DJOB7IX.js";
|
|
21
19
|
import {
|
|
22
20
|
BuildLogger,
|
|
21
|
+
dockerBuildContextDirectory,
|
|
22
|
+
droppedRuntimeEnvMessage,
|
|
23
23
|
injectGitToken,
|
|
24
|
+
normalizeDockerRelativePath,
|
|
25
|
+
resolveContextDockerfileCandidates,
|
|
26
|
+
resolveDockerfileCandidates,
|
|
27
|
+
resolveWithinDirectory,
|
|
24
28
|
runBuildPipeline,
|
|
29
|
+
splitRuntimeEnv,
|
|
25
30
|
sq,
|
|
26
31
|
transferLocalDirectory
|
|
27
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-AHJYP7DG.js";
|
|
33
|
+
import {
|
|
34
|
+
isRuntimeNotFoundError
|
|
35
|
+
} from "./chunk-N5YZMO33.js";
|
|
28
36
|
import {
|
|
29
37
|
PRICING,
|
|
30
38
|
STACKS,
|
|
31
39
|
SYSTEM,
|
|
32
40
|
TRANSFER_EXCLUDES,
|
|
33
41
|
buildOutputTransferExcludes,
|
|
34
|
-
isRuntimeNotFoundError,
|
|
35
42
|
missingOutputDirectoryMessage,
|
|
43
|
+
nodeBinPathExport,
|
|
36
44
|
packageManagerEnsureCommand,
|
|
37
45
|
safeErrorMessage,
|
|
38
46
|
shellSplitWords
|
|
39
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-WEZFTL4V.js";
|
|
40
48
|
|
|
41
49
|
// ../../packages/adapters/src/runtime/cloud.ts
|
|
42
50
|
import { readFile } from "fs/promises";
|
|
@@ -719,9 +727,11 @@ function restartPolicyForWorkload(policy) {
|
|
|
719
727
|
return "always";
|
|
720
728
|
}
|
|
721
729
|
function resolveCloudWorkloadCmd(opts) {
|
|
722
|
-
const { commandArgv, startCommand, workdir } = opts;
|
|
730
|
+
const { commandArgv, startCommand, workdir, binPathExport } = opts;
|
|
723
731
|
if (commandArgv != null) return commandArgv.length > 0 ? commandArgv : void 0;
|
|
724
|
-
|
|
732
|
+
if (!startCommand) return void 0;
|
|
733
|
+
const body = `cd ${sq(workdir)} && ${startCommand}`;
|
|
734
|
+
return ["sh", "-c", binPathExport ? `${binPathExport} && ${body}` : body];
|
|
725
735
|
}
|
|
726
736
|
function exposeTarget(port, serviceName, slug, domain = SYSTEM.DOMAINS.CLOUD_DOMAIN) {
|
|
727
737
|
const service = `service "${serviceName}" on port ${port}`;
|
|
@@ -1320,6 +1330,14 @@ function resolveCopyDestinationPath(copy, sourcePath, destination, destinationIs
|
|
|
1320
1330
|
const basename = posix.basename(sourcePath.replace(/\/+$/g, "")) || ".";
|
|
1321
1331
|
return posix.normalize(posix.join(destination, basename));
|
|
1322
1332
|
}
|
|
1333
|
+
function cloudBuildContextPath(config) {
|
|
1334
|
+
return dockerBuildContextDirectory(config) || normalizeDockerRelativePath(config.rootDirectory);
|
|
1335
|
+
}
|
|
1336
|
+
function compileCloudDockerfilePlan(source, config) {
|
|
1337
|
+
return compileDockerfileToWorkspacePlan(source, {
|
|
1338
|
+
buildArgs: resolveDockerBuildArgs(config)
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1323
1341
|
var PAGE_CONTAINER_PREFIX = "page:";
|
|
1324
1342
|
async function provisionCloudWorkspace(client, config, logger) {
|
|
1325
1343
|
logger?.log(`Creating workspace from image "${config.image}"...
|
|
@@ -1338,15 +1356,22 @@ async function provisionCloudWorkspace(client, config, logger) {
|
|
|
1338
1356
|
}
|
|
1339
1357
|
});
|
|
1340
1358
|
} catch (err) {
|
|
1341
|
-
logger?.log(
|
|
1342
|
-
|
|
1359
|
+
logger?.log(
|
|
1360
|
+
`Failed to create workspace from image "${config.image}": ${formatCloudError(err)}
|
|
1361
|
+
`,
|
|
1362
|
+
"error"
|
|
1363
|
+
);
|
|
1343
1364
|
throw err;
|
|
1344
1365
|
}
|
|
1345
1366
|
const ws = client.workspace(wsData.id);
|
|
1346
1367
|
try {
|
|
1347
1368
|
if (config.mode === "temporary" && config.ttl) {
|
|
1348
1369
|
try {
|
|
1349
|
-
await ws.lifecycle.makeTemporary({
|
|
1370
|
+
await ws.lifecycle.makeTemporary({
|
|
1371
|
+
ttl: config.ttl,
|
|
1372
|
+
ttl_action: "remove",
|
|
1373
|
+
remove_on_exit: true
|
|
1374
|
+
});
|
|
1350
1375
|
} catch {
|
|
1351
1376
|
}
|
|
1352
1377
|
}
|
|
@@ -1386,6 +1411,7 @@ var CloudRuntime = class {
|
|
|
1386
1411
|
name = "cloud";
|
|
1387
1412
|
capabilities = /* @__PURE__ */ new Set([
|
|
1388
1413
|
"build",
|
|
1414
|
+
"prebuiltImage",
|
|
1389
1415
|
"deploy",
|
|
1390
1416
|
"multiServiceDeploy",
|
|
1391
1417
|
"stop",
|
|
@@ -1537,6 +1563,94 @@ var CloudRuntime = class {
|
|
|
1537
1563
|
this.activeBuilds.get(sessionId)?.workspaceIds.delete(workspaceId);
|
|
1538
1564
|
}
|
|
1539
1565
|
// ── Build lifecycle ────────────────────────────────────────────────────
|
|
1566
|
+
/**
|
|
1567
|
+
* Materialize a prebuilt container image as the same opaque artifact Cloud's
|
|
1568
|
+
* deploy path already understands: a temporary workspace id.
|
|
1569
|
+
*
|
|
1570
|
+
* Unlike a source build, no command is executed in the workspace. Creating it
|
|
1571
|
+
* directly from the application image preserves the image's own ENTRYPOINT,
|
|
1572
|
+
* CMD and WORKDIR; deploy keeps those defaults when `prebuiltImage` is set.
|
|
1573
|
+
*/
|
|
1574
|
+
async prepareImage(config, logger) {
|
|
1575
|
+
const log = logger ?? new BuildLogger();
|
|
1576
|
+
const startedAt = Date.now();
|
|
1577
|
+
const activeBuild = this.createActiveBuild(config.sessionId);
|
|
1578
|
+
let workspaceId;
|
|
1579
|
+
const discardWorkspace = async () => {
|
|
1580
|
+
if (!workspaceId) return;
|
|
1581
|
+
await this.ws(workspaceId).delete().catch(() => {
|
|
1582
|
+
});
|
|
1583
|
+
this.builtArtifacts.delete(workspaceId);
|
|
1584
|
+
this.untrackActiveBuildWorkspace(config.sessionId, workspaceId);
|
|
1585
|
+
};
|
|
1586
|
+
const cancelled = async () => {
|
|
1587
|
+
await discardWorkspace();
|
|
1588
|
+
log.step("build", "failed", "Image preparation cancelled");
|
|
1589
|
+
return {
|
|
1590
|
+
sessionId: config.sessionId,
|
|
1591
|
+
status: "cancelled",
|
|
1592
|
+
// Keep the ref even after our best-effort delete: the pipeline can make
|
|
1593
|
+
// an idempotent cleanup retry if that delete failed mid-cancellation.
|
|
1594
|
+
imageRef: workspaceId,
|
|
1595
|
+
durationMs: Date.now() - startedAt,
|
|
1596
|
+
artifactOwned: true
|
|
1597
|
+
};
|
|
1598
|
+
};
|
|
1599
|
+
try {
|
|
1600
|
+
if (activeBuild.abort.signal.aborted) return await cancelled();
|
|
1601
|
+
const imageRef = config.imageRef.trim();
|
|
1602
|
+
if (!imageRef) throw new Error("Prebuilt image reference is required");
|
|
1603
|
+
log.step("build", "running", `Creating workspace from prebuilt image ${imageRef}`);
|
|
1604
|
+
const provisioned = await this.provisionWorkspace(
|
|
1605
|
+
{
|
|
1606
|
+
name: config.slug ?? `image-${config.projectId.slice(0, 20)}`,
|
|
1607
|
+
image: imageRef,
|
|
1608
|
+
mode: "temporary",
|
|
1609
|
+
resources: config.resources,
|
|
1610
|
+
env: config.envVars,
|
|
1611
|
+
ttl: "15m"
|
|
1612
|
+
},
|
|
1613
|
+
log
|
|
1614
|
+
);
|
|
1615
|
+
workspaceId = provisioned.workspaceId;
|
|
1616
|
+
this.trackActiveBuildWorkspace(config.sessionId, workspaceId);
|
|
1617
|
+
if (activeBuild.abort.signal.aborted) return await cancelled();
|
|
1618
|
+
this.builtArtifacts.set(workspaceId, {
|
|
1619
|
+
workspaceId,
|
|
1620
|
+
runtime: {
|
|
1621
|
+
workdir: "/",
|
|
1622
|
+
env: {},
|
|
1623
|
+
exposedPorts: [],
|
|
1624
|
+
baseImage: imageRef
|
|
1625
|
+
}
|
|
1626
|
+
});
|
|
1627
|
+
log.step("build", "completed", `Prebuilt image workspace ${workspaceId} is ready`);
|
|
1628
|
+
return {
|
|
1629
|
+
sessionId: config.sessionId,
|
|
1630
|
+
status: "deploying",
|
|
1631
|
+
imageRef: workspaceId,
|
|
1632
|
+
durationMs: Date.now() - startedAt,
|
|
1633
|
+
artifactOwned: true
|
|
1634
|
+
};
|
|
1635
|
+
} catch (err) {
|
|
1636
|
+
if (activeBuild.abort.signal.aborted) return await cancelled();
|
|
1637
|
+
await discardWorkspace();
|
|
1638
|
+
const message = formatCloudError(err);
|
|
1639
|
+
log.step("build", "failed", `Image preparation failed: ${message}`);
|
|
1640
|
+
return {
|
|
1641
|
+
sessionId: config.sessionId,
|
|
1642
|
+
status: "failed",
|
|
1643
|
+
// Same retry contract as cancellation. Undefined means provisioning
|
|
1644
|
+
// failed before Oblien handed us an owned workspace.
|
|
1645
|
+
imageRef: workspaceId,
|
|
1646
|
+
durationMs: Date.now() - startedAt,
|
|
1647
|
+
errorMessage: `Image preparation failed: ${message}`,
|
|
1648
|
+
artifactOwned: true
|
|
1649
|
+
};
|
|
1650
|
+
} finally {
|
|
1651
|
+
this.activeBuilds.delete(config.sessionId);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1540
1654
|
async build(config, logger) {
|
|
1541
1655
|
const log = logger ?? new BuildLogger();
|
|
1542
1656
|
const activeBuild = this.createActiveBuild(config.sessionId);
|
|
@@ -1595,7 +1709,9 @@ var CloudRuntime = class {
|
|
|
1595
1709
|
resolveProjectDir(buildDir, config.rootDirectory)
|
|
1596
1710
|
);
|
|
1597
1711
|
if (selfContained) {
|
|
1598
|
-
log.log(
|
|
1712
|
+
log.log(
|
|
1713
|
+
"Detected self-contained build output \u2014 uploading the bundle (no install on cloud).\n"
|
|
1714
|
+
);
|
|
1599
1715
|
await transferLocalDirectory(
|
|
1600
1716
|
selfContained.bundleDir,
|
|
1601
1717
|
{ kind: "cloud-runtime", runtime: rt, path: "/app" },
|
|
@@ -1633,7 +1749,11 @@ var CloudRuntime = class {
|
|
|
1633
1749
|
log.log("Installing production dependencies on cloud...\n");
|
|
1634
1750
|
const pmEnsure = packageManagerEnsureCommand(config.packageManager);
|
|
1635
1751
|
const fullInstall = pmEnsure ? `${pmEnsure} && ${installCmd}` : installCmd;
|
|
1636
|
-
await this.execAndStream(
|
|
1752
|
+
await this.execAndStream(
|
|
1753
|
+
rt,
|
|
1754
|
+
["sh", "-c", `cd /app && ${fullInstall}`],
|
|
1755
|
+
log.callback
|
|
1756
|
+
);
|
|
1637
1757
|
}
|
|
1638
1758
|
}
|
|
1639
1759
|
});
|
|
@@ -1718,7 +1838,7 @@ var CloudRuntime = class {
|
|
|
1718
1838
|
try {
|
|
1719
1839
|
log.log("Build strategy: Dockerfile plan (build in Oblien workspaces)\n");
|
|
1720
1840
|
source = await this.resolveDockerfileBuildSource(config, log);
|
|
1721
|
-
const plan =
|
|
1841
|
+
const plan = compileCloudDockerfilePlan(source.dockerfile, config);
|
|
1722
1842
|
const blocking = plan.diagnostics.filter(
|
|
1723
1843
|
(item) => item.severity === "error" || item.severity === "unsupported"
|
|
1724
1844
|
);
|
|
@@ -1792,7 +1912,7 @@ var CloudRuntime = class {
|
|
|
1792
1912
|
logger.log("Using Dockerfile content from source metadata.\n");
|
|
1793
1913
|
return {
|
|
1794
1914
|
kind: "remote",
|
|
1795
|
-
contextRelativePath:
|
|
1915
|
+
contextRelativePath: cloudBuildContextPath(config),
|
|
1796
1916
|
dockerfile: config.dockerfileContent,
|
|
1797
1917
|
cleanup: async () => {
|
|
1798
1918
|
}
|
|
@@ -1807,9 +1927,9 @@ var CloudRuntime = class {
|
|
|
1807
1927
|
requireRepositoryDockerfile: true,
|
|
1808
1928
|
onLog: logger.callback
|
|
1809
1929
|
});
|
|
1810
|
-
const dockerfilePath = resolveWithinDirectory(context.
|
|
1930
|
+
const dockerfilePath = resolveWithinDirectory(context.buildContextDir, context.dockerfileName);
|
|
1811
1931
|
const dockerfile = await readFile(dockerfilePath, "utf-8");
|
|
1812
|
-
const contextRoot = resolveWithinDirectory(context.contextDir, context.rootDirectory);
|
|
1932
|
+
const contextRoot = context.contextSubdir ? context.buildContextDir : resolveWithinDirectory(context.contextDir, context.rootDirectory);
|
|
1813
1933
|
return {
|
|
1814
1934
|
kind: "local",
|
|
1815
1935
|
contextRoot,
|
|
@@ -1818,7 +1938,10 @@ var CloudRuntime = class {
|
|
|
1818
1938
|
};
|
|
1819
1939
|
}
|
|
1820
1940
|
async resolveRemoteDockerfileBuildSource(config, logger) {
|
|
1821
|
-
const
|
|
1941
|
+
const contextSubdir = dockerBuildContextDirectory(config);
|
|
1942
|
+
const candidates = contextSubdir ? resolveContextDockerfileCandidates(contextSubdir, config.dockerfilePath).map(
|
|
1943
|
+
(candidate) => `${contextSubdir}/${candidate}`
|
|
1944
|
+
) : resolveDockerfileCandidates(config.rootDirectory, config.dockerfilePath);
|
|
1822
1945
|
const sourceLabel = config.commitSha ? `${config.branch}@${config.commitSha.slice(0, 7)}` : config.branch;
|
|
1823
1946
|
const checkoutRef = config.commitSha ?? "FETCH_HEAD";
|
|
1824
1947
|
const sourceDir = "/openship/dockerfile-source";
|
|
@@ -1867,11 +1990,9 @@ var CloudRuntime = class {
|
|
|
1867
1990
|
logger.log(`Checking Dockerfile candidate: ${candidate}
|
|
1868
1991
|
`);
|
|
1869
1992
|
const dockerfile = await executor.exec(
|
|
1870
|
-
[
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
`cat ${sq(candidate)}`
|
|
1874
|
-
].join(" && "),
|
|
1993
|
+
[`cd ${sq(sourceDir)}`, `test -f ${sq(candidate)}`, `cat ${sq(candidate)}`].join(
|
|
1994
|
+
" && "
|
|
1995
|
+
),
|
|
1875
1996
|
{ timeout: 3e4 }
|
|
1876
1997
|
).catch(() => null);
|
|
1877
1998
|
if (dockerfile !== null) {
|
|
@@ -1881,7 +2002,7 @@ var CloudRuntime = class {
|
|
|
1881
2002
|
sourceWorkspaceId = void 0;
|
|
1882
2003
|
return {
|
|
1883
2004
|
kind: "remote",
|
|
1884
|
-
contextRelativePath:
|
|
2005
|
+
contextRelativePath: cloudBuildContextPath(config),
|
|
1885
2006
|
dockerfile,
|
|
1886
2007
|
cleanup: async () => {
|
|
1887
2008
|
await this.ws(workspaceId).delete().catch(() => {
|
|
@@ -2063,6 +2184,25 @@ var CloudRuntime = class {
|
|
|
2063
2184
|
const contextSource = contextRelativePath ? joinWorkspacePath(repoRoot, contextRelativePath) : contextRoot;
|
|
2064
2185
|
const prepareContextCommands = contextRelativePath ? [
|
|
2065
2186
|
`echo "Copying Dockerfile context ${contextRelativePath}..."`,
|
|
2187
|
+
// `cp -a` FOLLOWS a symlinked source directory, so a repo that tracks
|
|
2188
|
+
// `svc -> /` and declares `build: ./svc` would copy the workspace's root
|
|
2189
|
+
// filesystem into its own build context. The lexical `..` refusal above
|
|
2190
|
+
// cannot see that; only a resolved path can. Same class as
|
|
2191
|
+
// GHSA-443m-7g52-94w8, reachable through the build context rather than
|
|
2192
|
+
// rootDirectory, and asserted here for the same reason the docker paths
|
|
2193
|
+
// assert it: at the sink.
|
|
2194
|
+
// `pwd -P` rather than `readlink -f`: it is a POSIX shell builtin, so it
|
|
2195
|
+
// exists in every workspace image, and it answers both questions at once —
|
|
2196
|
+
// the `cd` fails when the context is not a directory, and the resolved path
|
|
2197
|
+
// reveals a symlink that left the checkout.
|
|
2198
|
+
// BOTH sides resolved, and an unresolvable either side is fatal: an empty
|
|
2199
|
+
// `__root` would turn the prefix test into `"/"*`, which every absolute path
|
|
2200
|
+
// matches — a containment check that passes vacuously is worse than none.
|
|
2201
|
+
// `|| __x=""` keeps `set -e` from killing the script before the message.
|
|
2202
|
+
`__root="$(cd ${sq(repoRoot)} && pwd -P)" || __root=""`,
|
|
2203
|
+
`__ctx="$(cd ${sq(contextSource)} 2>/dev/null && pwd -P)" || __ctx=""`,
|
|
2204
|
+
`if [ -z "$__root" ] || [ -z "$__ctx" ]; then echo "Build context ${contextRelativePath} is not a directory in this repository." >&2; exit 1; fi`,
|
|
2205
|
+
`case "$__ctx/" in "$__root/"*) ;; *) echo "Build context ${contextRelativePath} resolves outside this repository." >&2; exit 1 ;; esac`,
|
|
2066
2206
|
`mkdir -p ${sq(contextRoot)}`,
|
|
2067
2207
|
`cp -a ${sq(`${contextSource}/.`)} ${sq(contextRoot)}/`
|
|
2068
2208
|
] : ['echo "Using repository root as Dockerfile context."'];
|
|
@@ -2082,8 +2222,10 @@ var CloudRuntime = class {
|
|
|
2082
2222
|
...prepareContextCommands,
|
|
2083
2223
|
'echo "Dockerfile context prepared."'
|
|
2084
2224
|
].join("\n");
|
|
2085
|
-
logger.log(
|
|
2086
|
-
`)
|
|
2225
|
+
logger.log(
|
|
2226
|
+
`Preparing Dockerfile build workspace for repository clone (branch: ${config.branch})...
|
|
2227
|
+
`
|
|
2228
|
+
);
|
|
2087
2229
|
await this.ensureWorkspaceGit(targetRuntime, logger, "Dockerfile build workspace");
|
|
2088
2230
|
logger.log(`Cloning Dockerfile context in build workspace (branch: ${config.branch})...
|
|
2089
2231
|
`);
|
|
@@ -2336,8 +2478,8 @@ var CloudRuntime = class {
|
|
|
2336
2478
|
}
|
|
2337
2479
|
}
|
|
2338
2480
|
const builtArtifact = this.builtArtifacts.get(workspaceId);
|
|
2339
|
-
const prodPaths = config.productionPaths;
|
|
2340
|
-
const workDir = builtArtifact?.runtime.workdir ?? (prodPaths?.length ? "/app/production" : "/app");
|
|
2481
|
+
const prodPaths = config.prebuiltImage ? void 0 : config.productionPaths;
|
|
2482
|
+
const workDir = config.prebuiltImage ? "/" : builtArtifact?.runtime.workdir ?? (prodPaths?.length ? "/app/production" : "/app");
|
|
2341
2483
|
if (prodPaths?.length) {
|
|
2342
2484
|
try {
|
|
2343
2485
|
const rt = await ws.runtime();
|
|
@@ -2384,23 +2526,48 @@ fi`;
|
|
|
2384
2526
|
);
|
|
2385
2527
|
}
|
|
2386
2528
|
}
|
|
2387
|
-
const startCommand = builtArtifact?.runtime.startCommand || config.startCommand || "npm start";
|
|
2529
|
+
const startCommand = builtArtifact?.runtime.startCommand || config.startCommand || (config.prebuiltImage ? void 0 : "npm start");
|
|
2530
|
+
const projectEnv = splitRuntimeEnv(config.envVars);
|
|
2531
|
+
if (projectEnv.dropped.length > 0) {
|
|
2532
|
+
onLog?.({
|
|
2533
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2534
|
+
level: "warn",
|
|
2535
|
+
message: droppedRuntimeEnvMessage(projectEnv.dropped)
|
|
2536
|
+
});
|
|
2537
|
+
}
|
|
2388
2538
|
const envArray = toEnvArray2({
|
|
2389
2539
|
...builtArtifact?.runtime.env ?? {},
|
|
2390
|
-
...
|
|
2540
|
+
...Object.fromEntries(projectEnv.entries)
|
|
2391
2541
|
});
|
|
2392
2542
|
const restartPolicy = config.restartPolicy === "no" ? "never" : config.restartPolicy ?? "always";
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2543
|
+
const workloadCmd = resolveCloudWorkloadCmd({
|
|
2544
|
+
startCommand,
|
|
2545
|
+
workdir: workDir,
|
|
2546
|
+
binPathExport: builtArtifact || config.prebuiltImage ? "" : nodeBinPathExport(config.packageManager, [workDir, "/app"])
|
|
2547
|
+
});
|
|
2548
|
+
if (workloadCmd) {
|
|
2549
|
+
try {
|
|
2550
|
+
if (config.prebuiltImage) {
|
|
2551
|
+
await ws.workloads.delete("app").catch(() => {
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
await ws.workloads.create({
|
|
2555
|
+
name: "app",
|
|
2556
|
+
cmd: workloadCmd,
|
|
2557
|
+
working_dir: workDir,
|
|
2558
|
+
env: [...envArray, `PORT=${config.port}`],
|
|
2559
|
+
restart_policy: restartPolicy,
|
|
2560
|
+
max_restarts: 10
|
|
2561
|
+
});
|
|
2562
|
+
} catch (err) {
|
|
2563
|
+
throw new Error(`Failed to create workload: ${err instanceof Error ? err.message : err}`);
|
|
2564
|
+
}
|
|
2565
|
+
} else {
|
|
2566
|
+
log({
|
|
2567
|
+
timestamp: now2(),
|
|
2568
|
+
level: "info",
|
|
2569
|
+
message: "Using the prebuilt image's default command and working directory.\n"
|
|
2401
2570
|
});
|
|
2402
|
-
} catch (err) {
|
|
2403
|
-
throw new Error(`Failed to create workload: ${err instanceof Error ? err.message : err}`);
|
|
2404
2571
|
}
|
|
2405
2572
|
let url;
|
|
2406
2573
|
const primaryEndpoint = primaryPublicEndpoint(config);
|
|
@@ -2538,7 +2705,9 @@ fi`;
|
|
|
2538
2705
|
pg = result.page;
|
|
2539
2706
|
} catch (err) {
|
|
2540
2707
|
if (isOutputPathError(err)) throw outputDirError();
|
|
2541
|
-
throw new Error(
|
|
2708
|
+
throw new Error(
|
|
2709
|
+
`Failed to create static page for slug "${pageSlug}": ${safeErrorMessage(err)}`
|
|
2710
|
+
);
|
|
2542
2711
|
}
|
|
2543
2712
|
return { ...pg, url: void 0 };
|
|
2544
2713
|
};
|
|
@@ -2553,7 +2722,9 @@ fi`;
|
|
|
2553
2722
|
});
|
|
2554
2723
|
page = result.page;
|
|
2555
2724
|
} catch (err) {
|
|
2556
|
-
throw new Error(
|
|
2725
|
+
throw new Error(
|
|
2726
|
+
`Failed to redeploy static page for slug "${pageSlug}": ${safeErrorMessage(err)}`
|
|
2727
|
+
);
|
|
2557
2728
|
}
|
|
2558
2729
|
} else {
|
|
2559
2730
|
if (existingPage) {
|
|
@@ -2695,15 +2866,7 @@ fi`;
|
|
|
2695
2866
|
}
|
|
2696
2867
|
async purge(deployment) {
|
|
2697
2868
|
if (deployment.containerId?.startsWith(PAGE_CONTAINER_PREFIX)) {
|
|
2698
|
-
|
|
2699
|
-
try {
|
|
2700
|
-
if (this.adminProxy?.deletePage) {
|
|
2701
|
-
await this.adminProxy.deletePage(slug);
|
|
2702
|
-
} else {
|
|
2703
|
-
await this.client.pages.delete(slug);
|
|
2704
|
-
}
|
|
2705
|
-
} catch {
|
|
2706
|
-
}
|
|
2869
|
+
await this.destroy(deployment.containerId);
|
|
2707
2870
|
return;
|
|
2708
2871
|
}
|
|
2709
2872
|
if (!deployment.containerId) return;
|
|
@@ -2717,10 +2880,7 @@ fi`;
|
|
|
2717
2880
|
err instanceof Error ? err.message : err
|
|
2718
2881
|
);
|
|
2719
2882
|
}
|
|
2720
|
-
|
|
2721
|
-
await this.destroy(deployment.containerId);
|
|
2722
|
-
} catch {
|
|
2723
|
-
}
|
|
2883
|
+
await this.destroy(deployment.containerId);
|
|
2724
2884
|
}
|
|
2725
2885
|
// ── Observability ──────────────────────────────────────────────────────
|
|
2726
2886
|
async getContainerInfo(containerId) {
|
|
@@ -3183,6 +3343,7 @@ function clampShellWindow(value, fallback, min, max) {
|
|
|
3183
3343
|
}
|
|
3184
3344
|
|
|
3185
3345
|
export {
|
|
3346
|
+
compileCloudDockerfilePlan,
|
|
3186
3347
|
PAGE_CONTAINER_PREFIX,
|
|
3187
3348
|
provisionCloudWorkspace,
|
|
3188
3349
|
HostBuildForbiddenError,
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
":" //# ; exec "$(command -v node || command -v bun)" "$0" "$@"
|
|
3
|
+
import { createRequire as __ospCreateRequire } from "node:module";
|
|
4
|
+
const require = __ospCreateRequire(import.meta.url);
|
|
5
|
+
|
|
6
|
+
// ../../packages/adapters/src/system/errors.ts
|
|
7
|
+
var SSH_AUTH_ERROR_PATTERNS = [
|
|
8
|
+
"All configured authentication methods failed"
|
|
9
|
+
];
|
|
10
|
+
var RETRYABLE_CONNECTION_ERROR_PATTERNS = [
|
|
11
|
+
"ECONNRESET",
|
|
12
|
+
"ECONNREFUSED",
|
|
13
|
+
"ETIMEDOUT",
|
|
14
|
+
"EHOSTUNREACH",
|
|
15
|
+
"ENETUNREACH",
|
|
16
|
+
"Timed out",
|
|
17
|
+
"Not connected",
|
|
18
|
+
"Connection lost",
|
|
19
|
+
"read ECONNRESET",
|
|
20
|
+
"Handshake failed",
|
|
21
|
+
"keepalive timeout",
|
|
22
|
+
"SSH connection closed before ready",
|
|
23
|
+
// ssh2 emits "Channel open failure: open failed" / "Channel open failure: …"
|
|
24
|
+
// when the SSH SERVER refuses a new channel - typically because the cached
|
|
25
|
+
// connection has gone half-dead (peer-side LOGOUT, network blip,
|
|
26
|
+
// remote sshd MaxSessions). The inner SshExecutor retries `exec`/
|
|
27
|
+
// `streamExec` on this, but `pipeLocal`, `transferIn`, and any other
|
|
28
|
+
// channel-bearing op bubble it up. Listing it here lets `withExecutor`
|
|
29
|
+
// drop the dead connection and retry transparently - fixes the
|
|
30
|
+
// "first redeploy fails, second click works" pattern.
|
|
31
|
+
"Channel open failure",
|
|
32
|
+
"open failed"
|
|
33
|
+
// NOT "socket hang up": it is what an HTTP client reports for ANY far end that vanished
|
|
34
|
+
// mid-request, including a permission-denied Docker socket and a wedged channel, neither
|
|
35
|
+
// of which a fresh connection fixes. A match here makes `withExecutor` replay its whole
|
|
36
|
+
// callback — and those callbacks install mail, create mailboxes and ensure the edge, so a
|
|
37
|
+
// non-retryable cause becomes a repeated mutation. `core/connectivity.ts` labels it
|
|
38
|
+
// without retrying, `api/lib/remote-state.ts` still maps it to a 503, and the bridge's
|
|
39
|
+
// 502 carries the real reason — so leaving it out here costs no diagnosis.
|
|
40
|
+
];
|
|
41
|
+
var SshDisconnectedError = class extends Error {
|
|
42
|
+
constructor(message = "SSH connection lost") {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = "SshDisconnectedError";
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
function isSshDisconnectedError(err) {
|
|
48
|
+
return err instanceof SshDisconnectedError || err instanceof Error && err.name === "SshDisconnectedError";
|
|
49
|
+
}
|
|
50
|
+
function isSshAuthError(err) {
|
|
51
|
+
if (!(err instanceof Error)) return false;
|
|
52
|
+
return SSH_AUTH_ERROR_PATTERNS.some((pattern) => err.message.includes(pattern));
|
|
53
|
+
}
|
|
54
|
+
var HostChannelUnavailableError = class extends Error {
|
|
55
|
+
code;
|
|
56
|
+
constructor(code, message) {
|
|
57
|
+
super(message);
|
|
58
|
+
this.name = "HostChannelUnavailableError";
|
|
59
|
+
this.code = code;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
function isRetryableRemoteConnectionError(err) {
|
|
63
|
+
if (isSshDisconnectedError(err)) return true;
|
|
64
|
+
if (!(err instanceof Error)) return false;
|
|
65
|
+
return RETRYABLE_CONNECTION_ERROR_PATTERNS.some(
|
|
66
|
+
(pattern) => err.message.includes(pattern)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function isRemoteConnectionError(err) {
|
|
70
|
+
return isSshAuthError(err) || isRetryableRemoteConnectionError(err);
|
|
71
|
+
}
|
|
72
|
+
var MAX_COMMAND_CHARS = 120;
|
|
73
|
+
function commandForError(command) {
|
|
74
|
+
const oneLine = command.trim().replace(/\s+/g, " ");
|
|
75
|
+
return oneLine.length > MAX_COMMAND_CHARS ? `${oneLine.slice(0, MAX_COMMAND_CHARS)}\u2026 (${command.length} chars)` : oneLine;
|
|
76
|
+
}
|
|
77
|
+
function isRuntimeNotFoundError(err) {
|
|
78
|
+
if (!err || typeof err !== "object") return false;
|
|
79
|
+
const e = err;
|
|
80
|
+
if (e.statusCode === 404) return true;
|
|
81
|
+
return typeof e.message === "string" && /no such (container|image|volume|network)/i.test(e.message);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ../../packages/adapters/src/system/debug.ts
|
|
85
|
+
function isTruthy(value) {
|
|
86
|
+
if (!value) return false;
|
|
87
|
+
return ["1", "true", "yes", "on", "debug"].includes(value.toLowerCase());
|
|
88
|
+
}
|
|
89
|
+
function isSystemDebugEnabled() {
|
|
90
|
+
return isTruthy(process.env.SYSTEM_DEBUG_LOGS);
|
|
91
|
+
}
|
|
92
|
+
function systemDebug(scope, message) {
|
|
93
|
+
if (!isSystemDebugEnabled()) return;
|
|
94
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
95
|
+
console.log(`[system-debug] ${timestamp} [${scope}] ${message}`);
|
|
96
|
+
}
|
|
97
|
+
function formatDuration(startMs) {
|
|
98
|
+
return `${Date.now() - startMs}ms`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
SshDisconnectedError,
|
|
103
|
+
isSshAuthError,
|
|
104
|
+
HostChannelUnavailableError,
|
|
105
|
+
isRetryableRemoteConnectionError,
|
|
106
|
+
isRemoteConnectionError,
|
|
107
|
+
commandForError,
|
|
108
|
+
isRuntimeNotFoundError,
|
|
109
|
+
systemDebug,
|
|
110
|
+
formatDuration
|
|
111
|
+
};
|