containerify 3.0.0 → 3.1.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/README.md CHANGED
@@ -80,6 +80,7 @@ Options:
80
80
  --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
81
81
  --verbose Verbose logging
82
82
  --allowInsecureRegistries Allow insecure registries (with self-signed/untrusted cert)
83
+ --allowNoPushAuth Allow pushing images without authentication/token if the registry allows it
83
84
  --customContent <dirs/files> Optional: Skip normal node_modules and applayer and include specified root folder files/directories instead. You can specify as
84
85
  local-path:absolute-container-path if you want to place it in a specific location
85
86
  --extraContent <dirs/files> Optional: Add specific content. Specify as local-path:absolute-container-path,local-path2:absolute-container-path2 etc
package/lib/cli.js CHANGED
@@ -53,6 +53,7 @@ const possibleArgs = {
53
53
  "--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
54
  "--verbose": "Verbose logging",
55
55
  "--allowInsecureRegistries": "Allow insecure registries (with self-signed/untrusted cert)",
56
+ "--allowNoPushAuth": "Allow pushing images without a authentication/token to registries that allow it",
56
57
  "--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",
57
58
  "--extraContent <dirs/files>": "Optional: Add specific content. Specify as local-path:absolute-container-path,local-path2:absolute-container-path2 etc",
58
59
  "--layerOwner <gid:uid>": "Optional: Set specific gid and uid on files in the added layers",
@@ -196,7 +197,7 @@ exitWithErrorIf(!options.folder, "--folder must be specified");
196
197
  exitWithErrorIf(!options.fromImage, "--fromImage must be specified");
197
198
  exitWithErrorIf(!options.toImage, "--toImage must be specified");
198
199
  exitWithErrorIf(!options.toRegistry && !options.toTar && !options.toDocker, "Must specify either --toTar, --toRegistry or --toDocker");
199
- exitWithErrorIf(!!options.toRegistry && !options.toToken, "A token must be given when uploading to docker hub");
200
+ exitWithErrorIf(!!options.toRegistry && !options.toToken && !options.allowNoPushAuth, "A token must be provided when uploading images");
200
201
  if (options.toRegistry && !options.toRegistry.endsWith("/"))
201
202
  options.toRegistry += "/";
202
203
  if (options.fromRegistry && !options.fromRegistry.endsWith("/"))
@@ -235,7 +236,7 @@ function run(options) {
235
236
  const todir = yield (0, fileutil_1.ensureEmptyDir)(path.join(tmpdir, "to"));
236
237
  const allowInsecure = options.allowInsecureRegistries ? types_1.InsecureRegistrySupport.YES : types_1.InsecureRegistrySupport.NO;
237
238
  const fromRegistryUrl = (_a = options.fromRegistry) !== null && _a !== void 0 ? _a : registry_1.DEFAULT_DOCKER_REGISTRY;
238
- const fromRegistry = (0, registry_1.createRegistry)(fromRegistryUrl, yield (0, registry_1.processToken)(fromRegistryUrl, allowInsecure, options.fromImage, options.fromToken), allowInsecure);
239
+ const fromRegistry = yield (0, registry_1.createRegistry)(fromRegistryUrl, options.fromImage, allowInsecure, options.fromToken);
239
240
  const originalManifest = yield fromRegistry.download(options.fromImage, fromdir, (0, utils_1.getPreferredPlatform)(options.platform), options.layerCacheFolder);
240
241
  yield appLayerCreator_1.default.addLayers(tmpdir, fromdir, todir, options);
241
242
  if (options.toDocker) {
@@ -250,7 +251,7 @@ function run(options) {
250
251
  yield tarExporter_1.default.saveToTar(todir, tmpdir, options.toTar, [options.toImage], options);
251
252
  }
252
253
  if (options.toRegistry) {
253
- const toRegistry = (0, registry_1.createRegistry)(options.toRegistry, yield (0, registry_1.processToken)(options.toRegistry, allowInsecure, options.toImage, options.toToken), allowInsecure, options.optimisticToRegistryCheck);
254
+ const toRegistry = yield (0, registry_1.createRegistry)(options.toRegistry, options.toImage, allowInsecure, options.toToken, options.optimisticToRegistryCheck);
254
255
  yield toRegistry.upload(options.toImage, todir, options.doCrossMount, originalManifest, options.fromImage);
255
256
  }
256
257
  logger_1.default.debug("Deleting " + tmpdir + " ...");
package/lib/registry.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { InsecureRegistrySupport, Registry } from "./types";
2
- export declare function processToken(registryBaseUrl: string, allowInsecure: InsecureRegistrySupport, imagePath: string, token?: string): Promise<string>;
3
- export declare function createRegistry(registryBaseUrl: string, auth: string, allowInsecure: InsecureRegistrySupport, optimisticToRegistryCheck?: boolean): Registry;
2
+ export declare function createRegistry(registryBaseUrl: string, imagePath: string, allowInsecure: InsecureRegistrySupport, auth?: string, optimisticToRegistryCheck?: boolean): Promise<Registry>;
4
3
  export declare const DEFAULT_DOCKER_REGISTRY = "https://registry-1.docker.io/v2/";
5
4
  export declare function parseFullImageUrl(imageStr: string): {
6
5
  registry: string;
package/lib/registry.js CHANGED
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.parseFullImageUrl = exports.DEFAULT_DOCKER_REGISTRY = exports.createRegistry = exports.processToken = void 0;
12
+ exports.parseFullImageUrl = exports.DEFAULT_DOCKER_REGISTRY = exports.createRegistry = void 0;
13
13
  const URL = require("url");
14
14
  const fss = require("fs");
15
15
  const fs_1 = require("fs");
@@ -98,11 +98,13 @@ function uploadContent(uploadUrl, file, fileConfig, allowInsecure, auth, content
98
98
  let url = uploadUrl;
99
99
  if (fileConfig.digest)
100
100
  url += (url.indexOf("?") == -1 ? "?" : "&") + "digest=" + fileConfig.digest;
101
- const options = (0, httpRequest_1.createHttpOptions)("PUT", url, {
102
- authorization: auth,
101
+ const headers = {
103
102
  "content-length": fileConfig.size,
104
103
  "content-type": contentType,
105
- });
104
+ };
105
+ if (auth)
106
+ headers.authorization = auth;
107
+ const options = (0, httpRequest_1.createHttpOptions)("PUT", url, headers);
106
108
  logger_1.default.debug(options.method, url);
107
109
  const req = (0, httpRequest_1.request)(options, allowInsecure, (res) => {
108
110
  var _a;
@@ -140,7 +142,7 @@ function processToken(registryBaseUrl, allowInsecure, imagePath, token) {
140
142
  return `Bearer ${resp.token}`;
141
143
  }
142
144
  if (!token)
143
- throw new Error("Needs auth token to upload to " + registryBaseUrl);
145
+ return ""; //We allow to pull from tokenless registries
144
146
  if (token.startsWith("Basic "))
145
147
  return token;
146
148
  if (token.startsWith("ghp_"))
@@ -148,181 +150,184 @@ function processToken(registryBaseUrl, allowInsecure, imagePath, token) {
148
150
  return "Bearer " + token;
149
151
  });
150
152
  }
151
- exports.processToken = processToken;
152
- function createRegistry(registryBaseUrl, auth, allowInsecure, optimisticToRegistryCheck = false) {
153
- function exists(image, layer) {
154
- return __awaiter(this, void 0, void 0, function* () {
155
- const url = `${registryBaseUrl}${image.path}/blobs/${layer.digest}`;
156
- return yield checkIfLayerExists(url, (0, httpRequest_1.buildHeaders)(layer.mediaType, auth), allowInsecure, optimisticToRegistryCheck, 0);
157
- });
158
- }
159
- function uploadLayerContent(uploadUrl, layer, dir) {
160
- return __awaiter(this, void 0, void 0, function* () {
161
- logger_1.default.info(layer.digest);
162
- const file = path.join(dir, (0, utils_1.getHash)(layer.digest) + (0, utils_1.getLayerTypeFileEnding)(layer));
163
- yield uploadContent(uploadUrl, file, layer, allowInsecure, auth);
164
- });
165
- }
166
- function getUploadUrl(image, mountParameters = undefined) {
167
- return __awaiter(this, void 0, void 0, function* () {
168
- return new Promise((resolve, reject) => {
169
- const parameters = new URLSearchParams(mountParameters);
170
- const url = `${registryBaseUrl}${image.path}/blobs/uploads/${parameters.size > 0 ? "?" + parameters : ""}`;
171
- const options = URL.parse(url);
172
- options.method = "POST";
173
- options.headers = { authorization: auth };
174
- (0, httpRequest_1.request)(options, allowInsecure, (res) => {
175
- logger_1.default.debug("POST", `${url}`, res.statusCode);
176
- if (res.statusCode == 202) {
177
- const { location } = res.headers;
178
- if (location) {
179
- if (location.startsWith("http")) {
180
- resolve({ uploadUrl: location });
153
+ function createRegistry(registryBaseUrl, imagePath, allowInsecure, auth, optimisticToRegistryCheck = false) {
154
+ return __awaiter(this, void 0, void 0, function* () {
155
+ const token = yield processToken(registryBaseUrl, allowInsecure, imagePath, auth);
156
+ function exists(image, layer) {
157
+ return __awaiter(this, void 0, void 0, function* () {
158
+ const url = `${registryBaseUrl}${image.path}/blobs/${layer.digest}`;
159
+ return yield checkIfLayerExists(url, (0, httpRequest_1.buildHeaders)(layer.mediaType, token), allowInsecure, optimisticToRegistryCheck, 0);
160
+ });
161
+ }
162
+ function uploadLayerContent(uploadUrl, layer, dir) {
163
+ return __awaiter(this, void 0, void 0, function* () {
164
+ logger_1.default.info(layer.digest);
165
+ const file = path.join(dir, (0, utils_1.getHash)(layer.digest) + (0, utils_1.getLayerTypeFileEnding)(layer));
166
+ yield uploadContent(uploadUrl, file, layer, allowInsecure, token);
167
+ });
168
+ }
169
+ function getUploadUrl(image, mountParameters = undefined) {
170
+ return __awaiter(this, void 0, void 0, function* () {
171
+ return new Promise((resolve, reject) => {
172
+ const parameters = new URLSearchParams(mountParameters);
173
+ const url = `${registryBaseUrl}${image.path}/blobs/uploads/${parameters.size > 0 ? "?" + parameters : ""}`;
174
+ const options = URL.parse(url);
175
+ options.method = "POST";
176
+ if (auth)
177
+ options.headers = { authorization: auth };
178
+ (0, httpRequest_1.request)(options, allowInsecure, (res) => {
179
+ logger_1.default.debug("POST", `${url}`, res.statusCode);
180
+ if (res.statusCode == 202) {
181
+ const { location } = res.headers;
182
+ if (location) {
183
+ if (location.startsWith("http")) {
184
+ resolve({ uploadUrl: location });
185
+ }
186
+ else {
187
+ const regURL = URL.parse(registryBaseUrl);
188
+ resolve({
189
+ uploadUrl: `${regURL.protocol}//${regURL.hostname}${regURL.port ? ":" + regURL.port : ""}${location}`,
190
+ });
191
+ }
181
192
  }
182
- else {
183
- const regURL = URL.parse(registryBaseUrl);
184
- resolve({
185
- uploadUrl: `${regURL.protocol}//${regURL.hostname}${regURL.port ? ":" + regURL.port : ""}${location}`,
186
- });
193
+ reject("Missing location for 202");
194
+ }
195
+ else if (mountParameters && res.statusCode == 201) {
196
+ const returnedDigest = res.headers["docker-content-digest"];
197
+ if (returnedDigest && returnedDigest != mountParameters.mount) {
198
+ reject(`ERROR: Layer mounted with wrong digest: Expected ${mountParameters.mount} but got ${returnedDigest}`);
187
199
  }
200
+ resolve({ mountSuccess: true });
188
201
  }
189
- reject("Missing location for 202");
190
- }
191
- else if (mountParameters && res.statusCode == 201) {
192
- const returnedDigest = res.headers["docker-content-digest"];
193
- if (returnedDigest && returnedDigest != mountParameters.mount) {
194
- reject(`ERROR: Layer mounted with wrong digest: Expected ${mountParameters.mount} but got ${returnedDigest}`);
202
+ else {
203
+ (0, httpRequest_1.waitForResponseEnd)(res, (data) => {
204
+ reject(`Error getting upload URL from ${url}. Got ${res.statusCode} ${res.statusMessage}:\n${data.toString()}`);
205
+ });
195
206
  }
196
- resolve({ mountSuccess: true });
197
- }
198
- else {
199
- (0, httpRequest_1.waitForResponseEnd)(res, (data) => {
200
- reject(`Error getting upload URL from ${url}. Got ${res.statusCode} ${res.statusMessage}:\n${data.toString()}`);
201
- });
202
- }
203
- }).end();
207
+ }).end();
208
+ });
204
209
  });
205
- });
206
- }
207
- function dlManifest(image, preferredPlatform, allowInsecure) {
208
- return __awaiter(this, void 0, void 0, function* () {
209
- // Accept both manifests and index/manifest lists
210
- const res = yield (0, httpRequest_1.dlJson)(`${registryBaseUrl}${image.path}/manifests/${image.tag}`, (0, httpRequest_1.buildHeaders)(`${MIMETypes_1.OCI.index}, ${MIMETypes_1.OCI.manifest}, ${MIMETypes_1.DockerV2.index}, ${MIMETypes_1.DockerV2.manifest}`, auth), allowInsecure);
211
- // We've received an OCI Index or Docker Manifest List and need to find which manifest we want
212
- if (res.mediaType === MIMETypes_1.OCI.index || res.mediaType === MIMETypes_1.DockerV2.index) {
213
- const availableManifests = res.manifests;
214
- const adequateManifest = pickManifest(availableManifests, preferredPlatform);
215
- return dlManifest(Object.assign(Object.assign({}, image), { tag: adequateManifest.digest }), preferredPlatform, allowInsecure);
210
+ }
211
+ function dlManifest(image, preferredPlatform, allowInsecure) {
212
+ return __awaiter(this, void 0, void 0, function* () {
213
+ // Accept both manifests and index/manifest lists
214
+ const res = yield (0, httpRequest_1.dlJson)(`${registryBaseUrl}${image.path}/manifests/${image.tag}`, (0, httpRequest_1.buildHeaders)(`${MIMETypes_1.OCI.index}, ${MIMETypes_1.OCI.manifest}, ${MIMETypes_1.DockerV2.index}, ${MIMETypes_1.DockerV2.manifest}`, token), allowInsecure);
215
+ // We've received an OCI Index or Docker Manifest List and need to find which manifest we want
216
+ if (res.mediaType === MIMETypes_1.OCI.index || res.mediaType === MIMETypes_1.DockerV2.index) {
217
+ const availableManifests = res.manifests;
218
+ const adequateManifest = pickManifest(availableManifests, preferredPlatform);
219
+ return dlManifest(Object.assign(Object.assign({}, image), { tag: adequateManifest.digest }), preferredPlatform, allowInsecure);
220
+ }
221
+ return res;
222
+ });
223
+ }
224
+ function pickManifest(manifests, preferredPlatform) {
225
+ const matchingArchitectures = new Set();
226
+ const matchingOSes = new Set();
227
+ // Find sets of matching architecture and os
228
+ for (const manifest of manifests) {
229
+ if (manifest.platform.architecture === preferredPlatform.architecture) {
230
+ matchingArchitectures.add(manifest);
231
+ }
232
+ if (manifest.platform.os === preferredPlatform.os) {
233
+ matchingOSes.add(manifest);
234
+ }
216
235
  }
217
- return res;
218
- });
219
- }
220
- function pickManifest(manifests, preferredPlatform) {
221
- const matchingArchitectures = new Set();
222
- const matchingOSes = new Set();
223
- // Find sets of matching architecture and os
224
- for (const manifest of manifests) {
225
- if (manifest.platform.architecture === preferredPlatform.architecture) {
226
- matchingArchitectures.add(manifest);
236
+ // If the intersection of matching architectures and OS is one we've found our optimal match
237
+ const intersection = new Set([...matchingArchitectures].filter((x) => matchingOSes.has(x)));
238
+ if (intersection.size == 1) {
239
+ return intersection.values().next().value;
227
240
  }
228
- if (manifest.platform.os === preferredPlatform.os) {
229
- matchingOSes.add(manifest);
241
+ // If we don't have a perfect match we give a warning and try the first matching architecture
242
+ if (matchingArchitectures.size >= 1) {
243
+ const matchingArch = matchingArchitectures.values().next().value;
244
+ logger_1.default.info(`[WARN] Preferred OS '${preferredPlatform.os}' not available.`);
245
+ logger_1.default.info("[WARN] Using closest available manifest:", JSON.stringify(matchingArch.platform));
246
+ return matchingArch;
230
247
  }
248
+ // If there's no image matching the wanted architecture we bail
249
+ logger_1.default.error(`No image matching requested architecture: '${preferredPlatform.architecture}'`);
250
+ logger_1.default.error("Available platforms:", JSON.stringify(manifests.map((m) => m.platform)));
251
+ throw new Error("No image matching requested architecture");
231
252
  }
232
- // If the intersection of matching architectures and OS is one we've found our optimal match
233
- const intersection = new Set([...matchingArchitectures].filter((x) => matchingOSes.has(x)));
234
- if (intersection.size == 1) {
235
- return intersection.values().next().value;
253
+ function dlConfig(image, config, allowInsecure) {
254
+ return __awaiter(this, void 0, void 0, function* () {
255
+ return yield (0, httpRequest_1.dlJson)(`${registryBaseUrl}${image.path}/blobs/${config.digest}`, (0, httpRequest_1.buildHeaders)("*/*", token), allowInsecure);
256
+ });
236
257
  }
237
- // If we don't have a perfect match we give a warning and try the first matching architecture
238
- if (matchingArchitectures.size >= 1) {
239
- const matchingArch = matchingArchitectures.values().next().value;
240
- logger_1.default.info(`[WARN] Preferred OS '${preferredPlatform.os}' not available.`);
241
- logger_1.default.info("[WARN] Using closest available manifest:", JSON.stringify(matchingArch.platform));
242
- return matchingArch;
258
+ function dlLayer(image, layer, folder, allowInsecure, cacheFolder) {
259
+ return __awaiter(this, void 0, void 0, function* () {
260
+ const file = (0, utils_1.getHash)(layer.digest) + (0, utils_1.getLayerTypeFileEnding)(layer);
261
+ yield dlToFile(`${registryBaseUrl}${image.path}/blobs/${layer.digest}`, path.join(folder, file), (0, httpRequest_1.buildHeaders)(layer.mediaType, token), allowInsecure, cacheFolder);
262
+ return file;
263
+ });
243
264
  }
244
- // If there's no image matching the wanted architecture we bail
245
- logger_1.default.error(`No image matching requested architecture: '${preferredPlatform.architecture}'`);
246
- logger_1.default.error("Available platforms:", JSON.stringify(manifests.map((m) => m.platform)));
247
- throw new Error("No image matching requested architecture");
248
- }
249
- function dlConfig(image, config, allowInsecure) {
250
- return __awaiter(this, void 0, void 0, function* () {
251
- return yield (0, httpRequest_1.dlJson)(`${registryBaseUrl}${image.path}/blobs/${config.digest}`, (0, httpRequest_1.buildHeaders)("*/*", auth), allowInsecure);
252
- });
253
- }
254
- function dlLayer(image, layer, folder, allowInsecure, cacheFolder) {
255
- return __awaiter(this, void 0, void 0, function* () {
256
- const file = (0, utils_1.getHash)(layer.digest) + (0, utils_1.getLayerTypeFileEnding)(layer);
257
- yield dlToFile(`${registryBaseUrl}${image.path}/blobs/${layer.digest}`, path.join(folder, file), (0, httpRequest_1.buildHeaders)(layer.mediaType, auth), allowInsecure, cacheFolder);
258
- return file;
259
- });
260
- }
261
- function upload(imageStr, folder, doCrossMount, originalManifest, originalRepository) {
262
- return __awaiter(this, void 0, void 0, function* () {
263
- const image = (0, utils_1.parseImage)(imageStr);
264
- const manifestFile = path.join(folder, "manifest.json");
265
- const manifest = (yield fse.readJson(manifestFile));
266
- logger_1.default.info("Checking layer status...");
267
- const layerStatus = yield Promise.all(manifest.layers.map((l) => __awaiter(this, void 0, void 0, function* () {
268
- return { layer: l, exists: yield exists(image, l) };
269
- })));
270
- const layersForUpload = layerStatus.filter((l) => !l.exists);
271
- logger_1.default.debug("Needs upload:", layersForUpload.map((l) => l.layer.digest));
272
- logger_1.default.info("Uploading layers...");
273
- yield Promise.all(layersForUpload.map((l) => __awaiter(this, void 0, void 0, function* () {
274
- if (doCrossMount && originalManifest.layers.find((x) => x.digest == l.layer.digest)) {
275
- const mount = yield getUploadUrl(image, { mount: l.layer.digest, from: originalRepository });
276
- if ("mountSuccess" in mount) {
277
- logger_1.default.info(`Cross mounted layer ${l.layer.digest} from '${originalRepository}'`);
278
- return;
265
+ function upload(imageStr, folder, doCrossMount, originalManifest, originalRepository) {
266
+ return __awaiter(this, void 0, void 0, function* () {
267
+ const image = (0, utils_1.parseImage)(imageStr);
268
+ const manifestFile = path.join(folder, "manifest.json");
269
+ const manifest = (yield fse.readJson(manifestFile));
270
+ logger_1.default.info("Checking layer status...");
271
+ const layerStatus = yield Promise.all(manifest.layers.map((l) => __awaiter(this, void 0, void 0, function* () {
272
+ return { layer: l, exists: yield exists(image, l) };
273
+ })));
274
+ const layersForUpload = layerStatus.filter((l) => !l.exists);
275
+ logger_1.default.debug("Needs upload:", layersForUpload.map((l) => l.layer.digest));
276
+ logger_1.default.info("Uploading layers...");
277
+ yield Promise.all(layersForUpload.map((l) => __awaiter(this, void 0, void 0, function* () {
278
+ if (doCrossMount && originalManifest.layers.find((x) => x.digest == l.layer.digest)) {
279
+ const mount = yield getUploadUrl(image, { mount: l.layer.digest, from: originalRepository });
280
+ if ("mountSuccess" in mount) {
281
+ logger_1.default.info(`Cross mounted layer ${l.layer.digest} from '${originalRepository}'`);
282
+ return;
283
+ }
284
+ yield uploadLayerContent(mount.uploadUrl, l.layer, folder);
285
+ }
286
+ else {
287
+ const url = yield getUploadUrl(image);
288
+ if ("mountSuccess" in url)
289
+ throw new Error("Mounting not supported for this upload");
290
+ yield uploadLayerContent(url.uploadUrl, l.layer, folder);
279
291
  }
280
- yield uploadLayerContent(mount.uploadUrl, l.layer, folder);
292
+ })));
293
+ logger_1.default.info("Uploading config...");
294
+ const configUploadUrl = yield getUploadUrl(image);
295
+ if ("mountSuccess" in configUploadUrl)
296
+ throw new Error("Mounting not supported for config upload");
297
+ const configFile = path.join(folder, (0, utils_1.getHash)(manifest.config.digest) + ".json");
298
+ yield uploadContent(configUploadUrl.uploadUrl, configFile, manifest.config, allowInsecure, token);
299
+ logger_1.default.info("Uploading manifest...");
300
+ const manifestSize = yield fileutil.sizeOf(manifestFile);
301
+ yield uploadContent(`${registryBaseUrl}${image.path}/manifests/${image.tag}`, manifestFile, { mediaType: manifest.mediaType, size: manifestSize }, allowInsecure, token, manifest.mediaType);
302
+ });
303
+ }
304
+ function download(imageStr, folder, preferredPlatform, cacheFolder) {
305
+ return __awaiter(this, void 0, void 0, function* () {
306
+ const image = (0, utils_1.parseImage)(imageStr);
307
+ logger_1.default.info("Downloading manifest...");
308
+ const manifest = yield dlManifest(image, preferredPlatform, allowInsecure);
309
+ yield fs_1.promises.writeFile(path.join(folder, "manifest.json"), JSON.stringify(manifest));
310
+ logger_1.default.info("Downloading config...");
311
+ const config = yield dlConfig(image, manifest.config, allowInsecure);
312
+ if (config.architecture != preferredPlatform.architecture) {
313
+ logger_1.default.info(`[WARN] Image architecture (${config.architecture}) does not match preferred architecture (${preferredPlatform.architecture}).`);
281
314
  }
282
- else {
283
- const url = yield getUploadUrl(image);
284
- if ("mountSuccess" in url)
285
- throw new Error("Mounting not supported for this upload");
286
- yield uploadLayerContent(url.uploadUrl, l.layer, folder);
315
+ if (config.os != preferredPlatform.os) {
316
+ logger_1.default.info(`[WARN] Image OS (${config.os}) does not match preferred OS (${preferredPlatform.os}).`);
287
317
  }
288
- })));
289
- logger_1.default.info("Uploading config...");
290
- const configUploadUrl = yield getUploadUrl(image);
291
- if ("mountSuccess" in configUploadUrl)
292
- throw new Error("Mounting not supported for config upload");
293
- const configFile = path.join(folder, (0, utils_1.getHash)(manifest.config.digest) + ".json");
294
- yield uploadContent(configUploadUrl.uploadUrl, configFile, manifest.config, allowInsecure, auth);
295
- logger_1.default.info("Uploading manifest...");
296
- const manifestSize = yield fileutil.sizeOf(manifestFile);
297
- yield uploadContent(`${registryBaseUrl}${image.path}/manifests/${image.tag}`, manifestFile, { mediaType: manifest.mediaType, size: manifestSize }, allowInsecure, auth, manifest.mediaType);
298
- });
299
- }
300
- function download(imageStr, folder, preferredPlatform, cacheFolder) {
301
- return __awaiter(this, void 0, void 0, function* () {
302
- const image = (0, utils_1.parseImage)(imageStr);
303
- logger_1.default.info("Downloading manifest...");
304
- const manifest = yield dlManifest(image, preferredPlatform, allowInsecure);
305
- yield fs_1.promises.writeFile(path.join(folder, "manifest.json"), JSON.stringify(manifest));
306
- logger_1.default.info("Downloading config...");
307
- const config = yield dlConfig(image, manifest.config, allowInsecure);
308
- if (config.architecture != preferredPlatform.architecture) {
309
- logger_1.default.info(`[WARN] Image architecture (${config.architecture}) does not match preferred architecture (${preferredPlatform.architecture}).`);
310
- }
311
- if (config.os != preferredPlatform.os) {
312
- logger_1.default.info(`[WARN] Image OS (${config.os}) does not match preferred OS (${preferredPlatform.os}).`);
313
- }
314
- yield fs_1.promises.writeFile(path.join(folder, "config.json"), JSON.stringify(config));
315
- logger_1.default.info("Downloading layers...");
316
- yield Promise.all(manifest.layers.map((layer) => dlLayer(image, layer, folder, allowInsecure, cacheFolder)));
317
- logger_1.default.info("Image downloaded.");
318
- return manifest;
319
- });
320
- }
321
- return {
322
- download: download,
323
- upload: upload,
324
- registryBaseUrl,
325
- };
318
+ yield fs_1.promises.writeFile(path.join(folder, "config.json"), JSON.stringify(config));
319
+ logger_1.default.info("Downloading layers...");
320
+ yield Promise.all(manifest.layers.map((layer) => dlLayer(image, layer, folder, allowInsecure, cacheFolder)));
321
+ logger_1.default.info("Image downloaded.");
322
+ return manifest;
323
+ });
324
+ }
325
+ return {
326
+ download: download,
327
+ upload: upload,
328
+ registryBaseUrl,
329
+ };
330
+ });
326
331
  }
327
332
  exports.createRegistry = createRegistry;
328
333
  exports.DEFAULT_DOCKER_REGISTRY = "https://registry-1.docker.io/v2/";
package/lib/types.d.ts CHANGED
@@ -79,6 +79,7 @@ export type Options = {
79
79
  setTimeStamp?: string;
80
80
  verbose?: boolean;
81
81
  allowInsecureRegistries?: boolean;
82
+ allowNoPushAuth?: boolean;
82
83
  customContent: Record<string, string>;
83
84
  extraContent: Record<string, string>;
84
85
  layerOwner?: string;
package/lib/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "3.0.0";
1
+ export declare const VERSION = "3.1.0";
package/lib/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = "3.0.0";
4
+ exports.VERSION = "3.1.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "containerify",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Build node.js docker images without docker",
5
5
  "main": "./lib/cli.js",
6
6
  "scripts": {
@@ -12,12 +12,16 @@
12
12
  "check": "npm run lint && npm run typecheck",
13
13
  "dev": "tsc --watch",
14
14
  "integrationTest": "cd tests/integration/ && ./test.sh",
15
- "registryTest": "cd tests/localtest/ && ./test.sh"
15
+ "registryTest": "cd tests/localtest/ && ./test.sh && ./test-insecure.sh",
16
+ "allTests": "npm run integrationTest && npm run registryTest"
16
17
  },
17
18
  "bin": {
18
19
  "containerify": "./lib/cli.js"
19
20
  },
20
21
  "author": "Erlend Oftedal <erlend@oftedal.no>",
22
+ "contributors": [
23
+ "Vegard S. Hagen <vegard@stonegarden.dev>"
24
+ ],
21
25
  "license": "Apache-2.0",
22
26
  "repository": {
23
27
  "type": "git",