containerify 3.1.1 → 3.2.0
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/lib/appLayerCreator.js +5 -5
- package/lib/cli.js +2 -1
- package/lib/fileutil.js +2 -3
- package/lib/httpRequest.d.ts +0 -3
- package/lib/httpRequest.js +25 -12
- package/lib/registry.js +10 -9
- package/lib/types.d.ts +1 -0
- package/lib/utils.js +7 -8
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +3 -3
package/lib/appLayerCreator.js
CHANGED
|
@@ -75,8 +75,8 @@ function calculateHash(path) {
|
|
|
75
75
|
stream.on("end", () => resolve(hash.digest("hex")));
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
|
-
function copySync(src, dest) {
|
|
79
|
-
const copyOptions = { overwrite: true, dereference: true };
|
|
78
|
+
function copySync(src, dest, preserveTimestamps) {
|
|
79
|
+
const copyOptions = { overwrite: true, dereference: true, preserveTimestamps: preserveTimestamps };
|
|
80
80
|
const destFolder = dest.substring(0, dest.lastIndexOf("/"));
|
|
81
81
|
logger_1.default.debug("Copying " + src + " to " + dest);
|
|
82
82
|
fse.ensureDirSync(destFolder);
|
|
@@ -112,10 +112,10 @@ function addDataLayer(tmpdir, todir, options, config, manifest, files, comment)
|
|
|
112
112
|
const buildDir = yield fileutil.ensureEmptyDir(path.join(tmpdir, "build"));
|
|
113
113
|
files.map((f) => {
|
|
114
114
|
if (Array.isArray(f)) {
|
|
115
|
-
copySync(path.join(options.folder, f[0]), path.join(buildDir, f[1]));
|
|
115
|
+
copySync(path.join(options.folder, f[0]), path.join(buildDir, f[1]), !!options.preserveTimeStamp);
|
|
116
116
|
}
|
|
117
117
|
else {
|
|
118
|
-
copySync(path.join(options.folder, f), path.join(buildDir, options.workdir, f));
|
|
118
|
+
copySync(path.join(options.folder, f), path.join(buildDir, options.workdir, f), !!options.preserveTimeStamp);
|
|
119
119
|
}
|
|
120
120
|
});
|
|
121
121
|
const layerFile = path.join(todir, "layer.tar.gz");
|
|
@@ -127,7 +127,7 @@ function addDataLayer(tmpdir, todir, options, config, manifest, files, comment)
|
|
|
127
127
|
comment +
|
|
128
128
|
(comment == "dependencies" ? ". Did you forget to run npm install?" : ""));
|
|
129
129
|
}
|
|
130
|
-
yield tar.create(Object.assign(Object.assign({}, tarDefaultConfig), Object.assign({ statCache: statCache(options.layerOwner), portable: !options.layerOwner, prefix: "/", cwd: buildDir, file: layerFile, gzip: true, noMtime: !options.setTimeStamp }, (options.setTimeStamp ? { mtime: new Date(options.setTimeStamp) } : {}))), filesToTar);
|
|
130
|
+
yield tar.create(Object.assign(Object.assign({}, tarDefaultConfig), Object.assign({ statCache: statCache(options.layerOwner), portable: !options.layerOwner, prefix: "/", cwd: buildDir, file: layerFile, gzip: true, noMtime: !(options.setTimeStamp || options.preserveTimeStamp) }, (options.setTimeStamp ? { mtime: new Date(options.setTimeStamp) } : {}))), filesToTar);
|
|
131
131
|
const fhash = yield calculateHash(layerFile);
|
|
132
132
|
const finalName = path.join(todir, fhash + ".tar.gz");
|
|
133
133
|
yield fse.move(layerFile, finalName);
|
package/lib/cli.js
CHANGED
|
@@ -50,6 +50,7 @@ const possibleArgs = {
|
|
|
50
50
|
"--label <label>": "Optional: Single label (name=value). This option can be used multiple times.",
|
|
51
51
|
"--envs <envs>": "Optional: Comma-separated list of key value pairs to use av environment variables.",
|
|
52
52
|
"--env <env>": "Optional: Single environment variable (name=value). This option can be used multiple times.",
|
|
53
|
+
"--preserveTimeStamp": "Optional: Preserve timestamps on files in the added layers. This might help with cache invalidation.",
|
|
53
54
|
"--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",
|
|
54
55
|
"--verbose": "Verbose logging",
|
|
55
56
|
"--allowInsecureRegistries": "Allow insecure registries (with self-signed/untrusted cert)",
|
|
@@ -226,8 +227,8 @@ Object.keys(options.extraContent).forEach((k) => {
|
|
|
226
227
|
exitWithErrorIf(!fs.existsSync(options.folder + k), "Could not find `" + k + "` in the folder " + options.folder);
|
|
227
228
|
});
|
|
228
229
|
function run(options) {
|
|
229
|
-
var _a;
|
|
230
230
|
return __awaiter(this, void 0, void 0, function* () {
|
|
231
|
+
var _a;
|
|
231
232
|
if (!(yield fse.pathExists(options.folder)))
|
|
232
233
|
throw new Error("No such folder: " + options.folder);
|
|
233
234
|
const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "containerify-"));
|
package/lib/fileutil.js
CHANGED
|
@@ -9,7 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
12
|
+
exports.sizeOf = sizeOf;
|
|
13
|
+
exports.ensureEmptyDir = ensureEmptyDir;
|
|
13
14
|
const fs_1 = require("fs");
|
|
14
15
|
const fse = require("fs-extra");
|
|
15
16
|
const fss = require("fs");
|
|
@@ -18,7 +19,6 @@ function sizeOf(file) {
|
|
|
18
19
|
return (yield fs_1.promises.lstat(file)).size;
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
|
-
exports.sizeOf = sizeOf;
|
|
22
22
|
function ensureEmptyDir(path) {
|
|
23
23
|
return __awaiter(this, void 0, void 0, function* () {
|
|
24
24
|
if (fss.existsSync(path))
|
|
@@ -27,7 +27,6 @@ function ensureEmptyDir(path) {
|
|
|
27
27
|
return path;
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
-
exports.ensureEmptyDir = ensureEmptyDir;
|
|
31
30
|
exports.default = {
|
|
32
31
|
sizeOf,
|
|
33
32
|
ensureEmptyDir,
|
package/lib/httpRequest.d.ts
CHANGED
package/lib/httpRequest.js
CHANGED
|
@@ -9,7 +9,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
12
|
+
exports.redirectCodes = void 0;
|
|
13
|
+
exports.isOk = isOk;
|
|
14
|
+
exports.createHttpOptions = createHttpOptions;
|
|
15
|
+
exports.buildHeaders = buildHeaders;
|
|
16
|
+
exports.request = request;
|
|
17
|
+
exports.toError = toError;
|
|
18
|
+
exports.waitForResponseEnd = waitForResponseEnd;
|
|
19
|
+
exports.dlJson = dlJson;
|
|
20
|
+
exports.followRedirects = followRedirects;
|
|
13
21
|
const https = require("https");
|
|
14
22
|
const http = require("http");
|
|
15
23
|
const URL = require("url");
|
|
@@ -19,21 +27,22 @@ exports.redirectCodes = [308, 307, 303, 302, 301];
|
|
|
19
27
|
function isOk(httpStatus) {
|
|
20
28
|
return httpStatus >= 200 && httpStatus < 300;
|
|
21
29
|
}
|
|
22
|
-
exports.isOk = isOk;
|
|
23
30
|
function createHttpOptions(method, url, headers) {
|
|
24
31
|
const options = Object.assign({}, URL.parse(url));
|
|
25
32
|
options.headers = headers;
|
|
26
33
|
options.method = method;
|
|
34
|
+
if (url.includes("X-Amz-Algorithm") && method == "GET") {
|
|
35
|
+
//We are using a pre-signed URL, so we don't need to send the Authorization header
|
|
36
|
+
options.headers["Authorization"] = "";
|
|
37
|
+
}
|
|
27
38
|
return options;
|
|
28
39
|
}
|
|
29
|
-
exports.createHttpOptions = createHttpOptions;
|
|
30
40
|
function buildHeaders(accept, auth) {
|
|
31
41
|
const headers = { accept: accept };
|
|
32
42
|
if (auth)
|
|
33
43
|
headers.authorization = auth;
|
|
34
44
|
return headers;
|
|
35
45
|
}
|
|
36
|
-
exports.buildHeaders = buildHeaders;
|
|
37
46
|
function request(options, allowInsecure, callback) {
|
|
38
47
|
if (allowInsecure == types_1.InsecureRegistrySupport.YES)
|
|
39
48
|
options.rejectUnauthorized = false;
|
|
@@ -46,17 +55,14 @@ function request(options, allowInsecure, callback) {
|
|
|
46
55
|
});
|
|
47
56
|
return req;
|
|
48
57
|
}
|
|
49
|
-
exports.request = request;
|
|
50
58
|
function toError(res) {
|
|
51
59
|
return `Unexpected HTTP status ${res.statusCode} : ${res.statusMessage}`;
|
|
52
60
|
}
|
|
53
|
-
exports.toError = toError;
|
|
54
61
|
function waitForResponseEnd(res, cb) {
|
|
55
62
|
const data = [];
|
|
56
63
|
res.on("data", (d) => data.push(d));
|
|
57
64
|
res.on("end", () => cb(Buffer.concat(data)));
|
|
58
65
|
}
|
|
59
|
-
exports.waitForResponseEnd = waitForResponseEnd;
|
|
60
66
|
function dl(uri, headers, allowInsecure) {
|
|
61
67
|
logger_1.default.debug("dl", uri);
|
|
62
68
|
return new Promise((resolve, reject) => {
|
|
@@ -66,9 +72,17 @@ function dl(uri, headers, allowInsecure) {
|
|
|
66
72
|
return reject(result.error);
|
|
67
73
|
const { res } = result;
|
|
68
74
|
logger_1.default.debug(res.statusCode, res.statusMessage, res.headers["content-type"], res.headers["content-length"]);
|
|
69
|
-
if (!isOk((_a = res.statusCode) !== null && _a !== void 0 ? _a : 0))
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
if (!isOk((_a = res.statusCode) !== null && _a !== void 0 ? _a : 0)) {
|
|
76
|
+
const d = [];
|
|
77
|
+
res.on("data", (dt) => d.push(dt));
|
|
78
|
+
res.on("end", () => {
|
|
79
|
+
logger_1.default.error("ERROR", Buffer.concat(d).toString());
|
|
80
|
+
reject(toError(res));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
waitForResponseEnd(res, (data) => resolve(data.toString()));
|
|
85
|
+
}
|
|
72
86
|
});
|
|
73
87
|
});
|
|
74
88
|
}
|
|
@@ -78,7 +92,6 @@ function dlJson(uri, headers, allowInsecure) {
|
|
|
78
92
|
return JSON.parse(Buffer.from(data).toString("utf-8"));
|
|
79
93
|
});
|
|
80
94
|
}
|
|
81
|
-
exports.dlJson = dlJson;
|
|
82
95
|
function followRedirects(uri, headers, allowInsecure, cb, count = 0) {
|
|
83
96
|
logger_1.default.debug("rc", uri);
|
|
84
97
|
const options = createHttpOptions("GET", uri, headers);
|
|
@@ -88,6 +101,7 @@ function followRedirects(uri, headers, allowInsecure, cb, count = 0) {
|
|
|
88
101
|
if (count > 10)
|
|
89
102
|
return cb({ error: "Too many redirects for " + uri });
|
|
90
103
|
const location = res.headers.location;
|
|
104
|
+
console.log(res.headers);
|
|
91
105
|
if (!location)
|
|
92
106
|
return cb({ error: "Redirect, but missing location header" });
|
|
93
107
|
return followRedirects(location, headers, allowInsecure, cb, count + 1);
|
|
@@ -95,4 +109,3 @@ function followRedirects(uri, headers, allowInsecure, cb, count = 0) {
|
|
|
95
109
|
cb({ res });
|
|
96
110
|
}).end();
|
|
97
111
|
}
|
|
98
|
-
exports.followRedirects = followRedirects;
|
package/lib/registry.js
CHANGED
|
@@ -9,7 +9,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
12
|
+
exports.DEFAULT_DOCKER_REGISTRY = void 0;
|
|
13
|
+
exports.createRegistry = createRegistry;
|
|
14
|
+
exports.parseFullImageUrl = parseFullImageUrl;
|
|
13
15
|
const URL = require("url");
|
|
14
16
|
const fss = require("fs");
|
|
15
17
|
const fs_1 = require("fs");
|
|
@@ -138,7 +140,8 @@ function processToken(registryBaseUrl, allowInsecure, imagePath, token) {
|
|
|
138
140
|
return getGitLabToken(token, image.path, allowInsecure);
|
|
139
141
|
if (token.startsWith("Basic "))
|
|
140
142
|
return token;
|
|
141
|
-
|
|
143
|
+
const githubPrefixes = ["ghp_", "github_pat_", "gho_", "ghu_", "ghs_"];
|
|
144
|
+
if (githubPrefixes.some((prefix) => token.startsWith(prefix)))
|
|
142
145
|
return "Bearer " + Buffer.from(token).toString("base64");
|
|
143
146
|
return "Bearer " + token;
|
|
144
147
|
});
|
|
@@ -152,14 +155,14 @@ function getDockerToken(imagePath, allowInsecure) {
|
|
|
152
155
|
function getGitLabToken(token, imagePath, allowInsecure) {
|
|
153
156
|
return __awaiter(this, void 0, void 0, function* () {
|
|
154
157
|
if (token.includes(":")) {
|
|
155
|
-
token = "Basic " + Buffer.from(token
|
|
158
|
+
token = "Basic " + Buffer.from(token.replace("Basic ", "")).toString("base64");
|
|
156
159
|
}
|
|
157
160
|
const resp = yield (0, httpRequest_1.dlJson)(`https://gitlab.com/jwt/auth?service=container_registry&scope=repository:${imagePath}:pull,push`, { Authorization: token }, allowInsecure);
|
|
158
161
|
return `Bearer ${resp.token}`;
|
|
159
162
|
});
|
|
160
163
|
}
|
|
161
|
-
function createRegistry(
|
|
162
|
-
return __awaiter(this,
|
|
164
|
+
function createRegistry(registryBaseUrl_1, imagePath_1, allowInsecure_1, auth_1) {
|
|
165
|
+
return __awaiter(this, arguments, void 0, function* (registryBaseUrl, imagePath, allowInsecure, auth, optimisticToRegistryCheck = false) {
|
|
163
166
|
const token = yield processToken(registryBaseUrl, allowInsecure, imagePath, auth);
|
|
164
167
|
function exists(image, layer) {
|
|
165
168
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -174,8 +177,8 @@ function createRegistry(registryBaseUrl, imagePath, allowInsecure, auth, optimis
|
|
|
174
177
|
yield uploadContent(uploadUrl, file, layer, allowInsecure, token);
|
|
175
178
|
});
|
|
176
179
|
}
|
|
177
|
-
function getUploadUrl(
|
|
178
|
-
return __awaiter(this,
|
|
180
|
+
function getUploadUrl(image_1) {
|
|
181
|
+
return __awaiter(this, arguments, void 0, function* (image, mountParameters = undefined) {
|
|
179
182
|
return new Promise((resolve, reject) => {
|
|
180
183
|
const parameters = new URLSearchParams(mountParameters);
|
|
181
184
|
const url = `${registryBaseUrl}${image.path}/blobs/uploads/${parameters.size > 0 ? "?" + parameters : ""}`;
|
|
@@ -337,7 +340,6 @@ function createRegistry(registryBaseUrl, imagePath, allowInsecure, auth, optimis
|
|
|
337
340
|
};
|
|
338
341
|
});
|
|
339
342
|
}
|
|
340
|
-
exports.createRegistry = createRegistry;
|
|
341
343
|
exports.DEFAULT_DOCKER_REGISTRY = "https://registry-1.docker.io/v2/";
|
|
342
344
|
function parseFullImageUrl(imageStr) {
|
|
343
345
|
const [registry, ...rest] = imageStr.split("/");
|
|
@@ -352,4 +354,3 @@ function parseFullImageUrl(imageStr) {
|
|
|
352
354
|
image: rest.join("/"),
|
|
353
355
|
};
|
|
354
356
|
}
|
|
355
|
-
exports.parseFullImageUrl = parseFullImageUrl;
|
package/lib/types.d.ts
CHANGED
package/lib/utils.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.unique = unique;
|
|
4
|
+
exports.omit = omit;
|
|
5
|
+
exports.getPreferredPlatform = getPreferredPlatform;
|
|
6
|
+
exports.getManifestLayerType = getManifestLayerType;
|
|
7
|
+
exports.getLayerTypeFileEnding = getLayerTypeFileEnding;
|
|
8
|
+
exports.getHash = getHash;
|
|
9
|
+
exports.parseImage = parseImage;
|
|
4
10
|
const MIMETypes_1 = require("./MIMETypes");
|
|
5
11
|
function unique(vals) {
|
|
6
12
|
return [...new Set(vals)];
|
|
7
13
|
}
|
|
8
|
-
exports.unique = unique;
|
|
9
14
|
function omit(obj, keys) {
|
|
10
15
|
return Object.fromEntries(Object.entries(obj).filter(([k]) => !keys.includes(k)));
|
|
11
16
|
}
|
|
12
|
-
exports.omit = omit;
|
|
13
17
|
function getPreferredPlatform(platform) {
|
|
14
18
|
var _a, _b;
|
|
15
19
|
// We assume the input is similar to docker which accepts `<os>/<arch>` and `<arch>`
|
|
@@ -68,7 +72,6 @@ function getPreferredPlatform(platform) {
|
|
|
68
72
|
architecture: targetArch,
|
|
69
73
|
};
|
|
70
74
|
}
|
|
71
|
-
exports.getPreferredPlatform = getPreferredPlatform;
|
|
72
75
|
function getManifestLayerType(manifest) {
|
|
73
76
|
if (manifest.mediaType === MIMETypes_1.OCI.manifest) {
|
|
74
77
|
return MIMETypes_1.OCI.layer.gzip;
|
|
@@ -78,7 +81,6 @@ function getManifestLayerType(manifest) {
|
|
|
78
81
|
}
|
|
79
82
|
throw new Error(`${manifest.mediaType} not recognized.`);
|
|
80
83
|
}
|
|
81
|
-
exports.getManifestLayerType = getManifestLayerType;
|
|
82
84
|
function getLayerTypeFileEnding(layer) {
|
|
83
85
|
switch (layer.mediaType) {
|
|
84
86
|
case MIMETypes_1.OCI.layer.gzip:
|
|
@@ -91,15 +93,12 @@ function getLayerTypeFileEnding(layer) {
|
|
|
91
93
|
throw new Error(`Layer mediaType ${layer.mediaType} not known.`);
|
|
92
94
|
}
|
|
93
95
|
}
|
|
94
|
-
exports.getLayerTypeFileEnding = getLayerTypeFileEnding;
|
|
95
96
|
function getHash(digest) {
|
|
96
97
|
return digest.split(":")[1];
|
|
97
98
|
}
|
|
98
|
-
exports.getHash = getHash;
|
|
99
99
|
function parseImage(imageStr) {
|
|
100
100
|
const ar = imageStr.split(":");
|
|
101
101
|
const tag = ar[1] || "latest";
|
|
102
102
|
const ipath = ar[0];
|
|
103
103
|
return { path: ipath, tag: tag };
|
|
104
104
|
}
|
|
105
|
-
exports.parseImage = parseImage;
|
package/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "3.
|
|
1
|
+
export declare const VERSION = "3.2.0";
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "containerify",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Build node.js docker images without docker",
|
|
5
5
|
"main": "./lib/cli.js",
|
|
6
6
|
"scripts": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"allTests": "npm run integrationTest && npm run registryTest"
|
|
17
17
|
},
|
|
18
18
|
"bin": {
|
|
19
|
-
"containerify": "
|
|
19
|
+
"containerify": "lib/cli.js"
|
|
20
20
|
},
|
|
21
21
|
"author": "Erlend Oftedal <erlend@oftedal.no>",
|
|
22
22
|
"contributors": [
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
28
|
-
"url": "https://github.com/eoftedal/containerify.git"
|
|
28
|
+
"url": "git+https://github.com/eoftedal/containerify.git"
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
31
31
|
"lib/"
|