containerify 3.2.0 → 3.3.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/README.md +35 -36
- package/lib/appLayerCreator.d.ts +2 -2
- package/lib/appLayerCreator.js +9 -2
- package/lib/cli.js +63 -84
- package/lib/dockerExporter.js +1 -1
- package/lib/httpRequest.js +0 -1
- package/lib/logger.js +0 -1
- package/lib/registry.js +1 -1
- package/lib/types.d.ts +2 -0
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +18 -15
package/README.md
CHANGED
|
@@ -53,42 +53,41 @@ This will take the `nginx:alpine` image, and copy the files from `./dist/` into
|
|
|
53
53
|
Usage: containerify [options]
|
|
54
54
|
|
|
55
55
|
Options:
|
|
56
|
-
--from <registry/image:tag>
|
|
57
|
-
--to <registry/image:tag>
|
|
58
|
-
--fromImage <name:tag>
|
|
59
|
-
--toImage <name:tag>
|
|
60
|
-
--folder <full path>
|
|
61
|
-
--file <path>
|
|
62
|
-
--doCrossMount
|
|
63
|
-
--fromRegistry <registry url>
|
|
64
|
-
--fromToken <token>
|
|
65
|
-
--toRegistry <registry url>
|
|
66
|
-
--optimisticToRegistryCheck
|
|
67
|
-
--toToken <token>
|
|
68
|
-
--toTar <path>
|
|
69
|
-
--toDocker
|
|
70
|
-
--registry <path>
|
|
71
|
-
--platform <platform>
|
|
72
|
-
--token <path>
|
|
73
|
-
--user <user>
|
|
74
|
-
--workdir <directory>
|
|
75
|
-
--entrypoint <entrypoint>
|
|
76
|
-
--labels <labels
|
|
77
|
-
--
|
|
78
|
-
--
|
|
79
|
-
--
|
|
80
|
-
--
|
|
81
|
-
--
|
|
82
|
-
--
|
|
83
|
-
--
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
--
|
|
87
|
-
--
|
|
88
|
-
--
|
|
89
|
-
--
|
|
90
|
-
--
|
|
91
|
-
-h, --help display help for command
|
|
56
|
+
--from <registry/image:tag> Optional: Shorthand to specify fromRegistry and fromImage in one argument
|
|
57
|
+
--to <registry/image:tag> Optional: Shorthand to specify toRegistry and toImage in one argument
|
|
58
|
+
--fromImage <name:tag> Required: Image name of base image - [path/]image:tag
|
|
59
|
+
--toImage <name:tag> Required: Image name of target image - [path/]image:tag
|
|
60
|
+
--folder <full path> Required: Base folder of node application (contains package.json)
|
|
61
|
+
--file <path> Optional: Name of configuration file (defaults to containerify.json if found on path)
|
|
62
|
+
--doCrossMount Optional: Cross mount image layers from the base image (only works if fromImage and toImage are in the same registry) (default: false)
|
|
63
|
+
--fromRegistry <registry url> Optional: URL of registry to pull base image from - Default: https://registry-1.docker.io/v2/
|
|
64
|
+
--fromToken <token> Optional: Authentication token for from registry
|
|
65
|
+
--toRegistry <registry url> Optional: URL of registry to push base image to - Default: https://registry-1.docker.io/v2/
|
|
66
|
+
--optimisticToRegistryCheck Treat redirects as layer existing in remote registry. Potentially unsafe, but can save bandwidth.
|
|
67
|
+
--toToken <token> Optional: Authentication token for target registry
|
|
68
|
+
--toTar <path> Optional: Export to tar file
|
|
69
|
+
--toDocker Optional: Export to local docker registry
|
|
70
|
+
--registry <path> Optional: Convenience argument for setting both from and to registry
|
|
71
|
+
--platform <platform> Optional: Preferred platform, e.g. linux/amd64 or arm64
|
|
72
|
+
--token <path> Optional: Convenience argument for setting token for both from and to registry
|
|
73
|
+
--user <user> Optional: User account to run process in container - default: 1000 (empty for customContent)
|
|
74
|
+
--workdir <directory> Optional: Workdir where node app will be added and run from - default: /app (empty for customContent)
|
|
75
|
+
--entrypoint <entrypoint> Optional: Entrypoint when starting container - default: npm start (empty for customContent)
|
|
76
|
+
--label, --labels <labels...> Optional: Comma-separated list of key value pairs to use as labels
|
|
77
|
+
--env, --envs <envs...> Optional: Comma-separated list of key value pairs to use av environment variables.
|
|
78
|
+
--setTimeStamp <timestamp> Optional: Set a specific ISO 8601 timestamp on all entries (e.g. git commit hash). Default: 1970 in tar files, and current time on manifest/config
|
|
79
|
+
--preserveTimeStamp Optional: Preserve timestamps on files in the added layers. This might help with cache invalidation.
|
|
80
|
+
--verbose Verbose logging
|
|
81
|
+
--allowInsecureRegistries Allow insecure registries (with self-signed/untrusted cert)
|
|
82
|
+
--allowNoPushAuth Allow pushing images without authentication/token if the registry allows it
|
|
83
|
+
--customContent <dirs/files...> Optional: Skip normal node_modules and applayer and include specified root folder files/directories instead. You can specify as
|
|
84
|
+
local-path:absolute-container-path if you want to place it in a specific location
|
|
85
|
+
--extraContent <dirs/files...> Optional: Add specific content. Specify as local-path:absolute-container-path,local-path2:absolute-container-path2 etc
|
|
86
|
+
--layerOwner <gid:uid> Optional: Set specific gid and uid on files in the added layers
|
|
87
|
+
--buildFolder <path> Optional: Use a specific build folder when creating the image
|
|
88
|
+
--layerCacheFolder <path> Optional: Folder to cache base layers between builds
|
|
89
|
+
--version Get containerify version
|
|
90
|
+
-h, --help display help for command
|
|
92
91
|
```
|
|
93
92
|
|
|
94
93
|
## Detailed info
|
package/lib/appLayerCreator.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Options } from "./types";
|
|
2
|
-
declare function addLayers(tmpdir: string, fromdir: string, todir: string, options: Options): Promise<
|
|
1
|
+
import { ManifestDescriptor, Options } from "./types";
|
|
2
|
+
declare function addLayers(tmpdir: string, fromdir: string, todir: string, options: Options): Promise<ManifestDescriptor>;
|
|
3
3
|
declare const _default: {
|
|
4
4
|
addLayers: typeof addLayers;
|
|
5
5
|
};
|
package/lib/appLayerCreator.js
CHANGED
|
@@ -78,7 +78,7 @@ function calculateHash(path) {
|
|
|
78
78
|
function copySync(src, dest, preserveTimestamps) {
|
|
79
79
|
const copyOptions = { overwrite: true, dereference: true, preserveTimestamps: preserveTimestamps };
|
|
80
80
|
const destFolder = dest.substring(0, dest.lastIndexOf("/"));
|
|
81
|
-
logger_1.default.debug(
|
|
81
|
+
logger_1.default.debug(`Copying ${src} to ${dest}`);
|
|
82
82
|
fse.ensureDirSync(destFolder);
|
|
83
83
|
fse.copySync(src, dest, copyOptions);
|
|
84
84
|
}
|
|
@@ -253,7 +253,14 @@ function addLayers(tmpdir, fromdir, todir, options) {
|
|
|
253
253
|
yield fs_1.promises.writeFile(configFile, configContent);
|
|
254
254
|
manifest.config.digest = "sha256:" + configHash;
|
|
255
255
|
manifest.config.size = yield fileutil.sizeOf(configFile);
|
|
256
|
-
|
|
256
|
+
const manifestJson = JSON.stringify(manifest);
|
|
257
|
+
yield fs_1.promises.writeFile(path.join(todir, "manifest.json"), manifestJson);
|
|
258
|
+
const manifestBuffer = Buffer.from(manifestJson, "utf-8");
|
|
259
|
+
return {
|
|
260
|
+
mediaType: manifest.mediaType,
|
|
261
|
+
digest: calculateHashOfBuffer(manifestBuffer),
|
|
262
|
+
size: manifestBuffer.byteLength,
|
|
263
|
+
};
|
|
257
264
|
});
|
|
258
265
|
}
|
|
259
266
|
exports.default = {
|
package/lib/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
10
|
});
|
|
11
11
|
};
|
|
12
|
-
var _a, _b, _c, _d, _e, _f, _g, _h
|
|
12
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
14
|
const os = require("os");
|
|
15
15
|
const commander_1 = require("commander");
|
|
@@ -25,68 +25,52 @@ const types_1 = require("./types");
|
|
|
25
25
|
const utils_1 = require("./utils");
|
|
26
26
|
const fileutil_1 = require("./fileutil");
|
|
27
27
|
const version_1 = require("./version");
|
|
28
|
-
const
|
|
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
|
-
|
|
28
|
+
const program = new commander_1.Command();
|
|
29
|
+
program
|
|
30
|
+
.name("containerify")
|
|
31
|
+
.description("A CLI-tool for creating container images.")
|
|
32
|
+
.option("--from <registry/image:tag>", "Optional: Shorthand to specify fromRegistry and fromImage in one argument")
|
|
33
|
+
.option("--to <registry/image:tag>", "Optional: Shorthand to specify toRegistry and toImage in one argument")
|
|
34
|
+
.option("--fromImage <name:tag>", "Required: Image name of base image - [path/]image:tag")
|
|
35
|
+
.option("--toImage <name:tag>", "Required: Image name of target image - [path/]image:tag")
|
|
36
|
+
.option("--folder <full path>", "Required: Base folder of node application (contains package.json)")
|
|
37
|
+
.option("--file <path>", "Optional: Name of configuration file (defaults to containerify.json if found on path)")
|
|
38
|
+
.option("--doCrossMount", "Optional: Cross mount image layers from the base image (only works if fromImage and toImage are in the same registry) (default, false)")
|
|
39
|
+
.option("--fromRegistry <registry url>", "Optional: URL of registry to pull base image from - Default, https,//registry-1.docker.io/v2/")
|
|
40
|
+
.option("--fromToken <token>", "Optional: Authentication token for from registry")
|
|
41
|
+
.option("--toRegistry <registry url>", "Optional: URL of registry to push base image to - Default, https,//registry-1.docker.io/v2/")
|
|
42
|
+
.option("--optimisticToRegistryCheck", "Treat redirects as layer existing in remote registry. Potentially unsafe) but can save bandwidth.")
|
|
43
|
+
.option("--toToken <token>", "Optional: Authentication token for target registry")
|
|
44
|
+
.option("--toTar <path>", "Optional: Export to tar file")
|
|
45
|
+
.option("--toDocker", "Optional: Export to local docker registry")
|
|
46
|
+
.option("--registry <path>", "Optional: Convenience argument for setting both from and to registry")
|
|
47
|
+
.option("--platform <platform>", "Optional: Preferred platform) e.g. linux/amd64 or arm64")
|
|
48
|
+
.option("--token <path>", "Optional: Convenience argument for setting token for both from and to registry")
|
|
49
|
+
.option("--user <user>", "Optional: User account to run process in container - default, 1000 (empty for customContent)")
|
|
50
|
+
.option("--workdir <directory>", "Optional: Workdir where node app will be added and run from - default, /app (empty for customContent)")
|
|
51
|
+
.option("--entrypoint <entrypoint>", "Optional: Entrypoint when starting container - default, npm start (empty for customContent)")
|
|
52
|
+
.option("--label, --labels <labels...>", "Optional: Comma-separated list of key value pairs to use as labels")
|
|
53
|
+
.option("--env, --envs <envs...>", "Optional: Comma-separated list of key value pairs to use av environment variables.")
|
|
54
|
+
.option("--preserveTimeStamp", "Optional: Preserve timestamps on files in the added layers. This might help with cache invalidation.")
|
|
55
|
+
.option("--setTimeStamp <timestamp>", "Optional: Set a specific ISO 8601 timestamp on all entries (e.g. git commit hash). Default, 1970 in tar files) and current time on manifest/config")
|
|
56
|
+
.option("--verbose", "Verbose logging")
|
|
57
|
+
.option("--allowInsecureRegistries", "Allow insecure registries (with self-signed/untrusted cert)")
|
|
58
|
+
.option("--allowNoPushAuth", "Allow pushing images without a authentication/token to registries that allow it")
|
|
59
|
+
.option("--customContent <dirs/files...>", "Optional: Skip normal node_modules and applayer and include specified root folder files/directories instead. You can specify as local-path,absolute-container-path if you want to place it in a specific location")
|
|
60
|
+
.option("--extraContent <dirs/files...>", "Optional: Add specific content. Specify as local-path,absolute-container-path)local-path2,absolute-container-path2 etc")
|
|
61
|
+
.option("--layerOwner <gid:uid>", "Optional: Set specific gid and uid on files in the added layers")
|
|
62
|
+
.option("--buildFolder <path>", "Optional: Use a specific build folder when creating the image")
|
|
63
|
+
.option("--layerCacheFolder <path>", "Optional: Folder to cache base layers between builds")
|
|
64
|
+
.option("--writeDigestTo <path>", "Optional: Write the resulting image digest to the file path provided")
|
|
65
|
+
.version(version_1.VERSION, "--version", "Get containerify version");
|
|
66
|
+
program.parse(process.argv);
|
|
65
67
|
function setKeyValue(target, keyValue, separator = "=", defaultValue) {
|
|
66
68
|
var _a;
|
|
67
69
|
const [k, v] = keyValue.split(separator, 2);
|
|
68
70
|
target[k.trim()] = (_a = v === null || v === void 0 ? void 0 : v.trim()) !== null && _a !== void 0 ? _a : defaultValue;
|
|
69
71
|
}
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
setKeyValue(cliLabels, ops);
|
|
73
|
-
});
|
|
74
|
-
const cliEnv = {};
|
|
75
|
-
commander_1.program.on("option:env", (ops) => {
|
|
76
|
-
setKeyValue(cliEnv, ops);
|
|
77
|
-
});
|
|
78
|
-
const cliOptions = Object.entries(possibleArgs)
|
|
79
|
-
.reduce((program, [k, v]) => {
|
|
80
|
-
program.option(k, v);
|
|
81
|
-
return program;
|
|
82
|
-
}, commander_1.program)
|
|
83
|
-
.parse()
|
|
84
|
-
.opts();
|
|
85
|
-
if (cliOptions.version) {
|
|
86
|
-
console.log(`containerify v${version_1.VERSION}`);
|
|
87
|
-
process.exit(0);
|
|
88
|
-
}
|
|
89
|
-
const keys = Object.keys(possibleArgs).map((k) => k.split(" ")[0].replace("--", ""));
|
|
72
|
+
const cliOptions = program.opts();
|
|
73
|
+
const keys = program.options.map((x) => { var _a; return (_a = x.long) === null || _a === void 0 ? void 0 : _a.replace("--", ""); });
|
|
90
74
|
const defaultOptions = {
|
|
91
75
|
workdir: "/app",
|
|
92
76
|
user: "1000",
|
|
@@ -108,26 +92,16 @@ Object.keys(configFromFile).forEach((k) => {
|
|
|
108
92
|
}
|
|
109
93
|
});
|
|
110
94
|
const labelsOpt = {};
|
|
111
|
-
(
|
|
112
|
-
Object.
|
|
113
|
-
.filter((l) => Object.keys(cliLabels).includes(l))
|
|
114
|
-
.forEach((l) => {
|
|
115
|
-
exitWithErrorIf(true, `Label ${l} specified both with --labels and --label`);
|
|
116
|
-
});
|
|
117
|
-
const labels = Object.assign(Object.assign(Object.assign({}, configFromFile.labels), labelsOpt), cliLabels); //Let cli arguments override file
|
|
95
|
+
(_a = cliOptions.labels) === null || _a === void 0 ? void 0 : _a.forEach((labels) => labels.split(",").forEach((label) => setKeyValue(labelsOpt, label)));
|
|
96
|
+
const labels = Object.assign(Object.assign({}, configFromFile.labels), labelsOpt); //Let cli arguments override file
|
|
118
97
|
const envOpt = {};
|
|
119
|
-
(
|
|
120
|
-
Object.
|
|
121
|
-
.filter((l) => Object.keys(cliEnv).includes(l))
|
|
122
|
-
.forEach((l) => {
|
|
123
|
-
exitWithErrorIf(true, `Env ${l} specified both with --envs and --env`);
|
|
124
|
-
});
|
|
125
|
-
const envs = Object.assign(Object.assign(Object.assign({}, configFromFile.envs), envOpt), cliEnv); //Let cli arguments override file
|
|
98
|
+
(_b = cliOptions.envs) === null || _b === void 0 ? void 0 : _b.forEach((envs) => envs.split(",").forEach((env) => setKeyValue(envOpt, env)));
|
|
99
|
+
const envs = Object.assign(Object.assign({}, configFromFile.envs), envOpt); //Let cli arguments override file
|
|
126
100
|
const customContent = {};
|
|
127
|
-
(
|
|
128
|
-
(
|
|
101
|
+
(_c = configFromFile.customContent) === null || _c === void 0 ? void 0 : _c.forEach((c) => setKeyValue(customContent, c, ":", c));
|
|
102
|
+
(_d = cliOptions.customContent) === null || _d === void 0 ? void 0 : _d.forEach((contents) => contents.split(",").forEach((content) => setKeyValue(customContent, content, ":", content)));
|
|
129
103
|
const cliExtraContent = {};
|
|
130
|
-
(
|
|
104
|
+
(_e = cliOptions.extraContent) === null || _e === void 0 ? void 0 : _e.forEach((extras) => extras.split(",").forEach((extra) => setKeyValue(cliExtraContent, extra, ":")));
|
|
131
105
|
const extraContent = Object.assign(Object.assign({}, configFromFile.extraContent), cliExtraContent);
|
|
132
106
|
const cliParams = (0, utils_1.omit)(cliOptions, [
|
|
133
107
|
"label",
|
|
@@ -144,15 +118,16 @@ const options = Object.assign(Object.assign(Object.assign({}, defaultOptions), s
|
|
|
144
118
|
user: setOptions.user,
|
|
145
119
|
workdir: setOptions.workdir,
|
|
146
120
|
entrypoint: setOptions.entrypoint,
|
|
147
|
-
} });
|
|
121
|
+
}, writeDigestTo: cliOptions.writeDigestTo });
|
|
148
122
|
function exitWithErrorIf(check, error) {
|
|
149
123
|
if (check) {
|
|
150
124
|
logger_1.default.error("ERROR: " + error);
|
|
151
|
-
|
|
125
|
+
program.help({ error: true });
|
|
152
126
|
}
|
|
153
127
|
}
|
|
154
128
|
if (options.verbose)
|
|
155
129
|
logger_1.default.enableDebug();
|
|
130
|
+
exitWithErrorIf(!!options.setTimeStamp && !!options.preserveTimeStamp, "Do not set both --preserveTimeStamp and --setTimeStamp");
|
|
156
131
|
exitWithErrorIf(!!options.registry && !!options.fromRegistry, "Do not set both --registry and --fromRegistry");
|
|
157
132
|
exitWithErrorIf(!!options.from && !!options.fromRegistry, "Do not set both --from and --fromRegistry");
|
|
158
133
|
exitWithErrorIf(!!options.registry && !!options.from, "Do not set both --registry and --from");
|
|
@@ -203,11 +178,11 @@ if (options.toRegistry && !options.toRegistry.endsWith("/"))
|
|
|
203
178
|
options.toRegistry += "/";
|
|
204
179
|
if (options.fromRegistry && !options.fromRegistry.endsWith("/"))
|
|
205
180
|
options.fromRegistry += "/";
|
|
206
|
-
if (!options.fromRegistry && !((
|
|
181
|
+
if (!options.fromRegistry && !((_h = (_g = (_f = options.fromImage) === null || _f === void 0 ? void 0 : _f.split(":")) === null || _g === void 0 ? void 0 : _g[0]) === null || _h === void 0 ? void 0 : _h.includes("/"))) {
|
|
207
182
|
options.fromImage = "library/" + options.fromImage;
|
|
208
183
|
}
|
|
209
184
|
Object.keys(options.customContent).forEach((p) => {
|
|
210
|
-
exitWithErrorIf(!fs.existsSync(p),
|
|
185
|
+
exitWithErrorIf(!fs.existsSync(p), `Could not find ${p} in the base folder ${options.folder}`);
|
|
211
186
|
});
|
|
212
187
|
if (options.layerCacheFolder) {
|
|
213
188
|
if (!fs.existsSync(options.layerCacheFolder)) {
|
|
@@ -216,7 +191,7 @@ if (options.layerCacheFolder) {
|
|
|
216
191
|
fs.mkdirSync(options.layerCacheFolder, { recursive: true });
|
|
217
192
|
}
|
|
218
193
|
catch (e) {
|
|
219
|
-
exitWithErrorIf(true,
|
|
194
|
+
exitWithErrorIf(true, `Failed to create layer cache folder ${e}`);
|
|
220
195
|
}
|
|
221
196
|
}
|
|
222
197
|
if (!options.layerCacheFolder.endsWith("/")) {
|
|
@@ -224,13 +199,13 @@ if (options.layerCacheFolder) {
|
|
|
224
199
|
}
|
|
225
200
|
}
|
|
226
201
|
Object.keys(options.extraContent).forEach((k) => {
|
|
227
|
-
exitWithErrorIf(!fs.existsSync(options.folder + k),
|
|
202
|
+
exitWithErrorIf(!fs.existsSync(options.folder + k), `Could not find '${k}' in the folder ${options.folder}`);
|
|
228
203
|
});
|
|
229
204
|
function run(options) {
|
|
230
205
|
return __awaiter(this, void 0, void 0, function* () {
|
|
231
206
|
var _a;
|
|
232
207
|
if (!(yield fse.pathExists(options.folder)))
|
|
233
|
-
throw new Error(
|
|
208
|
+
throw new Error(`No such folder: ${options.folder}`);
|
|
234
209
|
const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "containerify-"));
|
|
235
210
|
logger_1.default.debug("Using " + tmpdir);
|
|
236
211
|
const fromdir = yield (0, fileutil_1.ensureEmptyDir)(path.join(tmpdir, "from"));
|
|
@@ -239,7 +214,7 @@ function run(options) {
|
|
|
239
214
|
const fromRegistryUrl = (_a = options.fromRegistry) !== null && _a !== void 0 ? _a : registry_1.DEFAULT_DOCKER_REGISTRY;
|
|
240
215
|
const fromRegistry = yield (0, registry_1.createRegistry)(fromRegistryUrl, options.fromImage, allowInsecure, options.fromToken);
|
|
241
216
|
const originalManifest = yield fromRegistry.download(options.fromImage, fromdir, (0, utils_1.getPreferredPlatform)(options.platform), options.layerCacheFolder);
|
|
242
|
-
yield appLayerCreator_1.default.addLayers(tmpdir, fromdir, todir, options);
|
|
217
|
+
const manifestDescriptor = yield appLayerCreator_1.default.addLayers(tmpdir, fromdir, todir, options);
|
|
243
218
|
if (options.toDocker) {
|
|
244
219
|
if (!(yield dockerExporter_1.default.isAvailable())) {
|
|
245
220
|
throw new Error("Docker executable not found on path. Unable to export to local docker registry.");
|
|
@@ -255,9 +230,13 @@ function run(options) {
|
|
|
255
230
|
const toRegistry = yield (0, registry_1.createRegistry)(options.toRegistry, options.toImage, allowInsecure, options.toToken, options.optimisticToRegistryCheck);
|
|
256
231
|
yield toRegistry.upload(options.toImage, todir, options.doCrossMount, originalManifest, options.fromImage);
|
|
257
232
|
}
|
|
258
|
-
logger_1.default.debug(
|
|
233
|
+
logger_1.default.debug(`Deleting ${tmpdir} ...`);
|
|
259
234
|
yield fse.remove(tmpdir);
|
|
260
235
|
logger_1.default.debug("Done");
|
|
236
|
+
if (options.writeDigestTo) {
|
|
237
|
+
logger_1.default.debug(`Writing digest ${manifestDescriptor.digest} to ${options.writeDigestTo}`);
|
|
238
|
+
fs.writeFileSync(options.writeDigestTo, manifestDescriptor.digest);
|
|
239
|
+
}
|
|
261
240
|
});
|
|
262
241
|
}
|
|
263
242
|
logger_1.default.debug("Running with config:", options);
|
package/lib/dockerExporter.js
CHANGED
package/lib/httpRequest.js
CHANGED
|
@@ -101,7 +101,6 @@ function followRedirects(uri, headers, allowInsecure, cb, count = 0) {
|
|
|
101
101
|
if (count > 10)
|
|
102
102
|
return cb({ error: "Too many redirects for " + uri });
|
|
103
103
|
const location = res.headers.location;
|
|
104
|
-
console.log(res.headers);
|
|
105
104
|
if (!location)
|
|
106
105
|
return cb({ error: "Redirect, but missing location header" });
|
|
107
106
|
return followRedirects(location, headers, allowInsecure, cb, count + 1);
|
package/lib/logger.js
CHANGED
package/lib/registry.js
CHANGED
|
@@ -130,7 +130,7 @@ function uploadContent(uploadUrl, file, fileConfig, allowInsecure, auth, content
|
|
|
130
130
|
}
|
|
131
131
|
function processToken(registryBaseUrl, allowInsecure, imagePath, token) {
|
|
132
132
|
return __awaiter(this, void 0, void 0, function* () {
|
|
133
|
-
const { hostname } = URL.
|
|
133
|
+
const { hostname } = new URL.URL(registryBaseUrl);
|
|
134
134
|
const image = (0, utils_1.parseImage)(imagePath);
|
|
135
135
|
if ((hostname === null || hostname === void 0 ? void 0 : hostname.endsWith(".docker.io")) && !token)
|
|
136
136
|
return getDockerToken(image.path, allowInsecure);
|
package/lib/types.d.ts
CHANGED
|
@@ -91,6 +91,7 @@ export type Options = {
|
|
|
91
91
|
workdir?: string;
|
|
92
92
|
entrypoint?: string;
|
|
93
93
|
};
|
|
94
|
+
writeDigestTo?: string;
|
|
94
95
|
};
|
|
95
96
|
export declare enum InsecureRegistrySupport {
|
|
96
97
|
NO = 0,
|
|
@@ -101,4 +102,5 @@ export type Registry = {
|
|
|
101
102
|
upload: (imageStr: string, folder: string, doCrossMount: boolean, originalManifest: Manifest, originalRepository: string) => Promise<void>;
|
|
102
103
|
registryBaseUrl: string;
|
|
103
104
|
};
|
|
105
|
+
export type ManifestDescriptor = Descriptor;
|
|
104
106
|
export {};
|
package/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "3.
|
|
1
|
+
export declare const VERSION = "3.3.1";
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "containerify",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.1",
|
|
4
4
|
"description": "Build node.js docker images without docker",
|
|
5
5
|
"main": "./lib/cli.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"prebuild": "node -p \"'export const VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts",
|
|
8
8
|
"build": "tsc && chmod ugo+x lib/cli.js",
|
|
9
|
-
"lint": "eslint . --ext .ts --fix
|
|
9
|
+
"lint": "eslint . --ext .ts --fix",
|
|
10
10
|
"typecheck": "tsc --noEmit",
|
|
11
11
|
"watch": "tsc --watch",
|
|
12
12
|
"check": "npm run lint && npm run typecheck",
|
|
@@ -36,20 +36,23 @@
|
|
|
36
36
|
"image"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"commander": "^
|
|
40
|
-
"fs-extra": "^11.
|
|
41
|
-
"tar": "^6.1
|
|
39
|
+
"commander": "^13.1.0",
|
|
40
|
+
"fs-extra": "^11.3.0",
|
|
41
|
+
"tar": "^6.2.1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@
|
|
45
|
-
"@
|
|
46
|
-
"@types/
|
|
47
|
-
"@types/
|
|
48
|
-
"@
|
|
49
|
-
"@
|
|
50
|
-
"eslint": "^8.
|
|
51
|
-
"eslint
|
|
52
|
-
"
|
|
53
|
-
"
|
|
44
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
45
|
+
"@eslint/js": "^9.23.0",
|
|
46
|
+
"@types/fs-extra": "^11.0.4",
|
|
47
|
+
"@types/minizlib": "^2.1.7",
|
|
48
|
+
"@types/node": "^22.13.9",
|
|
49
|
+
"@types/tar": "^6.1.13",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^8.26.0",
|
|
51
|
+
"@typescript-eslint/parser": "^8.26",
|
|
52
|
+
"eslint": "^9.21.0",
|
|
53
|
+
"eslint-config-prettier": "^10.1.1",
|
|
54
|
+
"globals": "^16.0.0",
|
|
55
|
+
"prettier": "^3.5.3",
|
|
56
|
+
"typescript": "^5.8.2"
|
|
54
57
|
}
|
|
55
58
|
}
|