openship 0.6.6 → 0.6.7
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-7QD623QV.js +29 -0
- package/dist/{chunk-Q7WGTQQ5.js → chunk-2ANV5ZQ5.js} +1 -1
- package/dist/{chunk-UJ6JZM65.js → chunk-2VSW6Q6C.js} +601 -382
- package/dist/{chunk-PCPVTH6M.js → chunk-5T4UVOM3.js} +6 -4
- package/dist/{chunk-YMMQRE5J.js → chunk-BLEXTYRK.js} +1 -1
- package/dist/{chunk-OBPKYXQL.js → chunk-CIJV43LH.js} +71 -32
- package/dist/{chunk-NK5N5LXN.js → chunk-DDFUESPJ.js} +24 -9
- package/dist/{chunk-6FH4AXVY.js → chunk-E2X26PXR.js} +1 -1
- package/dist/{chunk-HKRQXPTI.js → chunk-FYPFEDPZ.js} +1 -1
- package/dist/{chunk-UVNMQM7W.js → chunk-FZ6QCF6B.js} +8 -6
- package/dist/{chunk-MLYSEZQB.js → chunk-GGIAIGUL.js} +15 -66
- package/dist/{chunk-2YM3NI2E.js → chunk-GIPJNNI5.js} +38 -44
- package/dist/{chunk-ZFJXPCQP.js → chunk-H3WT3A6R.js} +4 -3
- package/dist/chunk-H4FS5BED.js +936 -0
- package/dist/{chunk-CBLD2HS5.js → chunk-J2XM3QSP.js} +8 -5
- package/dist/{chunk-FE7ZZQSE.js → chunk-J3K6XABC.js} +3 -3
- package/dist/{chunk-77QDF6UD.js → chunk-LHFXSC36.js} +428 -93
- package/dist/{chunk-YIMOCZBZ.js → chunk-MEE6OZFP.js} +3 -3
- package/dist/chunk-N5YZMO33.js +111 -0
- package/dist/{chunk-QVW76WDL.js → chunk-NRTJRNAE.js} +58 -16
- package/dist/{chunk-WVAML4H6.js → chunk-PPEBIY5F.js} +247 -61
- package/dist/{chunk-4I2BM2DT.js → chunk-PVP4RGWT.js} +3 -3
- package/dist/{chunk-RSR7PFIQ.js → chunk-QHMDCYZ3.js} +4 -4
- package/dist/chunk-R4VDPFLS.js +106 -0
- package/dist/{chunk-VTGPEC6B.js → chunk-RDXT4BQB.js} +6 -4
- package/dist/{chunk-LFE5KQ7Y.js → chunk-S6B2Z3OO.js} +142 -57
- package/dist/chunk-UWEV6XTS.js +249 -0
- package/dist/{chunk-5DMWSTOM.js → chunk-VQM6JHJB.js} +940 -224
- package/dist/{chunk-SM26HDS2.js → chunk-VSTFHUCV.js} +44 -50
- package/dist/chunk-WU54BXFE.js +70 -0
- package/dist/{chunk-LUKYPVJ4.js → chunk-XWHWTHEL.js} +313 -469
- package/dist/{chunk-A4GXYE4L.js → chunk-ZPJ74LXE.js} +22 -20
- package/dist/cloud-KNNABE3O.js +40 -0
- package/dist/{detect-OIE5V3VZ.js → detect-G4DR3U2N.js} +9 -6
- package/dist/{docker-VTR2GK5Y.js → docker-YOVPCIB4.js} +11 -8
- package/dist/{docker-edge-executor-22IBXNYJ.js → docker-edge-executor-QFXD3337.js} +8 -6
- package/dist/edge-import-CRFVSR6P.js +56 -0
- package/dist/{edge-preflight-GBNCPNVU.js → edge-preflight-MXERVAMG.js} +15 -12
- package/dist/{ensure-container-edge-KPI2CVXJ.js → ensure-container-edge-F4JUNQIK.js} +13 -10
- package/dist/{executor-7LXXZPBR.js → executor-CVPAYYHR.js} +8 -6
- package/dist/host-channel-preflight-QTDNGLCK.js +54 -0
- package/dist/index.js +84 -48
- package/dist/{local-executor-WOFZD72X.js → local-executor-ODC6RANU.js} +3 -2
- package/dist/{nginx-KUFMLMAC.js → nginx-KLFF43TC.js} +11 -8
- package/dist/{openresty-lua-ATTH35BE.js → openresty-lua-VKWUV4GB.js} +6 -5
- package/dist/{privilege-I4W3ZNZK.js → privilege-7D3SSOL3.js} +6 -5
- package/dist/server/index.js +8813 -5232
- package/dist/setup-I5AJC6NJ.js +25 -0
- package/dist/{ssh-client-JBMA7SN4.js → ssh-client-625VWGKP.js} +3 -2
- package/dist/up-QXTOOVXC.js +61 -0
- package/package.json +1 -1
- package/dist/bare-OXJVW3AK.js +0 -25
- package/dist/chunk-LH2PLRQO.js +0 -434
- 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
|
@@ -4,17 +4,20 @@ import { createRequire as __ospCreateRequire } from "node:module";
|
|
|
4
4
|
const require = __ospCreateRequire(import.meta.url);
|
|
5
5
|
import {
|
|
6
6
|
assembleGitClone,
|
|
7
|
+
dockerBuildContextDirectory,
|
|
8
|
+
generateDockerfile,
|
|
7
9
|
localGitSshWriter,
|
|
8
10
|
materializeGitSsh,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
normalizeDockerRelativePath,
|
|
12
|
+
resolveContextDockerfileCandidates,
|
|
13
|
+
resolveDockerRootDirectory,
|
|
14
|
+
resolveDockerfileCandidates,
|
|
15
|
+
resolveWithinDirectory
|
|
16
|
+
} from "./chunk-H4FS5BED.js";
|
|
11
17
|
import {
|
|
12
18
|
getTarCreateEnv,
|
|
13
19
|
prepareSourceTarArgs
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import {
|
|
16
|
-
packageManagerEnsureCommand
|
|
17
|
-
} from "./chunk-UJ6JZM65.js";
|
|
20
|
+
} from "./chunk-2ANV5ZQ5.js";
|
|
18
21
|
import {
|
|
19
22
|
__commonJS,
|
|
20
23
|
__toESM
|
|
@@ -478,451 +481,227 @@ var require_ignore = __commonJS({
|
|
|
478
481
|
}
|
|
479
482
|
});
|
|
480
483
|
|
|
481
|
-
// ../../packages/adapters/src/runtime/docker-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
if (segments.includes("..")) {
|
|
490
|
-
throw new Error(
|
|
491
|
-
`Invalid ${label} ${JSON.stringify(value)}: path segments must not contain "..".`
|
|
492
|
-
);
|
|
493
|
-
}
|
|
494
|
-
return segments;
|
|
495
|
-
}
|
|
496
|
-
function normalizeDockerRelativePath(value) {
|
|
497
|
-
const normalized = value?.trim().replace(/^\.\//, "").replace(/^\/+|\/+$/g, "");
|
|
498
|
-
if (!normalized || normalized === ".") {
|
|
499
|
-
return "";
|
|
500
|
-
}
|
|
501
|
-
return toSafeSegments(normalized, "path").join("/");
|
|
502
|
-
}
|
|
503
|
-
function resolveWithinDirectory(baseDir, relativePath) {
|
|
504
|
-
const base = resolve(baseDir);
|
|
505
|
-
const target = resolve(base, ...toSafeSegments(relativePath?.trim() ?? "", "path"));
|
|
506
|
-
const escape = relative(base, target);
|
|
507
|
-
if (escape === ".." || escape.startsWith(`..${sep}`) || isAbsolute(escape)) {
|
|
508
|
-
throw new Error(`Resolved path ${JSON.stringify(target)} escapes ${JSON.stringify(base)}.`);
|
|
484
|
+
// ../../packages/adapters/src/runtime/docker-buildkit-trace.ts
|
|
485
|
+
var WIRE_VARINT = 0;
|
|
486
|
+
var WIRE_64BIT = 1;
|
|
487
|
+
var WIRE_LENGTH = 2;
|
|
488
|
+
var WIRE_32BIT = 5;
|
|
489
|
+
var WireReader = class {
|
|
490
|
+
constructor(buf) {
|
|
491
|
+
this.buf = buf;
|
|
509
492
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
let normalized = rootDirectory?.trim() ?? "";
|
|
514
|
-
if (!normalized) {
|
|
515
|
-
return "";
|
|
493
|
+
pos = 0;
|
|
494
|
+
get done() {
|
|
495
|
+
return this.pos >= this.buf.length;
|
|
516
496
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
497
|
+
/** Varint as a Number. Bails past 10 bytes (the protobuf maximum) so a corrupt
|
|
498
|
+
* buffer cannot spin. Values we actually read (`stream`, `cached`) are tiny. */
|
|
499
|
+
varint() {
|
|
500
|
+
let result = 0;
|
|
501
|
+
let shift = 0;
|
|
502
|
+
for (let i = 0; i < 10; i += 1) {
|
|
503
|
+
if (this.done) return null;
|
|
504
|
+
const byte = this.buf[this.pos];
|
|
505
|
+
this.pos += 1;
|
|
506
|
+
result += (byte & 127) * 2 ** shift;
|
|
507
|
+
if ((byte & 128) === 0) return result;
|
|
508
|
+
shift += 7;
|
|
521
509
|
}
|
|
522
|
-
|
|
523
|
-
normalized = relativePath;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
normalized = normalized.replace(/^\.\//, "").replace(/^\/+|\/+$/g, "");
|
|
527
|
-
if (!normalized || normalized === ".") {
|
|
528
|
-
return "";
|
|
529
|
-
}
|
|
530
|
-
return toSafeSegments(normalized, "rootDirectory").join("/");
|
|
531
|
-
}
|
|
532
|
-
function resolveExplicitDockerfileCandidate(rootDirectory, dockerfilePath) {
|
|
533
|
-
const normalizedRootDirectory = normalizeDockerRelativePath(rootDirectory);
|
|
534
|
-
const normalizedDockerfilePath = normalizeDockerRelativePath(dockerfilePath);
|
|
535
|
-
if (!normalizedDockerfilePath) {
|
|
536
|
-
return "";
|
|
510
|
+
return null;
|
|
537
511
|
}
|
|
538
|
-
|
|
539
|
-
|
|
512
|
+
/** Field header → { field, wire }. */
|
|
513
|
+
key() {
|
|
514
|
+
const tag = this.varint();
|
|
515
|
+
if (tag === null) return null;
|
|
516
|
+
const field = Math.floor(tag / 8);
|
|
517
|
+
if (field <= 0) return null;
|
|
518
|
+
return { field, wire: tag % 8 };
|
|
540
519
|
}
|
|
541
|
-
|
|
542
|
-
|
|
520
|
+
bytes() {
|
|
521
|
+
const length = this.varint();
|
|
522
|
+
if (length === null || length < 0 || this.pos + length > this.buf.length) return null;
|
|
523
|
+
const slice = this.buf.subarray(this.pos, this.pos + length);
|
|
524
|
+
this.pos += length;
|
|
525
|
+
return slice;
|
|
543
526
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
"Pipfile",
|
|
561
|
-
"go.mod",
|
|
562
|
-
"Cargo.toml",
|
|
563
|
-
"Gemfile",
|
|
564
|
-
"composer.json",
|
|
565
|
-
"pom.xml",
|
|
566
|
-
"build.gradle",
|
|
567
|
-
"build.gradle.kts",
|
|
568
|
-
"mix.exs",
|
|
569
|
-
"Dockerfile"
|
|
570
|
-
];
|
|
571
|
-
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
572
|
-
"node_modules",
|
|
573
|
-
".git",
|
|
574
|
-
".next",
|
|
575
|
-
".nuxt",
|
|
576
|
-
".output",
|
|
577
|
-
".turbo",
|
|
578
|
-
".venv",
|
|
579
|
-
"__pycache__",
|
|
580
|
-
"build",
|
|
581
|
-
"dist",
|
|
582
|
-
"target",
|
|
583
|
-
"vendor"
|
|
584
|
-
]);
|
|
585
|
-
async function pathExists(path) {
|
|
586
|
-
return access(path).then(() => true).catch(() => false);
|
|
587
|
-
}
|
|
588
|
-
async function manifestCount(dir) {
|
|
589
|
-
let count = 0;
|
|
590
|
-
for (const file of ROOT_MANIFESTS) {
|
|
591
|
-
if (await pathExists(join(dir, file))) {
|
|
592
|
-
count += 1;
|
|
527
|
+
/** Advance past a field we don't read. Returns false when the buffer is
|
|
528
|
+
* malformed, which ends the scan. */
|
|
529
|
+
skip(wire) {
|
|
530
|
+
switch (wire) {
|
|
531
|
+
case WIRE_VARINT:
|
|
532
|
+
return this.varint() !== null;
|
|
533
|
+
case WIRE_64BIT:
|
|
534
|
+
this.pos += 8;
|
|
535
|
+
return this.pos <= this.buf.length;
|
|
536
|
+
case WIRE_LENGTH:
|
|
537
|
+
return this.bytes() !== null;
|
|
538
|
+
case WIRE_32BIT:
|
|
539
|
+
this.pos += 4;
|
|
540
|
+
return this.pos <= this.buf.length;
|
|
541
|
+
default:
|
|
542
|
+
return false;
|
|
593
543
|
}
|
|
594
544
|
}
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
function
|
|
598
|
-
return
|
|
545
|
+
};
|
|
546
|
+
var decoder = new TextDecoder("utf-8", { fatal: false });
|
|
547
|
+
function text(bytes) {
|
|
548
|
+
return decoder.decode(bytes);
|
|
599
549
|
}
|
|
600
|
-
function
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
550
|
+
function decodeVertex(bytes) {
|
|
551
|
+
const reader = new WireReader(bytes);
|
|
552
|
+
const vertex = {
|
|
553
|
+
digest: "",
|
|
554
|
+
name: "",
|
|
555
|
+
cached: false,
|
|
556
|
+
started: false,
|
|
557
|
+
completed: false,
|
|
558
|
+
error: ""
|
|
559
|
+
};
|
|
560
|
+
while (!reader.done) {
|
|
561
|
+
const key = reader.key();
|
|
562
|
+
if (!key) return null;
|
|
563
|
+
if (key.field === 1 && key.wire === WIRE_LENGTH) {
|
|
564
|
+
const value = reader.bytes();
|
|
565
|
+
if (!value) return null;
|
|
566
|
+
vertex.digest = text(value);
|
|
567
|
+
} else if (key.field === 3 && key.wire === WIRE_LENGTH) {
|
|
568
|
+
const value = reader.bytes();
|
|
569
|
+
if (!value) return null;
|
|
570
|
+
vertex.name = text(value);
|
|
571
|
+
} else if (key.field === 4 && key.wire === WIRE_VARINT) {
|
|
572
|
+
const value = reader.varint();
|
|
573
|
+
if (value === null) return null;
|
|
574
|
+
vertex.cached = value !== 0;
|
|
575
|
+
} else if (key.field === 5 && key.wire === WIRE_LENGTH) {
|
|
576
|
+
if (!reader.bytes()) return null;
|
|
577
|
+
vertex.started = true;
|
|
578
|
+
} else if (key.field === 6 && key.wire === WIRE_LENGTH) {
|
|
579
|
+
if (!reader.bytes()) return null;
|
|
580
|
+
vertex.completed = true;
|
|
581
|
+
} else if (key.field === 7 && key.wire === WIRE_LENGTH) {
|
|
582
|
+
const value = reader.bytes();
|
|
583
|
+
if (!value) return null;
|
|
584
|
+
vertex.error = text(value);
|
|
585
|
+
} else if (!reader.skip(key.wire)) {
|
|
586
|
+
return null;
|
|
609
587
|
}
|
|
610
588
|
}
|
|
611
|
-
return
|
|
589
|
+
return vertex;
|
|
612
590
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
const entries = await readdir(currentDir, { withFileTypes: true }).catch(() => []);
|
|
633
|
-
for (const entry of entries) {
|
|
634
|
-
if (!entry.isDirectory() || IGNORED_DIRS.has(entry.name)) {
|
|
635
|
-
continue;
|
|
591
|
+
function decodeLog(bytes) {
|
|
592
|
+
const reader = new WireReader(bytes);
|
|
593
|
+
let vertex = "";
|
|
594
|
+
let msg = null;
|
|
595
|
+
while (!reader.done) {
|
|
596
|
+
const key = reader.key();
|
|
597
|
+
if (!key) return null;
|
|
598
|
+
if (key.field === 1 && key.wire === WIRE_LENGTH) {
|
|
599
|
+
const value = reader.bytes();
|
|
600
|
+
if (!value) return null;
|
|
601
|
+
vertex = text(value);
|
|
602
|
+
} else if (key.field === 4 && key.wire === WIRE_LENGTH) {
|
|
603
|
+
const value = reader.bytes();
|
|
604
|
+
if (!value) return null;
|
|
605
|
+
msg = value;
|
|
606
|
+
} else if (!reader.skip(key.wire)) {
|
|
607
|
+
return null;
|
|
636
608
|
}
|
|
637
|
-
await collectCandidates(rootDir, join(currentDir, entry.name), depth + 1, candidates);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
async function resolveDockerRootDirectory(contextDir, rootDirectory, localPath) {
|
|
641
|
-
const hasExplicitRootDirectory = typeof rootDirectory === "string";
|
|
642
|
-
const normalized = normalizeDockerRootDirectory(rootDirectory, localPath);
|
|
643
|
-
if (hasExplicitRootDirectory) {
|
|
644
|
-
return normalized;
|
|
645
|
-
}
|
|
646
|
-
if (normalized) {
|
|
647
|
-
return normalized;
|
|
648
|
-
}
|
|
649
|
-
const candidates = [];
|
|
650
|
-
await collectCandidates(contextDir, contextDir, 0, candidates);
|
|
651
|
-
if (candidates.length === 0) {
|
|
652
|
-
return "";
|
|
653
|
-
}
|
|
654
|
-
candidates.sort((left, right) => right.score - left.score);
|
|
655
|
-
const bestNonRoot = candidates.find((candidate) => candidate.path);
|
|
656
|
-
if (bestNonRoot && bestNonRoot.score > 0) {
|
|
657
|
-
return bestNonRoot.path;
|
|
658
|
-
}
|
|
659
|
-
return candidates[0]?.path ?? "";
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
// ../../packages/adapters/src/runtime/docker-build-plan.ts
|
|
663
|
-
var DOCKER_BUILD_EVENT_PREFIX = "[openship-build]";
|
|
664
|
-
var INLINE_BUILD_ENV_EXCLUDES = /* @__PURE__ */ new Set(["FORCE_COLOR", "TERM"]);
|
|
665
|
-
function formatDockerBuildEvent(step, status) {
|
|
666
|
-
return `${DOCKER_BUILD_EVENT_PREFIX} step=${step} status=${status}`;
|
|
667
|
-
}
|
|
668
|
-
function normalizeRelativePath(value) {
|
|
669
|
-
const normalized = value?.trim().replace(/^\.\//, "").replace(/^\/+|\/+$/g, "");
|
|
670
|
-
if (!normalized || normalized === ".") {
|
|
671
|
-
return "";
|
|
672
|
-
}
|
|
673
|
-
return normalized;
|
|
674
|
-
}
|
|
675
|
-
function builderSourceDir(rootDirectory) {
|
|
676
|
-
const normalized = normalizeDockerRootDirectory(rootDirectory);
|
|
677
|
-
return normalized ? `/workspace/${normalized}` : "/workspace";
|
|
678
|
-
}
|
|
679
|
-
function buildEnvPrefix(envVars) {
|
|
680
|
-
const assignments = Object.entries(envVars).filter(([key]) => !INLINE_BUILD_ENV_EXCLUDES.has(key)).filter(([key]) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(key)).map(([key, value]) => `${key}=${sq(value)}`);
|
|
681
|
-
if (!envVars.NO_COLOR) {
|
|
682
|
-
assignments.push("NO_COLOR='1'");
|
|
683
|
-
}
|
|
684
|
-
if (assignments.length === 0) {
|
|
685
|
-
return "";
|
|
686
609
|
}
|
|
687
|
-
|
|
688
|
-
}
|
|
689
|
-
function buildRunCommand(command, envPrefix) {
|
|
690
|
-
return `${envPrefix}${command}`;
|
|
691
|
-
}
|
|
692
|
-
function runtimeCopyDirectives(config, sourceDir) {
|
|
693
|
-
if (config.productionPaths && config.productionPaths.length > 0) {
|
|
694
|
-
return config.productionPaths.map((path) => {
|
|
695
|
-
const normalized = normalizeRelativePath(path);
|
|
696
|
-
const source = normalized ? `${sourceDir}/${normalized}` : sourceDir;
|
|
697
|
-
const target = normalized ? `/app/${normalized}` : "/app";
|
|
698
|
-
return `COPY --from=builder ${source} ${target}`;
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
return [`COPY --from=builder ${sourceDir} /app`];
|
|
702
|
-
}
|
|
703
|
-
function needsMultiStage(config) {
|
|
704
|
-
return config.buildImage !== config.runtimeImage;
|
|
705
|
-
}
|
|
706
|
-
function workspacePrepareRunLine(config, envPrefix, workspacePrepare) {
|
|
707
|
-
const pmEnsure = packageManagerEnsureCommand(config.packageManager);
|
|
708
|
-
const body = pmEnsure ? `${pmEnsure} && ${workspacePrepare}` : workspacePrepare;
|
|
709
|
-
return `RUN ${envPrefix}${body}`;
|
|
710
|
-
}
|
|
711
|
-
function stepsRunLine(config, envPrefix, opts) {
|
|
712
|
-
const install = opts.install ? config.installCommand : "";
|
|
713
|
-
const build = opts.build ? config.buildCommand : "";
|
|
714
|
-
const steps = [];
|
|
715
|
-
const pmEnsure = packageManagerEnsureCommand(opts.packageManager ?? config.packageManager);
|
|
716
|
-
if (pmEnsure && (install || build)) {
|
|
717
|
-
steps.push(pmEnsure);
|
|
718
|
-
}
|
|
719
|
-
if (install) {
|
|
720
|
-
steps.push(`printf '${formatDockerBuildEvent("install", "running")}\\n'`);
|
|
721
|
-
steps.push(buildRunCommand(install, envPrefix));
|
|
722
|
-
steps.push(`printf '${formatDockerBuildEvent("install", "completed")}\\n'`);
|
|
723
|
-
}
|
|
724
|
-
if (build) {
|
|
725
|
-
steps.push(`printf '${formatDockerBuildEvent("build", "running")}\\n'`);
|
|
726
|
-
steps.push(buildRunCommand(build, envPrefix));
|
|
727
|
-
steps.push(`printf '${formatDockerBuildEvent("build", "completed")}\\n'`);
|
|
728
|
-
}
|
|
729
|
-
return steps.length > 0 ? `RUN ${steps.join(" && ")}` : null;
|
|
730
|
-
}
|
|
731
|
-
function installBuildRunLine(config, envPrefix) {
|
|
732
|
-
return stepsRunLine(config, envPrefix, { install: true, build: true });
|
|
733
|
-
}
|
|
734
|
-
function installRunLine(config, envPrefix) {
|
|
735
|
-
return stepsRunLine(config, envPrefix, { install: true });
|
|
736
|
-
}
|
|
737
|
-
var JS_PACKAGE_MANAGERS = ["pnpm", "yarn", "bun", "npm"];
|
|
738
|
-
function buildOnlyRunLine(config, envPrefix) {
|
|
739
|
-
const command = config.buildCommand?.trim() ?? "";
|
|
740
|
-
const packageManager = JS_PACKAGE_MANAGERS.find(
|
|
741
|
-
(pm) => new RegExp(`(^|\\s|&&\\s*)${pm}\\s`).test(command)
|
|
742
|
-
);
|
|
743
|
-
return stepsRunLine(config, envPrefix, { build: true, packageManager });
|
|
744
|
-
}
|
|
745
|
-
function isPhpRuntime(config) {
|
|
746
|
-
return /^php:/.test(config.runtimeImage) || /frankenphp/i.test(config.runtimeImage);
|
|
610
|
+
if (!vertex.startsWith("sha256:") || !msg?.length) return null;
|
|
611
|
+
return { vertex, msg: text(msg) };
|
|
747
612
|
}
|
|
748
|
-
var
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
];
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
`WORKDIR /workspace`,
|
|
784
|
-
`COPY . /workspace`,
|
|
785
|
-
`WORKDIR ${sourceDir}`,
|
|
786
|
-
assetBuildLine
|
|
787
|
-
);
|
|
788
|
-
}
|
|
789
|
-
const docroot = normalizeRelativePath(config.outputDirectory) || "public";
|
|
790
|
-
lines.push(
|
|
791
|
-
`FROM ${config.runtimeImage} AS runtime`,
|
|
792
|
-
PHP_EXT_INSTALL_LINE,
|
|
793
|
-
// --chown on the COPY instead of a trailing `chown -R`: the recursive form
|
|
794
|
-
// rewrites every file into a second layer, doubling the app's size on disk.
|
|
795
|
-
// Ownership has to be settled HERE, before anything mounts: Docker seeds a
|
|
796
|
-
// fresh named volume from the image directory it covers, ownership included,
|
|
797
|
-
// so `/app/storage` is only writable by the runtime user if it was already
|
|
798
|
-
// owned by them in the image (see @repo/core volumes.ts).
|
|
799
|
-
`COPY --from=builder --chown=www-data:www-data ${sourceDir} /app`
|
|
800
|
-
);
|
|
801
|
-
if (assetBuildLine) {
|
|
802
|
-
lines.push(
|
|
803
|
-
`COPY --from=assets --chown=www-data:www-data ${sourceDir}/${docroot} /app/${docroot}`
|
|
804
|
-
);
|
|
805
|
-
}
|
|
806
|
-
lines.push(
|
|
807
|
-
`WORKDIR /app`,
|
|
808
|
-
// FrankenPHP's bundled Caddyfile reads the docroot from SERVER_ROOT (default
|
|
809
|
-
// `public/`, resolved against WORKDIR). Setting it keeps the project's
|
|
810
|
-
// outputDirectory authoritative for BOTH what gets served and where the asset
|
|
811
|
-
// stage's output lands — otherwise a project that renamed its docroot builds
|
|
812
|
-
// assets into one directory and serves another.
|
|
813
|
-
`ENV SERVER_ROOT=${docroot}/`,
|
|
814
|
-
// Caddy's config + data dirs are root-owned in the base image, and it writes
|
|
815
|
-
// to both on startup — without this the non-root switch below fails at boot.
|
|
816
|
-
`RUN mkdir -p /data/caddy /config/caddy && chown -R www-data:www-data /data/caddy /config/caddy`,
|
|
817
|
-
// The frankenphp binary carries cap_net_bind_service, so dropping root here
|
|
818
|
-
// still leaves any port bindable.
|
|
819
|
-
`USER www-data`,
|
|
820
|
-
`EXPOSE ${config.port}`
|
|
821
|
-
);
|
|
822
|
-
if (config.startCommand) {
|
|
823
|
-
lines.push(`CMD ["sh", "-c", ${JSON.stringify(config.startCommand)}]`);
|
|
824
|
-
}
|
|
825
|
-
return lines.join("\n");
|
|
826
|
-
}
|
|
827
|
-
function staticNginxTemplateLines(port) {
|
|
828
|
-
return [
|
|
829
|
-
"server {",
|
|
830
|
-
` listen ${port} default_server;`,
|
|
831
|
-
" root /usr/share/nginx/html;",
|
|
832
|
-
" index index.html;",
|
|
833
|
-
" location / { try_files $uri $uri/ /index.html; }",
|
|
834
|
-
"}"
|
|
835
|
-
];
|
|
836
|
-
}
|
|
837
|
-
function staticBuilderOutputPath(config) {
|
|
838
|
-
const sourceDir = builderSourceDir(
|
|
839
|
-
normalizeDockerRootDirectory(config.rootDirectory, config.localPath)
|
|
840
|
-
);
|
|
841
|
-
const output = normalizeRelativePath(config.outputDirectory);
|
|
842
|
-
return output ? `${sourceDir}/${output}` : sourceDir;
|
|
843
|
-
}
|
|
844
|
-
function generateStaticDockerfile(config) {
|
|
845
|
-
const sourceDir = builderSourceDir(
|
|
846
|
-
normalizeDockerRootDirectory(config.rootDirectory, config.localPath)
|
|
847
|
-
);
|
|
848
|
-
const envPrefix = buildEnvPrefix(config.envVars);
|
|
849
|
-
const workspacePrepare = config.workspacePrepareCommand?.trim();
|
|
850
|
-
const outputPath = staticBuilderOutputPath(config);
|
|
851
|
-
const nginxTemplate = staticNginxTemplateLines(config.port).map((line) => `'${line}'`).join(" ");
|
|
852
|
-
const lines = [
|
|
853
|
-
`FROM ${config.buildImage} AS builder`,
|
|
854
|
-
`WORKDIR /workspace`,
|
|
855
|
-
`COPY . /workspace`
|
|
856
|
-
];
|
|
857
|
-
if (workspacePrepare) {
|
|
858
|
-
lines.push(workspacePrepareRunLine(config, envPrefix, workspacePrepare));
|
|
859
|
-
}
|
|
860
|
-
lines.push(`WORKDIR ${sourceDir}`);
|
|
861
|
-
const stepsLine = installBuildRunLine(config, envPrefix);
|
|
862
|
-
if (stepsLine) lines.push(stepsLine);
|
|
863
|
-
if (config.staticExtractOnly) return lines.join("\n");
|
|
864
|
-
lines.push(
|
|
865
|
-
`FROM nginx:alpine AS runtime`,
|
|
866
|
-
`RUN rm -f /etc/nginx/conf.d/default.conf`,
|
|
867
|
-
`COPY --from=builder ${outputPath} /usr/share/nginx/html`,
|
|
868
|
-
`RUN printf '%s\\n' ${nginxTemplate} > /etc/nginx/conf.d/app.conf`,
|
|
869
|
-
`EXPOSE ${config.port}`,
|
|
870
|
-
`CMD ["nginx", "-g", "daemon off;"]`
|
|
871
|
-
);
|
|
872
|
-
return lines.join("\n");
|
|
873
|
-
}
|
|
874
|
-
function generateDockerfile(config) {
|
|
875
|
-
if (config.isStatic) {
|
|
876
|
-
return generateStaticDockerfile(config);
|
|
877
|
-
}
|
|
878
|
-
if (isPhpRuntime(config) && needsMultiStage(config)) {
|
|
879
|
-
return generatePhpDockerfile(config);
|
|
613
|
+
var BuildKitTraceDecoder = class {
|
|
614
|
+
numbers = /* @__PURE__ */ new Map();
|
|
615
|
+
announced = /* @__PURE__ */ new Set();
|
|
616
|
+
settled = /* @__PURE__ */ new Set();
|
|
617
|
+
/** Decode one `aux` payload into the lines it should add to the build log. */
|
|
618
|
+
push(auxBase64) {
|
|
619
|
+
let payload;
|
|
620
|
+
try {
|
|
621
|
+
payload = Uint8Array.from(Buffer.from(auxBase64, "base64"));
|
|
622
|
+
} catch {
|
|
623
|
+
return [];
|
|
624
|
+
}
|
|
625
|
+
if (payload.length === 0) return [];
|
|
626
|
+
const lines = [];
|
|
627
|
+
const reader = new WireReader(payload);
|
|
628
|
+
while (!reader.done) {
|
|
629
|
+
const key = reader.key();
|
|
630
|
+
if (!key) break;
|
|
631
|
+
if (key.field === 1 && key.wire === WIRE_LENGTH) {
|
|
632
|
+
const bytes = reader.bytes();
|
|
633
|
+
if (!bytes) break;
|
|
634
|
+
const vertex = decodeVertex(bytes);
|
|
635
|
+
if (vertex) lines.push(...this.renderVertex(vertex));
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
if (key.field === 3 && key.wire === WIRE_LENGTH) {
|
|
639
|
+
const bytes = reader.bytes();
|
|
640
|
+
if (!bytes) break;
|
|
641
|
+
const entry = decodeLog(bytes);
|
|
642
|
+
if (entry) lines.push(...this.renderLog(entry));
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
if (!reader.skip(key.wire)) break;
|
|
646
|
+
}
|
|
647
|
+
return lines;
|
|
880
648
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
const lines = multiStage ? [
|
|
888
|
-
`FROM ${config.buildImage} AS builder`,
|
|
889
|
-
`WORKDIR /workspace`,
|
|
890
|
-
`COPY . /workspace`
|
|
891
|
-
] : [
|
|
892
|
-
`FROM ${config.runtimeImage}`,
|
|
893
|
-
`WORKDIR /workspace`,
|
|
894
|
-
`COPY . /workspace`
|
|
895
|
-
];
|
|
896
|
-
if (workspacePrepare) {
|
|
897
|
-
lines.push(workspacePrepareRunLine(config, envPrefix, workspacePrepare));
|
|
649
|
+
numberFor(digest) {
|
|
650
|
+
const existing = this.numbers.get(digest);
|
|
651
|
+
if (existing !== void 0) return existing;
|
|
652
|
+
const next = this.numbers.size + 1;
|
|
653
|
+
this.numbers.set(digest, next);
|
|
654
|
+
return next;
|
|
898
655
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
lines
|
|
656
|
+
renderVertex(vertex) {
|
|
657
|
+
if (!vertex.digest) return [];
|
|
658
|
+
const number = this.numberFor(vertex.digest);
|
|
659
|
+
const lines = [];
|
|
660
|
+
if (vertex.name && !this.announced.has(vertex.digest)) {
|
|
661
|
+
this.announced.add(vertex.digest);
|
|
662
|
+
lines.push({ message: `#${number} ${vertex.name}`, level: "info" });
|
|
663
|
+
}
|
|
664
|
+
if ((vertex.completed || vertex.error) && !this.settled.has(vertex.digest)) {
|
|
665
|
+
this.settled.add(vertex.digest);
|
|
666
|
+
if (vertex.error) {
|
|
667
|
+
lines.push({ message: `#${number} ERROR: ${vertex.error}`, level: "error" });
|
|
668
|
+
} else if (vertex.cached) {
|
|
669
|
+
lines.push({ message: `#${number} CACHED`, level: "info" });
|
|
670
|
+
} else {
|
|
671
|
+
lines.push({ message: `#${number} DONE`, level: "info" });
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return lines;
|
|
903
675
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
lines.push(`WORKDIR /app`);
|
|
676
|
+
renderLog(entry) {
|
|
677
|
+
const number = this.numberFor(entry.vertex);
|
|
678
|
+
return entry.msg.split(/\r?\n/).filter((line) => line.trim().length > 0).map((line) => ({ message: `#${number} ${line}`, level: "info" }));
|
|
908
679
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
680
|
+
};
|
|
681
|
+
function dockerfileNeedsBuildKit(contents) {
|
|
682
|
+
if (/^\s*#\s*syntax\s*=/im.test(contents.slice(0, 4096))) return true;
|
|
683
|
+
for (const line of contents.split(/\r?\n/)) {
|
|
684
|
+
if (/^\s*#/.test(line)) continue;
|
|
685
|
+
const match = /^\s*(RUN|COPY|ADD)((?:\s+--[^\s]+)*)/i.exec(line);
|
|
686
|
+
if (!match) continue;
|
|
687
|
+
const flags = match[2] ?? "";
|
|
688
|
+
if (/--(mount|network|security)\b/i.test(flags)) return true;
|
|
689
|
+
if (/--(link|checksum|keep-git-dir)\b/i.test(flags)) return true;
|
|
690
|
+
if (/(?:^|\s)<<-?\s*['"]?[A-Za-z_][A-Za-z0-9_]*['"]?(?:\s|$)/.test(line)) return true;
|
|
912
691
|
}
|
|
913
|
-
return
|
|
692
|
+
return false;
|
|
914
693
|
}
|
|
915
694
|
|
|
916
695
|
// ../../packages/adapters/src/runtime/docker-build-context.ts
|
|
917
696
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
918
697
|
import { spawn } from "child_process";
|
|
919
|
-
import { access
|
|
698
|
+
import { access, mkdtemp, readdir, readFile, realpath, rm, stat, writeFile } from "fs/promises";
|
|
920
699
|
import { tmpdir } from "os";
|
|
921
|
-
import { join
|
|
700
|
+
import { isAbsolute, join, relative, sep } from "path";
|
|
922
701
|
var GIT_CLONE_IDLE_TIMEOUT_MS = 5 * 6e4;
|
|
923
702
|
var GIT_CHECKOUT_IDLE_TIMEOUT_MS = 6e4;
|
|
924
703
|
function spawnGit(args, opts) {
|
|
925
|
-
return new Promise((
|
|
704
|
+
return new Promise((resolve, reject) => {
|
|
926
705
|
const child = spawn("git", args, {
|
|
927
706
|
stdio: ["ignore", "pipe", "pipe"],
|
|
928
707
|
env: {
|
|
@@ -961,9 +740,9 @@ function spawnGit(args, opts) {
|
|
|
961
740
|
});
|
|
962
741
|
child.stderr?.on("data", (buf) => {
|
|
963
742
|
armIdle();
|
|
964
|
-
const
|
|
965
|
-
stderr +=
|
|
966
|
-
for (const ln of
|
|
743
|
+
const text2 = buf.toString();
|
|
744
|
+
stderr += text2;
|
|
745
|
+
for (const ln of text2.split(/\r?\n/)) flushLine(ln);
|
|
967
746
|
});
|
|
968
747
|
armIdle();
|
|
969
748
|
child.on("error", (err) => {
|
|
@@ -972,14 +751,14 @@ function spawnGit(args, opts) {
|
|
|
972
751
|
});
|
|
973
752
|
child.on("close", (code) => {
|
|
974
753
|
clearTimeout(idleTimer);
|
|
975
|
-
if (code === 0)
|
|
754
|
+
if (code === 0) resolve();
|
|
976
755
|
else reject(new Error(`git exited with code ${code}: ${stderr.trim().slice(-500) || "no stderr"}`));
|
|
977
756
|
});
|
|
978
757
|
});
|
|
979
758
|
}
|
|
980
759
|
var GENERATED_DOCKERFILE_NAME = "Dockerfile.openship";
|
|
981
|
-
function
|
|
982
|
-
return value.split(
|
|
760
|
+
function toPosixPath(value) {
|
|
761
|
+
return value.split(sep).filter(Boolean).join("/");
|
|
983
762
|
}
|
|
984
763
|
function anchorDockerignorePattern(line) {
|
|
985
764
|
const trimmed = line.trim();
|
|
@@ -992,7 +771,7 @@ function anchorDockerignorePattern(line) {
|
|
|
992
771
|
}
|
|
993
772
|
async function loadDockerignoreMatcher(rootPath) {
|
|
994
773
|
try {
|
|
995
|
-
const contents = await readFile(
|
|
774
|
+
const contents = await readFile(join(rootPath, ".dockerignore"), "utf-8");
|
|
996
775
|
return (0, import_ignore.default)().add(contents.split(/\r?\n/).map(anchorDockerignorePattern));
|
|
997
776
|
} catch {
|
|
998
777
|
return void 0;
|
|
@@ -1009,16 +788,45 @@ function buildFilePaths(config) {
|
|
|
1009
788
|
}
|
|
1010
789
|
return paths;
|
|
1011
790
|
}
|
|
791
|
+
function contextKeepPaths(dockerfileName) {
|
|
792
|
+
const paths = /* @__PURE__ */ new Set([".dockerignore"]);
|
|
793
|
+
const segments = dockerfileName.split("/").filter(Boolean);
|
|
794
|
+
for (let depth = 1; depth <= segments.length; depth += 1) {
|
|
795
|
+
paths.add(segments.slice(0, depth).join("/"));
|
|
796
|
+
}
|
|
797
|
+
return paths;
|
|
798
|
+
}
|
|
799
|
+
async function loadContextIgnore(buildContextDir, keep) {
|
|
800
|
+
const matcher = await loadDockerignoreMatcher(buildContextDir);
|
|
801
|
+
if (!matcher) return void 0;
|
|
802
|
+
return (relativePosixPath) => !keep.has(relativePosixPath) && matcher.ignores(relativePosixPath);
|
|
803
|
+
}
|
|
804
|
+
async function resolveBuildContextDir(treeDir, contextSubdir) {
|
|
805
|
+
const target = resolveWithinDirectory(treeDir, contextSubdir);
|
|
806
|
+
if (!contextSubdir) return target;
|
|
807
|
+
const stats = await stat(target).catch(() => null);
|
|
808
|
+
if (!stats?.isDirectory()) {
|
|
809
|
+
throw new Error(
|
|
810
|
+
`Build context "${contextSubdir}" is not a directory in this source. Check the service's build path.`
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
const [realTree, realTarget] = await Promise.all([realpath(treeDir), realpath(target)]);
|
|
814
|
+
const escape = relative(realTree, realTarget);
|
|
815
|
+
if (escape === ".." || escape.startsWith(`..${sep}`) || isAbsolute(escape)) {
|
|
816
|
+
throw new Error(`Build context "${contextSubdir}" resolves outside the source tree.`);
|
|
817
|
+
}
|
|
818
|
+
return target;
|
|
819
|
+
}
|
|
1012
820
|
async function applyDockerignore(contextDir, config) {
|
|
1013
821
|
const matcher = await loadDockerignoreMatcher(contextDir);
|
|
1014
822
|
if (!matcher) return;
|
|
1015
823
|
const buildFiles = buildFilePaths(config);
|
|
1016
824
|
const prune = async (currentPath) => {
|
|
1017
|
-
const entries = await
|
|
825
|
+
const entries = await readdir(currentPath, { withFileTypes: true });
|
|
1018
826
|
await Promise.all(
|
|
1019
827
|
entries.map(async (entry) => {
|
|
1020
|
-
const absolutePath =
|
|
1021
|
-
const relativePath =
|
|
828
|
+
const absolutePath = join(currentPath, entry.name);
|
|
829
|
+
const relativePath = toPosixPath(relative(contextDir, absolutePath));
|
|
1022
830
|
if (!buildFiles.has(relativePath) && matcher.ignores(relativePath)) {
|
|
1023
831
|
await rm(absolutePath, { recursive: true, force: true });
|
|
1024
832
|
return;
|
|
@@ -1029,10 +837,12 @@ async function applyDockerignore(contextDir, config) {
|
|
|
1029
837
|
};
|
|
1030
838
|
await prune(contextDir);
|
|
1031
839
|
}
|
|
1032
|
-
async function materializeLocalSource(sourcePath, targetPath) {
|
|
1033
|
-
const
|
|
840
|
+
async function materializeLocalSource(sourcePath, targetPath, outputDirectory) {
|
|
841
|
+
const outDir = outputDirectory?.trim().replace(/^\.\//, "").replace(/^\/+|\/+$/g, "");
|
|
842
|
+
const alsoInclude = outDir && outDir !== "." ? [outDir] : void 0;
|
|
843
|
+
const { args, cleanup } = await prepareSourceTarArgs(sourcePath, { alsoInclude });
|
|
1034
844
|
try {
|
|
1035
|
-
await new Promise((
|
|
845
|
+
await new Promise((resolve, reject) => {
|
|
1036
846
|
const create = spawn("tar", args, { env: getTarCreateEnv() });
|
|
1037
847
|
const extract = spawn("tar", ["-xzf", "-", "-C", targetPath]);
|
|
1038
848
|
let err = "";
|
|
@@ -1045,7 +855,7 @@ async function materializeLocalSource(sourcePath, targetPath) {
|
|
|
1045
855
|
create.on("close", (code) => createCode = code);
|
|
1046
856
|
create.stdout.pipe(extract.stdin);
|
|
1047
857
|
extract.on("close", (code) => {
|
|
1048
|
-
if (code === 0 && (createCode ?? 0) === 0) return
|
|
858
|
+
if (code === 0 && (createCode ?? 0) === 0) return resolve();
|
|
1049
859
|
const parts = [
|
|
1050
860
|
createCode ? `pack exited ${createCode}: ${createErr.trim().slice(-500)}` : "",
|
|
1051
861
|
code ? `extract exited ${code}: ${err.trim().slice(-500)}` : ""
|
|
@@ -1057,23 +867,28 @@ async function materializeLocalSource(sourcePath, targetPath) {
|
|
|
1057
867
|
await cleanup();
|
|
1058
868
|
}
|
|
1059
869
|
}
|
|
1060
|
-
async function
|
|
1061
|
-
const candidates = resolveDockerfileCandidates(rootDirectory, explicitDockerfilePath);
|
|
870
|
+
async function firstExistingCandidate(dir, candidates) {
|
|
1062
871
|
for (const candidate of candidates) {
|
|
1063
|
-
const candidatePath =
|
|
1064
|
-
const exists = await
|
|
872
|
+
const candidatePath = join(dir, ...candidate.split("/"));
|
|
873
|
+
const exists = await access(candidatePath).then(() => true).catch(() => false);
|
|
1065
874
|
if (exists) {
|
|
1066
875
|
return candidate;
|
|
1067
876
|
}
|
|
1068
877
|
}
|
|
1069
878
|
return null;
|
|
1070
879
|
}
|
|
880
|
+
async function resolveDockerfileName(contextDir, rootDirectory, explicitDockerfilePath) {
|
|
881
|
+
return firstExistingCandidate(
|
|
882
|
+
contextDir,
|
|
883
|
+
resolveDockerfileCandidates(rootDirectory, explicitDockerfilePath)
|
|
884
|
+
);
|
|
885
|
+
}
|
|
1071
886
|
async function cloneGitSource(config, targetPath, onLog) {
|
|
1072
887
|
let sshMaterial;
|
|
1073
888
|
if (config.gitSsh) {
|
|
1074
889
|
sshMaterial = await materializeGitSsh(
|
|
1075
890
|
localGitSshWriter(),
|
|
1076
|
-
await mkdtemp(
|
|
891
|
+
await mkdtemp(join(tmpdir(), "opsh-ghkey-")),
|
|
1077
892
|
config.gitSsh
|
|
1078
893
|
);
|
|
1079
894
|
}
|
|
@@ -1104,22 +919,24 @@ async function cloneGitSource(config, targetPath, onLog) {
|
|
|
1104
919
|
env: gitEnv
|
|
1105
920
|
});
|
|
1106
921
|
}
|
|
1107
|
-
await rm(
|
|
922
|
+
await rm(join(targetPath, ".git"), { recursive: true, force: true });
|
|
1108
923
|
} finally {
|
|
1109
924
|
await sshMaterial?.cleanup();
|
|
1110
925
|
}
|
|
1111
926
|
}
|
|
1112
927
|
async function prepareSourceTree(config, opts) {
|
|
1113
|
-
const contextDir = await mkdtemp(
|
|
928
|
+
const contextDir = await mkdtemp(join(tmpdir(), "openship-docker-context-"));
|
|
1114
929
|
try {
|
|
930
|
+
const pruneDockerignore = !opts?.perServiceBuildContexts && !dockerBuildContextDirectory(config);
|
|
1115
931
|
if (config.localPath) {
|
|
1116
|
-
await materializeLocalSource(config.localPath, contextDir);
|
|
932
|
+
await materializeLocalSource(config.localPath, contextDir, config.outputDirectory);
|
|
1117
933
|
} else {
|
|
1118
934
|
await cloneGitSource(config, contextDir, opts?.onLog);
|
|
1119
935
|
}
|
|
1120
|
-
await applyDockerignore(contextDir, config);
|
|
936
|
+
if (pruneDockerignore) await applyDockerignore(contextDir, config);
|
|
1121
937
|
return {
|
|
1122
938
|
contextDir,
|
|
939
|
+
dockerignorePruned: pruneDockerignore,
|
|
1123
940
|
cleanup: async () => {
|
|
1124
941
|
await rm(contextDir, { recursive: true, force: true }).catch(() => {
|
|
1125
942
|
});
|
|
@@ -1131,20 +948,42 @@ async function prepareSourceTree(config, opts) {
|
|
|
1131
948
|
throw error;
|
|
1132
949
|
}
|
|
1133
950
|
}
|
|
951
|
+
function missingContextDockerfileMessage(contextSubdir, dockerfilePath, opts) {
|
|
952
|
+
const expected = normalizeDockerRelativePath(dockerfilePath) || "Dockerfile";
|
|
953
|
+
const at = `${contextSubdir}/${expected.startsWith(`${contextSubdir}/`) ? expected.slice(contextSubdir.length + 1) : expected}`;
|
|
954
|
+
return `No Dockerfile found in the build context "${contextSubdir}". Expected ${at}.` + // Names the file the build used to fall back to. Before #634 a build dir with no
|
|
955
|
+
// Dockerfile silently built the REPO ROOT's one under this service's tag, so an
|
|
956
|
+
// upgrade turns those deploys from "green with the wrong image" to red — and
|
|
957
|
+
// without this sentence the red gives no clue why.
|
|
958
|
+
(opts?.rootDockerfileExists ? " The source root has a Dockerfile, but it is outside this service's build context, so it is not used \u2014 set the service's build path to the directory that holds the Dockerfile you want, or add one there." : "") + (opts?.generatedRecipeRefused ? " A declared build context builds from its own Dockerfile \u2014 Openship will not generate one for it, because a generated recipe copies from the source root." : "");
|
|
959
|
+
}
|
|
1134
960
|
async function resolveServiceDockerfile(contextDir, config, opts) {
|
|
1135
961
|
const requireRepositoryDockerfile = opts?.requireRepositoryDockerfile ?? false;
|
|
1136
962
|
const generatedName = opts?.generatedName ?? GENERATED_DOCKERFILE_NAME;
|
|
1137
|
-
const
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
resolvedRootDirectory,
|
|
1145
|
-
config.dockerfilePath
|
|
1146
|
-
);
|
|
963
|
+
const contextSubdir = dockerBuildContextDirectory(config);
|
|
964
|
+
const buildContextDir = await resolveBuildContextDir(contextDir, contextSubdir);
|
|
965
|
+
const resolvedRootDirectory = contextSubdir ? contextSubdir : await resolveDockerRootDirectory(contextDir, config.rootDirectory, config.localPath);
|
|
966
|
+
const repositoryDockerfileName = contextSubdir ? await firstExistingCandidate(
|
|
967
|
+
buildContextDir,
|
|
968
|
+
resolveContextDockerfileCandidates(contextSubdir, config.dockerfilePath)
|
|
969
|
+
) : await resolveDockerfileName(contextDir, resolvedRootDirectory, config.dockerfilePath);
|
|
1147
970
|
const hasRepositoryDockerfile = repositoryDockerfileName !== null;
|
|
971
|
+
if (!hasRepositoryDockerfile && contextSubdir) {
|
|
972
|
+
throw new Error(
|
|
973
|
+
missingContextDockerfileMessage(contextSubdir, config.dockerfilePath, {
|
|
974
|
+
generatedRecipeRefused: !requireRepositoryDockerfile,
|
|
975
|
+
rootDockerfileExists: await access(join(contextDir, "Dockerfile")).then(() => true).catch(() => false)
|
|
976
|
+
})
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
const explicitDockerfile = normalizeDockerRelativePath(config.dockerfilePath);
|
|
980
|
+
if (contextSubdir && explicitDockerfile && repositoryDockerfileName === "Dockerfile" && explicitDockerfile !== "Dockerfile" && explicitDockerfile !== `${contextSubdir}/Dockerfile`) {
|
|
981
|
+
opts?.onLog?.({
|
|
982
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
983
|
+
message: `Configured Dockerfile "${config.dockerfilePath}" was not found in build context "${contextSubdir}" \u2014 using ${contextSubdir}/Dockerfile instead.`,
|
|
984
|
+
level: "warn"
|
|
985
|
+
});
|
|
986
|
+
}
|
|
1148
987
|
if (!hasRepositoryDockerfile && requireRepositoryDockerfile) {
|
|
1149
988
|
const expectedDockerfile = config.dockerfilePath?.trim() || "Dockerfile";
|
|
1150
989
|
throw new Error(
|
|
@@ -1153,7 +992,7 @@ async function resolveServiceDockerfile(contextDir, config, opts) {
|
|
|
1153
992
|
}
|
|
1154
993
|
if (!hasRepositoryDockerfile) {
|
|
1155
994
|
await writeFile(
|
|
1156
|
-
|
|
995
|
+
join(contextDir, generatedName),
|
|
1157
996
|
generateDockerfile({
|
|
1158
997
|
...config,
|
|
1159
998
|
rootDirectory: resolvedRootDirectory
|
|
@@ -1161,26 +1000,34 @@ async function resolveServiceDockerfile(contextDir, config, opts) {
|
|
|
1161
1000
|
"utf-8"
|
|
1162
1001
|
);
|
|
1163
1002
|
}
|
|
1164
|
-
const
|
|
1003
|
+
const dockerfileName = repositoryDockerfileName ?? generatedName;
|
|
1004
|
+
const requiresBuildKit = hasRepositoryDockerfile ? await readFile(join(buildContextDir, ...dockerfileName.split("/")), "utf-8").then(dockerfileNeedsBuildKit).catch(() => false) : false;
|
|
1005
|
+
const ignoreContextPath = opts?.dockerignorePruned === false ? await loadContextIgnore(buildContextDir, contextKeepPaths(dockerfileName)) : void 0;
|
|
1006
|
+
const contextEntries = (await readdir(buildContextDir)).filter(
|
|
1007
|
+
(entry) => !ignoreContextPath?.(entry)
|
|
1008
|
+
);
|
|
1165
1009
|
return {
|
|
1010
|
+
buildContextDir,
|
|
1011
|
+
contextSubdir,
|
|
1166
1012
|
contextEntries,
|
|
1167
|
-
|
|
1013
|
+
ignoreContextPath,
|
|
1014
|
+
dockerfileName,
|
|
1168
1015
|
rootDirectory: resolvedRootDirectory,
|
|
1169
|
-
usesRepositoryDockerfile: hasRepositoryDockerfile
|
|
1016
|
+
usesRepositoryDockerfile: hasRepositoryDockerfile,
|
|
1017
|
+
requiresBuildKit
|
|
1170
1018
|
};
|
|
1171
1019
|
}
|
|
1172
1020
|
async function createDockerBuildContext(config, opts) {
|
|
1173
1021
|
const tree = await prepareSourceTree(config, { onLog: opts?.onLog });
|
|
1174
1022
|
try {
|
|
1175
1023
|
const resolved = await resolveServiceDockerfile(tree.contextDir, config, {
|
|
1176
|
-
requireRepositoryDockerfile: opts?.requireRepositoryDockerfile
|
|
1024
|
+
requireRepositoryDockerfile: opts?.requireRepositoryDockerfile,
|
|
1025
|
+
dockerignorePruned: tree.dockerignorePruned,
|
|
1026
|
+
onLog: opts?.onLog
|
|
1177
1027
|
});
|
|
1178
1028
|
return {
|
|
1029
|
+
...resolved,
|
|
1179
1030
|
contextDir: tree.contextDir,
|
|
1180
|
-
contextEntries: resolved.contextEntries,
|
|
1181
|
-
dockerfileName: resolved.dockerfileName,
|
|
1182
|
-
rootDirectory: resolved.rootDirectory,
|
|
1183
|
-
usesRepositoryDockerfile: resolved.usesRepositoryDockerfile,
|
|
1184
1031
|
cleanup: tree.cleanup
|
|
1185
1032
|
};
|
|
1186
1033
|
} catch (error) {
|
|
@@ -1190,12 +1037,9 @@ async function createDockerBuildContext(config, opts) {
|
|
|
1190
1037
|
}
|
|
1191
1038
|
|
|
1192
1039
|
export {
|
|
1193
|
-
|
|
1194
|
-
resolveWithinDirectory,
|
|
1195
|
-
resolveDockerfileCandidates,
|
|
1196
|
-
staticBuilderOutputPath,
|
|
1197
|
-
generateDockerfile,
|
|
1040
|
+
BuildKitTraceDecoder,
|
|
1198
1041
|
prepareSourceTree,
|
|
1042
|
+
missingContextDockerfileMessage,
|
|
1199
1043
|
resolveServiceDockerfile,
|
|
1200
1044
|
createDockerBuildContext
|
|
1201
1045
|
};
|