openship 0.6.9 → 0.7.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/bare-AB4SMTNJ.js +29 -0
- package/dist/{chunk-P7RCYELG.js → chunk-2BAKEGGF.js} +1 -1
- package/dist/{chunk-VAI5VOOI.js → chunk-2YAY5XHK.js} +1 -1
- package/dist/{chunk-X53JXRS4.js → chunk-37XZTLBJ.js} +3 -3
- package/dist/{chunk-R5DC5PEF.js → chunk-3K65MMYV.js} +39 -10
- package/dist/{chunk-D3OEMXYP.js → chunk-3WBKLDGC.js} +12 -7
- package/dist/{chunk-ENKJ7XIF.js → chunk-3YSSXSC2.js} +12 -12
- package/dist/{chunk-3MP6ACKM.js → chunk-7MCPDYKP.js} +799 -165
- package/dist/{chunk-D6HU4VUZ.js → chunk-7TQP6723.js} +2 -2
- package/dist/{chunk-SK4FFCFR.js → chunk-AMADTKC6.js} +1 -1
- package/dist/{chunk-NPSRGIHV.js → chunk-B2BQX6JX.js} +6 -5
- package/dist/{chunk-MUWVC5R4.js → chunk-BBPKAQ54.js} +12 -9
- package/dist/{chunk-WEZFTL4V.js → chunk-COI4VFRW.js} +217 -2
- package/dist/{chunk-5DJOB7IX.js → chunk-CQ2JOXEQ.js} +3 -3
- package/dist/{chunk-7PEXM2UI.js → chunk-DQR5TPFB.js} +6 -6
- package/dist/{chunk-IAMOUDHY.js → chunk-ETK6IBCH.js} +24 -15
- package/dist/{chunk-T46BQOLY.js → chunk-FNWNUYS3.js} +3 -3
- package/dist/{chunk-UAWY6X7V.js → chunk-GPTBEPUE.js} +485 -67
- package/dist/{chunk-634DDFT5.js → chunk-H36ASAYI.js} +8 -8
- package/dist/{chunk-AHJYP7DG.js → chunk-IVMPA6RC.js} +74 -17
- package/dist/{chunk-5UXHT6ED.js → chunk-IXBOV7BE.js} +113 -20
- package/dist/{chunk-GNOMEMNI.js → chunk-KATXZ5AB.js} +1 -1
- package/dist/{chunk-T6DVUX4F.js → chunk-KCMEWNP7.js} +14 -3
- package/dist/{chunk-N5YZMO33.js → chunk-L4DRH6ZA.js} +12 -1
- package/dist/{chunk-BO6CY4V2.js → chunk-NF7NQOAP.js} +47 -4
- package/dist/{chunk-FCGJN5Q5.js → chunk-NIOLZENU.js} +9 -9
- package/dist/{chunk-7YBWXEM6.js → chunk-OEYXXSAM.js} +4 -4
- package/dist/{chunk-6GU6JSK6.js → chunk-S5GU345P.js} +3 -3
- package/dist/{chunk-TAZZZKYO.js → chunk-TKK5NPDQ.js} +2 -2
- package/dist/{chunk-NUT6HKK6.js → chunk-WG5PDWZ3.js} +5 -5
- package/dist/{chunk-NIYD7MD2.js → chunk-XJSLJLNY.js} +90 -46
- package/dist/{cloud-REV3DPJH.js → cloud-T2JOFEQL.js} +20 -20
- package/dist/{detect-BBFT5GEU.js → detect-SWLZURYX.js} +5 -5
- package/dist/{docker-EG3QSE6I.js → docker-SQNZRZXN.js} +11 -11
- package/dist/{docker-edge-executor-E5TOTLMR.js → docker-edge-executor-BSSQQS76.js} +8 -8
- package/dist/{edge-import-YJVJUGYC.js → edge-import-LUDX77C2.js} +27 -27
- package/dist/{edge-preflight-QUW4BM7N.js → edge-preflight-JAEFWBRI.js} +14 -14
- package/dist/{ensure-container-edge-FOEFQ3PI.js → ensure-container-edge-U7FTLFVJ.js} +12 -12
- package/dist/{executor-NTWDJ3DB.js → executor-GYRZ7RIU.js} +7 -7
- package/dist/host-channel-preflight-U4IZCRXR.js +54 -0
- package/dist/index.js +72 -50
- package/dist/{local-executor-OQ2GRN7K.js → local-executor-JCR46VAP.js} +3 -3
- package/dist/{nginx-4U4C2XUS.js → nginx-YA6IRPJT.js} +10 -10
- package/dist/{openresty-lua-LLWT5XQW.js → openresty-lua-DUOB64TP.js} +8 -8
- package/dist/{privilege-MNDVMDST.js → privilege-4B2TMVW2.js} +6 -6
- package/dist/server/index.js +13286 -5381
- package/dist/server/lua/site_logger.lua +18 -1
- package/dist/server/migrations/0117_github_installation_workspace_scope.sql +24 -0
- package/dist/server/migrations/0118_git_sources.sql +66 -0
- package/dist/server/migrations/0119_backup_run_batch.sql +8 -0
- package/dist/server/migrations/0120_domain_primary_unique.sql +24 -0
- package/dist/server/migrations/0121_host_port_claim_overlap.sql +14 -0
- package/dist/server/migrations/0122_data_transfer_staging.sql +45 -0
- package/dist/server/migrations/meta/_journal.json +42 -0
- package/dist/setup-T3WN3HRS.js +25 -0
- package/dist/{ssh-client-2XWZCMGI.js → ssh-client-FZDWP5PQ.js} +3 -3
- package/dist/up-U73RUN5R.js +61 -0
- package/package.json +1 -1
- package/dist/bare-FA66N7GC.js +0 -29
- package/dist/host-channel-preflight-OI2TRHSG.js +0 -54
- package/dist/setup-IFIMPQZJ.js +0 -25
- package/dist/up-RC5ZQIAD.js +0 -61
|
@@ -10,28 +10,28 @@ import {
|
|
|
10
10
|
readComposeEnvFile,
|
|
11
11
|
resolveLocalEnvironmentSync,
|
|
12
12
|
thisHost
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-IXBOV7BE.js";
|
|
14
14
|
import {
|
|
15
15
|
OS_DIR
|
|
16
16
|
} from "./chunk-ELGRMZOW.js";
|
|
17
17
|
import {
|
|
18
18
|
systemCatalog
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-XJSLJLNY.js";
|
|
20
20
|
import {
|
|
21
21
|
EDGE_CONTAINER_MOUNTS,
|
|
22
22
|
EDGE_HOST_STATE_DIR
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-DQR5TPFB.js";
|
|
24
24
|
import {
|
|
25
25
|
DEFAULT_DOCKER_SOCKET_PATH,
|
|
26
26
|
resolveLocalDockerSocketPath
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-7MCPDYKP.js";
|
|
28
28
|
import {
|
|
29
29
|
LocalExecutor
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-KCMEWNP7.js";
|
|
31
31
|
import {
|
|
32
32
|
invalidateEdgeContainer,
|
|
33
33
|
sanitizeEdgeVhosts
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-37XZTLBJ.js";
|
|
35
35
|
import {
|
|
36
36
|
DEFAULT_IMAGE_REGISTRY,
|
|
37
37
|
HOST_CHANNEL_BLOCKED,
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
hostFirewallRule,
|
|
52
52
|
hostFirewallRuleRemoval,
|
|
53
53
|
wrapText
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-COI4VFRW.js";
|
|
55
55
|
|
|
56
56
|
// src/lib/host-channel-preflight.ts
|
|
57
57
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
@@ -1627,7 +1627,7 @@ function operatorEnvPassthrough(managed, prev) {
|
|
|
1627
1627
|
}
|
|
1628
1628
|
var PRESERVED_ENV_HEADER = "# Preserved from your existing .env \u2014 set by you, not by `openship up` (#485).";
|
|
1629
1629
|
function resolveImageVersion(opts) {
|
|
1630
|
-
return opts.version?.trim() || process.env.OPENSHIP_VERSION?.trim() || (true ? "0.
|
|
1630
|
+
return opts.version?.trim() || process.env.OPENSHIP_VERSION?.trim() || (true ? "0.7.1" : "latest");
|
|
1631
1631
|
}
|
|
1632
1632
|
function managedEnvLines(opts, host, cfg, prev) {
|
|
1633
1633
|
const lines = [
|
|
@@ -5,7 +5,7 @@ const require = __ospCreateRequire(import.meta.url);
|
|
|
5
5
|
import {
|
|
6
6
|
getTarCreateEnv,
|
|
7
7
|
prepareSourceTarArgs
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-2YAY5XHK.js";
|
|
9
9
|
import {
|
|
10
10
|
TRANSFER_EXCLUDES,
|
|
11
11
|
nodeBinDirs,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
packageManagerEnsureCommand,
|
|
14
14
|
safeErrorMessage,
|
|
15
15
|
shellQuote
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-COI4VFRW.js";
|
|
17
17
|
|
|
18
18
|
// ../../packages/adapters/src/runtime/git-clone.ts
|
|
19
19
|
var sq = shellQuote;
|
|
@@ -75,18 +75,48 @@ function assembleGitClone(auth) {
|
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
77
|
if (auth.gitCredentialHelperPath) {
|
|
78
|
-
|
|
78
|
+
const base = invocation(
|
|
79
79
|
auth.repoUrl,
|
|
80
80
|
[
|
|
81
81
|
TERMINAL_PROMPT,
|
|
82
|
-
|
|
82
|
+
// An empty helper value resets helpers inherited from the host before
|
|
83
|
+
// adding the deployment-scoped relay. Without the reset, a server's
|
|
84
|
+
// stale/global helper can answer first and silently override the
|
|
85
|
+
// forwarded identity named in the deployment log.
|
|
86
|
+
["GIT_CONFIG_COUNT", "3", false],
|
|
83
87
|
["GIT_CONFIG_KEY_0", "credential.helper", false],
|
|
84
|
-
["GIT_CONFIG_VALUE_0",
|
|
85
|
-
["GIT_CONFIG_KEY_1", "credential.
|
|
86
|
-
["GIT_CONFIG_VALUE_1",
|
|
88
|
+
["GIT_CONFIG_VALUE_0", "", true],
|
|
89
|
+
["GIT_CONFIG_KEY_1", "credential.helper", false],
|
|
90
|
+
["GIT_CONFIG_VALUE_1", auth.gitCredentialHelperPath, true],
|
|
91
|
+
["GIT_CONFIG_KEY_2", "credential.useHttpPath", false],
|
|
92
|
+
["GIT_CONFIG_VALUE_2", "true", false]
|
|
87
93
|
],
|
|
88
94
|
false
|
|
89
95
|
);
|
|
96
|
+
let url;
|
|
97
|
+
try {
|
|
98
|
+
url = new URL(auth.repoUrl);
|
|
99
|
+
} catch {
|
|
100
|
+
throw new Error("Git credential forwarding requires an absolute HTTPS repository URL");
|
|
101
|
+
}
|
|
102
|
+
if (url.protocol !== "https:") {
|
|
103
|
+
throw new Error("Git credential forwarding requires an HTTPS repository URL");
|
|
104
|
+
}
|
|
105
|
+
const protocol = url.protocol.slice(0, -1);
|
|
106
|
+
const path = url.pathname.replace(/^\/+/, "");
|
|
107
|
+
const headerVar = "OPENSHIP_GIT_AUTH_HEADER";
|
|
108
|
+
const headerCommand = `${sq(auth.gitCredentialHelperPath)} auth-header ${sq(protocol)} ${sq(url.hostname)} ${sq(path)}`;
|
|
109
|
+
return {
|
|
110
|
+
...base,
|
|
111
|
+
// `auth-header` returns only the Basic header, never prints it to the
|
|
112
|
+
// build log, and keeps the token out of the command text and filesystem.
|
|
113
|
+
// The value exists only in the remote shell/git process for this command.
|
|
114
|
+
shellPrelude: `${headerVar}="$(${headerCommand})" && test -n "$${headerVar}"`,
|
|
115
|
+
// The argv representation above remains a valid challenge-based helper
|
|
116
|
+
// invocation for local spawn users. Remote shell users call
|
|
117
|
+
// gitShellCommand(), which adds the preemptive header as config slot 3.
|
|
118
|
+
gitEnv: `${base.gitEnv.replace("GIT_CONFIG_COUNT=3", "GIT_CONFIG_COUNT=4")} GIT_CONFIG_KEY_3=http.extraHeader GIT_CONFIG_VALUE_3="$${headerVar}"`
|
|
119
|
+
};
|
|
90
120
|
}
|
|
91
121
|
if (auth.ambient) {
|
|
92
122
|
if (auth.ambient.via === "ssh") {
|
|
@@ -112,6 +142,12 @@ function assembleGitClone(auth) {
|
|
|
112
142
|
true
|
|
113
143
|
);
|
|
114
144
|
}
|
|
145
|
+
function gitShellCommand(invocation2, args) {
|
|
146
|
+
const env = invocation2.gitEnv ? `${invocation2.gitEnv} ` : "";
|
|
147
|
+
const flags = invocation2.credFlag ? `${invocation2.credFlag} ` : "";
|
|
148
|
+
const command = `${env}git ${flags}${args}`;
|
|
149
|
+
return invocation2.shellPrelude ? `${invocation2.shellPrelude} && ${command}` : command;
|
|
150
|
+
}
|
|
115
151
|
|
|
116
152
|
// ../../packages/adapters/src/runtime/git-ssh-material.ts
|
|
117
153
|
import { mkdir, rm, writeFile } from "fs/promises";
|
|
@@ -253,31 +289,49 @@ async function runBuildPipeline(env, config, logger) {
|
|
|
253
289
|
config.gitSsh
|
|
254
290
|
);
|
|
255
291
|
}
|
|
256
|
-
const
|
|
292
|
+
const gitInvocation = assembleGitClone({
|
|
257
293
|
repoUrl: config.repoUrl,
|
|
258
294
|
gitToken: config.gitToken,
|
|
259
295
|
gitCredentialHelperPath: config.gitCredentialHelperPath,
|
|
260
296
|
ssh: sshMaterial,
|
|
261
297
|
ambient: config.gitAmbient
|
|
262
298
|
});
|
|
299
|
+
const { cloneUrl, credFlag: CRED } = gitInvocation;
|
|
263
300
|
try {
|
|
264
301
|
if (config.commitSha) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
302
|
+
await exec(
|
|
303
|
+
gitShellCommand(
|
|
304
|
+
gitInvocation,
|
|
305
|
+
`clone --progress --depth 50 --branch ${sq(config.branch)} ${sq(cloneUrl)} ${sq(env.projectDir)}`
|
|
306
|
+
)
|
|
307
|
+
);
|
|
308
|
+
const commitPresent = await exec(
|
|
309
|
+
`cd ${sq(env.projectDir)} && git ${CRED} cat-file -e ${sq(`${config.commitSha}^{commit}`)}`
|
|
310
|
+
).then(
|
|
311
|
+
() => true,
|
|
312
|
+
() => false
|
|
313
|
+
);
|
|
314
|
+
if (!commitPresent) {
|
|
270
315
|
logger.log(
|
|
271
|
-
`
|
|
316
|
+
`Commit ${config.commitSha} is not in the depth-50 clone; fetching full history before checkout.`,
|
|
272
317
|
"warn"
|
|
273
318
|
);
|
|
274
319
|
await exec(
|
|
275
|
-
`cd ${sq(env.projectDir)} && ${
|
|
320
|
+
`cd ${sq(env.projectDir)} && ${gitShellCommand(gitInvocation, "fetch --progress --unshallow")}`
|
|
276
321
|
);
|
|
277
322
|
}
|
|
323
|
+
await exec(
|
|
324
|
+
`cd ${sq(env.projectDir)} && ${gitShellCommand(
|
|
325
|
+
gitInvocation,
|
|
326
|
+
`-c advice.detachedHead=false checkout ${sq(config.commitSha)}`
|
|
327
|
+
)}`
|
|
328
|
+
);
|
|
278
329
|
} else {
|
|
279
330
|
await exec(
|
|
280
|
-
|
|
331
|
+
gitShellCommand(
|
|
332
|
+
gitInvocation,
|
|
333
|
+
`clone --progress --depth 1 --branch ${sq(config.branch)} ${sq(cloneUrl)} ${sq(env.projectDir)}`
|
|
334
|
+
)
|
|
281
335
|
);
|
|
282
336
|
}
|
|
283
337
|
} finally {
|
|
@@ -342,7 +396,9 @@ function parseLogLevel(message) {
|
|
|
342
396
|
function detectBuildKillHint(output) {
|
|
343
397
|
if (!output) return null;
|
|
344
398
|
const tail = output.slice(-4096);
|
|
345
|
-
if (/\bsigkill\b|\bKilled\b|out of memory|JavaScript heap out of memory|Allocation failed/i.test(
|
|
399
|
+
if (/\bsigkill\b|\bKilled\b|out of memory|JavaScript heap out of memory|Allocation failed/i.test(
|
|
400
|
+
tail
|
|
401
|
+
)) {
|
|
346
402
|
return "Build process was killed - typically because the target ran out of memory during the build. Increase RAM on the target, add swap, or build locally and ship the dist.";
|
|
347
403
|
}
|
|
348
404
|
return null;
|
|
@@ -913,6 +969,7 @@ export {
|
|
|
913
969
|
sq,
|
|
914
970
|
injectGitToken,
|
|
915
971
|
assembleGitClone,
|
|
972
|
+
gitShellCommand,
|
|
916
973
|
materializeGitSsh,
|
|
917
974
|
shellGitSshWriter,
|
|
918
975
|
localGitSshWriter,
|
|
@@ -134,10 +134,10 @@ import {
|
|
|
134
134
|
} from "./chunk-ZWLDLTJP.js";
|
|
135
135
|
import {
|
|
136
136
|
CloudRuntime
|
|
137
|
-
} from "./chunk-
|
|
137
|
+
} from "./chunk-BBPKAQ54.js";
|
|
138
138
|
import {
|
|
139
139
|
OPENRESTY_DEFAULT_PATHS
|
|
140
|
-
} from "./chunk-
|
|
140
|
+
} from "./chunk-DQR5TPFB.js";
|
|
141
141
|
import {
|
|
142
142
|
DockerRuntime,
|
|
143
143
|
daemonConnectionFrom,
|
|
@@ -146,26 +146,26 @@ import {
|
|
|
146
146
|
resolveExecExitCode,
|
|
147
147
|
startAttachStream,
|
|
148
148
|
startExecStream
|
|
149
|
-
} from "./chunk-
|
|
149
|
+
} from "./chunk-7MCPDYKP.js";
|
|
150
150
|
import {
|
|
151
151
|
BareRuntime
|
|
152
|
-
} from "./chunk-
|
|
152
|
+
} from "./chunk-3YSSXSC2.js";
|
|
153
153
|
import {
|
|
154
154
|
createExecutor
|
|
155
|
-
} from "./chunk-
|
|
155
|
+
} from "./chunk-GPTBEPUE.js";
|
|
156
156
|
import {
|
|
157
157
|
envOps
|
|
158
|
-
} from "./chunk-
|
|
158
|
+
} from "./chunk-KATXZ5AB.js";
|
|
159
159
|
import {
|
|
160
160
|
ENVIRONMENT_PROBE_SCRIPT,
|
|
161
161
|
ENVIRONMENT_PROBE_TIMEOUT_MS,
|
|
162
162
|
cacheEnvironment,
|
|
163
163
|
parseEnvironmentProbe,
|
|
164
164
|
profileIsFresh
|
|
165
|
-
} from "./chunk-
|
|
165
|
+
} from "./chunk-B2BQX6JX.js";
|
|
166
166
|
import {
|
|
167
167
|
MAIL_HOST_STATE_DIR
|
|
168
|
-
} from "./chunk-
|
|
168
|
+
} from "./chunk-7TQP6723.js";
|
|
169
169
|
import {
|
|
170
170
|
FAMILY_BY_DISTRO,
|
|
171
171
|
isAwsS3Endpoint,
|
|
@@ -177,7 +177,7 @@ import {
|
|
|
177
177
|
validateBackupPath,
|
|
178
178
|
validateClearPath,
|
|
179
179
|
withTimeout
|
|
180
|
-
} from "./chunk-
|
|
180
|
+
} from "./chunk-COI4VFRW.js";
|
|
181
181
|
|
|
182
182
|
// ../../packages/adapters/src/runtime/image-transfer.ts
|
|
183
183
|
import { Transform } from "stream";
|
|
@@ -1141,6 +1141,8 @@ function demuxDockerStream(source, stdout, stderr, onError) {
|
|
|
1141
1141
|
|
|
1142
1142
|
// ../../packages/adapters/src/backup/executors/docker.ts
|
|
1143
1143
|
var HELPER_IMAGE = "alpine:3";
|
|
1144
|
+
var BIND_PROBE_TARGET = "/__openship_bind_source";
|
|
1145
|
+
var DOCKER_MODE_DIRECTORY = 2 ** 31;
|
|
1144
1146
|
var ARTIFACT_BUFFER_BYTES = 4 * 1024 * 1024;
|
|
1145
1147
|
var DEFAULT_HELPER_IDLE_MS = 10 * 60 * 1e3;
|
|
1146
1148
|
var DEFAULT_HELPER_TIMEOUT_MS = 6 * 60 * 60 * 1e3;
|
|
@@ -1216,6 +1218,29 @@ function parseVolumeSpec(spec) {
|
|
|
1216
1218
|
const type = isHostPathSource(source) ? "bind" : "volume";
|
|
1217
1219
|
return { source, target, type };
|
|
1218
1220
|
}
|
|
1221
|
+
function archivePathIsDirectory(response, path) {
|
|
1222
|
+
const rawHeader = response.headers?.["x-docker-container-path-stat"];
|
|
1223
|
+
const encoded = Array.isArray(rawHeader) ? rawHeader[0] : rawHeader;
|
|
1224
|
+
if (!encoded) {
|
|
1225
|
+
throw new Error(`Docker returned no path-stat header while inspecting ${path}`);
|
|
1226
|
+
}
|
|
1227
|
+
let stat;
|
|
1228
|
+
try {
|
|
1229
|
+
stat = JSON.parse(Buffer.from(encoded, "base64").toString("utf8"));
|
|
1230
|
+
} catch (err) {
|
|
1231
|
+
throw new Error(`Docker returned an invalid path-stat header while inspecting ${path}`, {
|
|
1232
|
+
cause: err
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
const mode = stat?.mode;
|
|
1236
|
+
if (typeof mode !== "number" || !Number.isSafeInteger(mode)) {
|
|
1237
|
+
throw new Error(`Docker returned no valid path mode while inspecting ${path}`);
|
|
1238
|
+
}
|
|
1239
|
+
return mode >= DOCKER_MODE_DIRECTORY;
|
|
1240
|
+
}
|
|
1241
|
+
function isMissingBindSourceError(err) {
|
|
1242
|
+
return err?.statusCode === 400 && /bind source path does not exist/i.test(safeErrorMessage(err));
|
|
1243
|
+
}
|
|
1219
1244
|
var DockerBackupExecutor = class {
|
|
1220
1245
|
constructor(runtime) {
|
|
1221
1246
|
this.runtime = runtime;
|
|
@@ -1244,23 +1269,82 @@ var DockerBackupExecutor = class {
|
|
|
1244
1269
|
);
|
|
1245
1270
|
}
|
|
1246
1271
|
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Ask the Docker daemon about a path in a container's mount namespace.
|
|
1274
|
+
*
|
|
1275
|
+
* The API may itself run in a container or connect to Docker over SSH/TLS, so
|
|
1276
|
+
* `node:fs` would inspect the wrong filesystem. Docker's archive-info endpoint
|
|
1277
|
+
* returns the daemon-side path's Go FileMode without copying any archive bytes.
|
|
1278
|
+
*/
|
|
1279
|
+
async containerPathIsDirectory(container, path) {
|
|
1280
|
+
let response;
|
|
1281
|
+
try {
|
|
1282
|
+
response = await container.infoArchive({ path });
|
|
1283
|
+
return archivePathIsDirectory(response, path);
|
|
1284
|
+
} finally {
|
|
1285
|
+
response?.resume?.();
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Classify a recorded bind source when there is no service container to inspect.
|
|
1290
|
+
* A never-started helper gives Docker a mount namespace, and archive-info reports
|
|
1291
|
+
* the source type from the daemon host. `Mounts` (rather than legacy `Binds`) is
|
|
1292
|
+
* deliberate: a missing source is rejected instead of being created as a directory.
|
|
1293
|
+
*/
|
|
1294
|
+
async declaredBindIsDirectory(sourcePath) {
|
|
1295
|
+
try {
|
|
1296
|
+
return await this.withHelper(
|
|
1297
|
+
{
|
|
1298
|
+
Image: HELPER_IMAGE,
|
|
1299
|
+
NetworkDisabled: true,
|
|
1300
|
+
HostConfig: {
|
|
1301
|
+
Mounts: [
|
|
1302
|
+
{
|
|
1303
|
+
Type: "bind",
|
|
1304
|
+
Source: sourcePath,
|
|
1305
|
+
Target: BIND_PROBE_TARGET,
|
|
1306
|
+
ReadOnly: true
|
|
1307
|
+
}
|
|
1308
|
+
]
|
|
1309
|
+
}
|
|
1310
|
+
},
|
|
1311
|
+
(helper) => this.containerPathIsDirectory(helper, BIND_PROBE_TARGET)
|
|
1312
|
+
);
|
|
1313
|
+
} catch (err) {
|
|
1314
|
+
if (isMissingBindSourceError(err)) return true;
|
|
1315
|
+
throw new Error(
|
|
1316
|
+
`Could not inspect bind mount source "${sourcePath}" on the Docker host: ` + safeErrorMessage(err),
|
|
1317
|
+
{ cause: err }
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1247
1321
|
async listSources(service) {
|
|
1248
1322
|
if (service.containerId) {
|
|
1323
|
+
const container = this.dockerode.getContainer(service.containerId);
|
|
1324
|
+
let data = null;
|
|
1249
1325
|
try {
|
|
1250
|
-
|
|
1251
|
-
const mounts = data.Mounts ?? [];
|
|
1252
|
-
return mounts.filter((m2) => m2.Type === "volume" || m2.Type === "bind").map(
|
|
1253
|
-
(m2, i2) => ({
|
|
1254
|
-
id: m2.Name ?? m2.Source ?? `mount-${i2}`,
|
|
1255
|
-
target: m2.Destination ?? "",
|
|
1256
|
-
source: m2.Name ?? m2.Source ?? "",
|
|
1257
|
-
type: m2.Type ?? "volume"
|
|
1258
|
-
})
|
|
1259
|
-
);
|
|
1326
|
+
data = await container.inspect();
|
|
1260
1327
|
} catch {
|
|
1261
1328
|
}
|
|
1329
|
+
if (data) {
|
|
1330
|
+
const mounts = data.Mounts ?? [];
|
|
1331
|
+
const sources2 = [];
|
|
1332
|
+
for (const [i2, mount] of mounts.entries()) {
|
|
1333
|
+
if (mount.Type !== "volume" && mount.Type !== "bind") continue;
|
|
1334
|
+
if (mount.Type === "bind" && (!mount.Destination || !await this.containerPathIsDirectory(container, mount.Destination))) {
|
|
1335
|
+
continue;
|
|
1336
|
+
}
|
|
1337
|
+
sources2.push({
|
|
1338
|
+
id: mount.Name ?? mount.Source ?? `mount-${i2}`,
|
|
1339
|
+
target: mount.Destination ?? "",
|
|
1340
|
+
source: mount.Name ?? mount.Source ?? "",
|
|
1341
|
+
type: mount.Type
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
return sources2;
|
|
1345
|
+
}
|
|
1262
1346
|
}
|
|
1263
|
-
|
|
1347
|
+
const sources = service.volumes.map((spec, i2) => {
|
|
1264
1348
|
const parsed = parseVolumeSpec(spec);
|
|
1265
1349
|
if (!parsed || !parsed.source) return null;
|
|
1266
1350
|
const source = parsed.type === "volume" && service.namespaceVolumes ? ensureScopedVolumeName(service.projectSlug, parsed.source) : parsed.source;
|
|
@@ -1271,6 +1355,15 @@ var DockerBackupExecutor = class {
|
|
|
1271
1355
|
type: parsed.type
|
|
1272
1356
|
};
|
|
1273
1357
|
}).filter((x2) => x2 !== null);
|
|
1358
|
+
if (!sources.some((source) => source.type === "bind")) return sources;
|
|
1359
|
+
await this.ensureImage(HELPER_IMAGE);
|
|
1360
|
+
const backupable = [];
|
|
1361
|
+
for (const source of sources) {
|
|
1362
|
+
if (source.type !== "bind" || await this.declaredBindIsDirectory(source.source)) {
|
|
1363
|
+
backupable.push(source);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
return backupable;
|
|
1274
1367
|
}
|
|
1275
1368
|
/**
|
|
1276
1369
|
* The container's own `Config.Env`, as `KEY=value` pairs already split.
|
|
@@ -8,13 +8,14 @@ import {
|
|
|
8
8
|
getLocalShellPath,
|
|
9
9
|
logEntry,
|
|
10
10
|
sq
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-2BAKEGGF.js";
|
|
12
12
|
|
|
13
13
|
// ../../packages/adapters/src/system/local-executor.ts
|
|
14
14
|
import { exec, spawn } from "child_process";
|
|
15
15
|
import {
|
|
16
16
|
access,
|
|
17
17
|
mkdir as fsMkdir,
|
|
18
|
+
open as fsOpen,
|
|
18
19
|
readFile as fsReadFile,
|
|
19
20
|
rename as fsRename,
|
|
20
21
|
rm as fsRm,
|
|
@@ -82,9 +83,19 @@ var LocalExecutor = class {
|
|
|
82
83
|
});
|
|
83
84
|
});
|
|
84
85
|
}
|
|
85
|
-
async writeFile(path, content) {
|
|
86
|
+
async writeFile(path, content, opts) {
|
|
86
87
|
await fsMkdir(dirname(path), { recursive: true });
|
|
87
|
-
|
|
88
|
+
if (opts?.mode === void 0) {
|
|
89
|
+
await fsWriteFile(path, content, "utf-8");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const file = await fsOpen(path, "w", opts.mode);
|
|
93
|
+
try {
|
|
94
|
+
await file.chmod(opts.mode);
|
|
95
|
+
await file.writeFile(content, "utf-8");
|
|
96
|
+
} finally {
|
|
97
|
+
await file.close();
|
|
98
|
+
}
|
|
88
99
|
}
|
|
89
100
|
async rename(from, to) {
|
|
90
101
|
await fsRename(from, to);
|
|
@@ -44,6 +44,15 @@ var SshDisconnectedError = class extends Error {
|
|
|
44
44
|
this.name = "SshDisconnectedError";
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
|
+
var SshExecRequestError = class extends Error {
|
|
48
|
+
constructor(cause) {
|
|
49
|
+
super(cause.message, { cause });
|
|
50
|
+
this.name = "SshExecRequestError";
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
function isSshExecRequestError(err) {
|
|
54
|
+
return err instanceof SshExecRequestError || err instanceof Error && err.name === "SshExecRequestError";
|
|
55
|
+
}
|
|
47
56
|
function isSshDisconnectedError(err) {
|
|
48
57
|
return err instanceof SshDisconnectedError || err instanceof Error && err.name === "SshDisconnectedError";
|
|
49
58
|
}
|
|
@@ -67,7 +76,7 @@ function isRetryableRemoteConnectionError(err) {
|
|
|
67
76
|
);
|
|
68
77
|
}
|
|
69
78
|
function isRemoteConnectionError(err) {
|
|
70
|
-
return isSshAuthError(err) || isRetryableRemoteConnectionError(err);
|
|
79
|
+
return isSshAuthError(err) || isRetryableRemoteConnectionError(err) || isSshExecRequestError(err);
|
|
71
80
|
}
|
|
72
81
|
var MAX_COMMAND_CHARS = 120;
|
|
73
82
|
function commandForError(command) {
|
|
@@ -100,6 +109,8 @@ function formatDuration(startMs) {
|
|
|
100
109
|
|
|
101
110
|
export {
|
|
102
111
|
SshDisconnectedError,
|
|
112
|
+
SshExecRequestError,
|
|
113
|
+
isSshExecRequestError,
|
|
103
114
|
isSshAuthError,
|
|
104
115
|
HostChannelUnavailableError,
|
|
105
116
|
isRetryableRemoteConnectionError,
|
|
@@ -7,13 +7,43 @@ import {
|
|
|
7
7
|
isContainerPortForwarder,
|
|
8
8
|
managedDeploymentIdFromUnit,
|
|
9
9
|
tryExec
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-7TQP6723.js";
|
|
11
11
|
import {
|
|
12
12
|
shellQuote
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-COI4VFRW.js";
|
|
14
14
|
|
|
15
15
|
// ../../packages/adapters/src/system/port-listen.ts
|
|
16
16
|
var PROC_NET_TCP_FILES = ["/proc/net/tcp", "/proc/net/tcp6"];
|
|
17
|
+
var PROBE_LISTENING = "__OPENSHIP_PORT_LISTENING__";
|
|
18
|
+
var PROBE_FREE = "__OPENSHIP_PORT_FREE__";
|
|
19
|
+
var PROBE_UNAVAILABLE = "__OPENSHIP_PORT_UNAVAILABLE__";
|
|
20
|
+
function buildPortProbeCommand(port) {
|
|
21
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
22
|
+
throw new RangeError(`Invalid TCP port: ${port}`);
|
|
23
|
+
}
|
|
24
|
+
return [
|
|
25
|
+
`lsof_output="$(lsof -nP -t -iTCP:${port} -sTCP:LISTEN 2>&1)"`,
|
|
26
|
+
`lsof_status=$?`,
|
|
27
|
+
`if [ "$lsof_status" -eq 0 ] && [ -n "$lsof_output" ] && printf '%s\\n' "$lsof_output" | grep -Eqv '^[0-9]+$'; then`,
|
|
28
|
+
` : # Unexpected lsof output: fall through to ss instead of guessing.`,
|
|
29
|
+
`elif [ "$lsof_status" -eq 0 ] && [ -n "$lsof_output" ]; then`,
|
|
30
|
+
` printf '${PROBE_LISTENING}\\n'`,
|
|
31
|
+
` exit 0`,
|
|
32
|
+
`elif [ "$lsof_status" -eq 1 ] && [ -z "$lsof_output" ]; then`,
|
|
33
|
+
` printf '${PROBE_FREE}\\n'`,
|
|
34
|
+
` exit 0`,
|
|
35
|
+
`fi`,
|
|
36
|
+
`ss_output="$(ss -tln "sport = :${port}" 2>&1)"`,
|
|
37
|
+
`ss_status=$?`,
|
|
38
|
+
`if [ "$ss_status" -eq 0 ] && printf '%s\\n' "$ss_output" | grep -q LISTEN; then`,
|
|
39
|
+
` printf '${PROBE_LISTENING}\\n'`,
|
|
40
|
+
`elif [ "$ss_status" -eq 0 ]; then`,
|
|
41
|
+
` printf '${PROBE_FREE}\\n'`,
|
|
42
|
+
`else`,
|
|
43
|
+
` printf '${PROBE_UNAVAILABLE}\\n'`,
|
|
44
|
+
`fi`
|
|
45
|
+
].join("\n");
|
|
46
|
+
}
|
|
17
47
|
var TCP_LISTEN = "0A";
|
|
18
48
|
function parseListeningPorts(procText) {
|
|
19
49
|
const ports = /* @__PURE__ */ new Set();
|
|
@@ -33,7 +63,15 @@ function parseListeningPorts(procText) {
|
|
|
33
63
|
}
|
|
34
64
|
return ports;
|
|
35
65
|
}
|
|
66
|
+
function parsePortProbeOutput(out) {
|
|
67
|
+
const trimmed = out.trim();
|
|
68
|
+
if (trimmed === PROBE_LISTENING) return true;
|
|
69
|
+
if (trimmed === PROBE_FREE) return false;
|
|
70
|
+
if (trimmed === PROBE_UNAVAILABLE) return null;
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
36
73
|
async function probePortListeningOnce(executor, port) {
|
|
74
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) return null;
|
|
37
75
|
const readable = [];
|
|
38
76
|
for (const file of PROC_NET_TCP_FILES) {
|
|
39
77
|
try {
|
|
@@ -41,8 +79,13 @@ async function probePortListeningOnce(executor, port) {
|
|
|
41
79
|
} catch {
|
|
42
80
|
}
|
|
43
81
|
}
|
|
44
|
-
if (readable.length
|
|
45
|
-
|
|
82
|
+
if (readable.length > 0) return parseListeningPorts(readable.join("\n")).has(port);
|
|
83
|
+
try {
|
|
84
|
+
const out = await executor.exec(buildPortProbeCommand(port), { timeout: 5e3 });
|
|
85
|
+
return parsePortProbeOutput(out);
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
46
89
|
}
|
|
47
90
|
function delay(ms) {
|
|
48
91
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
resolvePorts,
|
|
29
29
|
sourceBuildDir,
|
|
30
30
|
verifyHostChannel
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-H36ASAYI.js";
|
|
32
32
|
import {
|
|
33
33
|
bootstrapAdmin,
|
|
34
34
|
importMigratedSites,
|
|
@@ -36,18 +36,18 @@ import {
|
|
|
36
36
|
internalPost,
|
|
37
37
|
mintBareInternalToken,
|
|
38
38
|
waitHealthy
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-OEYXXSAM.js";
|
|
40
40
|
import {
|
|
41
41
|
startUnitHint,
|
|
42
42
|
thisHost
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-IXBOV7BE.js";
|
|
44
44
|
import {
|
|
45
45
|
completeHostEdge,
|
|
46
46
|
markStoppedProxyImported,
|
|
47
47
|
planAndApplyHostEdge,
|
|
48
48
|
previewHostEdge,
|
|
49
49
|
rollbackHostEdge
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-WG5PDWZ3.js";
|
|
51
51
|
import {
|
|
52
52
|
AUTH_SECRET_FILE,
|
|
53
53
|
DATA_DIR,
|
|
@@ -58,17 +58,17 @@ import {
|
|
|
58
58
|
} from "./chunk-ELGRMZOW.js";
|
|
59
59
|
import {
|
|
60
60
|
LocalExecutor
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-KCMEWNP7.js";
|
|
62
62
|
import {
|
|
63
63
|
edgeCrashReason,
|
|
64
64
|
edgeIsBroken
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-37XZTLBJ.js";
|
|
66
66
|
import {
|
|
67
67
|
EDGE_CONTAINER_NAME
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-7TQP6723.js";
|
|
69
69
|
import {
|
|
70
70
|
isValidEmail
|
|
71
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-COI4VFRW.js";
|
|
72
72
|
|
|
73
73
|
// src/commands/up.ts
|
|
74
74
|
import { Command } from "commander";
|
|
@@ -1564,7 +1564,7 @@ async function runForeground(opts, source) {
|
|
|
1564
1564
|
const uiSpinner = ora("Preparing the dashboard\u2026").start();
|
|
1565
1565
|
try {
|
|
1566
1566
|
const bundle = await ensureDashboard({
|
|
1567
|
-
tag: source ? "local" : opts.uiVersion || `v${"0.
|
|
1567
|
+
tag: source ? "local" : opts.uiVersion || `v${"0.7.1"}`,
|
|
1568
1568
|
onProgress: (received, total) => {
|
|
1569
1569
|
if (total) {
|
|
1570
1570
|
uiSpinner.text = `Downloading dashboard\u2026 ${Math.round(received / total * 100)}%`;
|
|
@@ -7,20 +7,20 @@ import {
|
|
|
7
7
|
composeInstallExists,
|
|
8
8
|
readComposeEnvFile,
|
|
9
9
|
startUnitHint
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-IXBOV7BE.js";
|
|
11
11
|
import {
|
|
12
12
|
INTERNAL_TOKEN_FILE,
|
|
13
13
|
OS_DIR
|
|
14
14
|
} from "./chunk-ELGRMZOW.js";
|
|
15
15
|
import {
|
|
16
16
|
LocalExecutor
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-KCMEWNP7.js";
|
|
18
18
|
import {
|
|
19
19
|
edgeCrashReason
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-37XZTLBJ.js";
|
|
21
21
|
import {
|
|
22
22
|
EDGE_CONTAINER_NAME
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-7TQP6723.js";
|
|
24
24
|
|
|
25
25
|
// src/lib/edge-import.ts
|
|
26
26
|
import chalk from "chalk";
|
|
@@ -13,14 +13,14 @@ import {
|
|
|
13
13
|
resolveDockerRootDirectory,
|
|
14
14
|
resolveDockerfileCandidates,
|
|
15
15
|
resolveWithinDirectory
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-IVMPA6RC.js";
|
|
17
17
|
import {
|
|
18
18
|
getTarCreateEnv,
|
|
19
19
|
prepareSourceTarArgs
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-2YAY5XHK.js";
|
|
21
21
|
import {
|
|
22
22
|
isValidEnvKey
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-COI4VFRW.js";
|
|
24
24
|
import {
|
|
25
25
|
__commonJS,
|
|
26
26
|
__toESM
|