-
Notifications
You must be signed in to change notification settings - Fork 28
Expand file tree
/
Copy pathbuildWithBuildx.ts
More file actions
121 lines (111 loc) · 3.7 KB
/
buildWithBuildx.ts
File metadata and controls
121 lines (111 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { ListrTask } from "listr";
import semver from "semver";
import { shell } from "../utils/shell";
import { Architecture, PackageImage, PackageImageLocal } from "../types";
import { saveAndCompressImagesCached } from "./saveAndCompressImages";
import { getDocker } from "../utils/docker";
const minimumDockerVersion = "19.03.0";
const buildxInstanceName = "dappnode-multiarch-builder";
/**
* Save docker image
* This step is extremely expensive computationally.
* A local cache file will prevent unnecessary compressions if the image hasn't changed
*/
export function buildWithBuildx({
architecture,
images,
composePath,
destPath,
buildTimeout,
skipSave
}: {
architecture: Architecture;
images: PackageImage[];
composePath: string;
destPath: string;
buildTimeout: number;
skipSave?: boolean;
}): ListrTask[] {
const docker = getDocker();
const localImages = images.filter(
(image): image is PackageImageLocal => image.type === "local"
);
return [
{
title: "Build docker image",
enabled: () => localImages.length > 0,
task: async (_, task) => {
// Must enable this flag for buildx to work on all environments
process.env.DOCKER_CLI_EXPERIMENTAL = "enabled";
// Make sure `docker version` is >= 19.03
const dockerInfo = await docker.version().catch(e => {
throw Error(`docker is not installed: ${e.message}`);
});
const dockerVersion = dockerInfo.Version;
if (
semver.valid(dockerVersion) &&
semver.lt(dockerVersion, minimumDockerVersion)
)
throw Error(
`docker version must be at least ${minimumDockerVersion} to use buildx, current version ${dockerVersion}`
);
switch (architecture) {
case "linux/arm64":
await shell(
`docker run --rm --privileged docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64`
);
// Make sure QEMU is enabled
// `cat /proc/sys/fs/binfmt_misc/qemu-aarch64`
break;
}
const currentInstances = await shell(`docker buildx ls`);
if (currentInstances.includes(buildxInstanceName)) {
await shell(`docker buildx use ${buildxInstanceName}`);
} else {
await shell(
`docker buildx create --name ${buildxInstanceName} --use`
);
}
// Will build all services defined in the compose in paralel
// The resulting images will be imported to docker with the tag declared in the service
await shell(
[
"docker buildx bake",
"--progress plain",
"--load",
`--file ${composePath}`,
`--set=*.platform=${architecture}`
].join(" "),
{
timeout: buildTimeout,
maxBuffer: 100 * 1e6,
onData: data => (task.output = data)
}
);
const firstImage = images.find(image => image.type === "local");
if (firstImage) {
const firstImageTag = firstImage.imageTag;
// Make sure the built was done for the correct architecture
switch (architecture) {
case "linux/arm64": {
const res = await shell(
`docker run --rm --entrypoint="" ${firstImageTag} uname -m`
);
if (res !== "aarch64")
throw Error(`Unexpected resulting architecture: ${res}`);
else task.output = `Validated ${firstImageTag} architecture`;
break;
}
}
}
}
},
...saveAndCompressImagesCached({
images,
architecture,
destPath,
buildTimeout,
skipSave
})
];
}