@izykitten/ce 0.0.3-dev.0 → 0.0.3-dev.2

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.
Files changed (3) hide show
  1. package/README.md +20 -3
  2. package/dist/ce.js +139 -54
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -38,7 +38,7 @@ Tags and the `TAG_PREFIX`/`CACHE_PREFIX` values can use placeholders, filled fro
38
38
  | `{image}` | registry host and repository, lowercased, e.g. `code.izy.sh/library/gateway` |
39
39
  | `{ref}` | the branch name, `pr-<number>` for a pull request, or the tag name |
40
40
  | `{branch}`, `{pr}`, `{tag}` | the branch, PR number or tag, set only when building that kind of ref |
41
- | `{default}` | set only when building the default branch, never for a pull request |
41
+ | `{default}` | set only when building the default branch, never for a pull request or tag |
42
42
  | `{revision}`, `{sha}` | the commit, in full and as its first 7 characters |
43
43
  | `{version}`, `{major}`, `{minor}`, `{patch}` | from a SemVer tag such as `v1.2.3` or `1.2.3-rc.1` |
44
44
  | `{stable}` | set only for a SemVer tag without a pre-release |
@@ -48,7 +48,7 @@ Tags and the `TAG_PREFIX`/`CACHE_PREFIX` values can use placeholders, filled fro
48
48
 
49
49
  Values are made safe for a tag: any ASCII character a tag cannot have becomes `-`, so branch `ci/modernize-build` gives `ci-modernize-build`; a non-ASCII character is an error. A value never adds a tag or changes the image name. Prefixes can use `{name}` but not guards.
50
50
 
51
- Guards decide from the run's metadata, so they need it too: outside a Forgejo Actions run, or with its event file missing `repository.default_branch`, `{?default}` fails like `{default}` would. Only `{env.NAME}` works everywhere.
51
+ Guards decide from the run's metadata, so they need it too: outside a Forgejo Actions run, `{?default}` fails like `{default}` would. Only `{env.NAME}` works everywhere. To tell whether a branch is the default one, ce reads `repository.default_branch` from the event file, or `DEFAULT_BRANCH` when the event has none, as on a scheduled run; set `DEFAULT_BRANCH: main` in such a workflow. Pull requests and tags are never the default branch, so they need neither.
52
52
 
53
53
  Put templates in single quotes: `?` is a glob character in zsh.
54
54
 
@@ -64,6 +64,12 @@ A branch, PR and default-branch build:
64
64
  -t '{image}:{ref},{?default}latest,{?default}caddy-{env.CADDY_VERSION}' .
65
65
  ```
66
66
 
67
+ With `CACHE_PREFIX='{ref}'`, each branch and pull request gets a gha cache of its own, so a new one starts cold. With `CACHE_FROM_DEFAULT=true`, or `--cache-from-default` before the engine, each gha `--cache-to` is also read from its scope on the default branch: ce expands the cache prefix as it would be there, so PR 3 reads `pr-3` and `main` and writes only `pr-3`, and with `scope=js` it reads `main-js`. The default branch comes from the event file or `DEFAULT_BRANCH`, as for `{default}`. A prefix with `{sha}` or `{revision}` cannot be worked out for it, and is an error. This only picks what is read: whether it hits depends on what the default branch last wrote.
68
+
69
+ The same can be written by hand, since ce only prefixes a `--cache-from` whose scope matches a `--cache-to` and passes any other on unchanged: `--cache type=gha,mode=max --cache-from type=gha,scope=main`.
70
+
71
+ The `--cache-from` that `--cache` makes leaves out a gha `mode=`, which only applies to export, and a `--cache-from` given twice, as the default branch's own scope is on the default branch, is passed once.
72
+
67
73
  A release on a SemVer tag, with the version aliases only for stable releases:
68
74
 
69
75
  ```sh
@@ -72,6 +78,15 @@ A release on a SemVer tag, with the version aliases only for stable releases:
72
78
 
73
79
  `{stable}` does not mean newest: rebuilding an older release moves its aliases back to it.
74
80
 
81
+ ### Checking a template
82
+
83
+ `--dry-run` before the engine, or `CE_DRY_RUN=true`, prints the build summary and the engine command ce would run, and exits 0 without running it or waiting for the engine. A template that fails still exits 2, so it also works as a check before pushing a workflow:
84
+
85
+ ```sh
86
+ GITHUB_REF=refs/tags/v1.2.3 GITHUB_SERVER_URL=https://code.example GITHUB_REPOSITORY=org/app \
87
+ ce --dry-run docker buildx build -t '{image}:{ref},{?stable}{major}.{minor}' .
88
+ ```
89
+
75
90
  ### OCI labels
76
91
 
77
92
  With `OCI_LABELS=true`, or `--oci-labels` before the engine (`ce --oci-labels docker buildx build …`, like the prefix options), builds get `org.opencontainers.image.source`, `revision`, `version`, `title` and `description` labels from the run. Labels the run cannot tell are left out. They override a Dockerfile `LABEL` with the same key, and a `--label` you pass wins over them. `created` is not set, so the labels do not change the image on every rebuild.
@@ -84,7 +99,7 @@ The package installs `docker-credential-ce-forgejo` next to `ce`. `ce` puts that
84
99
 
85
100
  ## Compatibility
86
101
 
87
- These stay the same from release to release, and [test/contract.test.ts](test/contract.test.ts) checks them by running the built package under Node and Bun:
102
+ These behave the same under Node and Bun, and [test/contract.test.ts](test/contract.test.ts) checks them by running the built package under both. A release that changes one says so here:
88
103
 
89
104
  - commands, flags and the environment variables `login` reads;
90
105
  - the arguments the engine receives after tag and cache rewriting, and its exit code, signals and PID (`ce` replaces itself with the engine);
@@ -94,6 +109,8 @@ These stay the same from release to release, and [test/contract.test.ts](test/co
94
109
 
95
110
  The wording of messages is not part of that and may change.
96
111
 
112
+ In 0.0.3, the `--cache-from` that `--cache` makes no longer has a gha `mode=`, which only applies to export, and a `--cache-from` given twice is passed once.
113
+
97
114
  Compared with the Python version:
98
115
 
99
116
  - The state file is locked with `ce-forgejo-credentials.json.pidlock`, holding the holder's PID and hostname, rather than `flock`, which Node does not have. A lock whose holder died is broken right away if the PID is gone on the same host, otherwise after two minutes. It does not exclude a Python `ce` using the same Docker config at the same moment.
package/dist/ce.js CHANGED
@@ -298,7 +298,7 @@ class CiMetadata {
298
298
  const ref = this.#parsedRef();
299
299
  if (ref.kind !== "branch")
300
300
  return;
301
- return ref.name === this.#defaultBranch() ? ref.name : undefined;
301
+ return ref.name === this.defaultBranch() ? ref.name : undefined;
302
302
  }
303
303
  case "revision":
304
304
  return this.#require("SHA", `{${name}}`);
@@ -359,11 +359,12 @@ class CiMetadata {
359
359
  const ref = this.#parsedRef();
360
360
  return ref.kind === "tag" ? SEMVER.exec(ref.name) : null;
361
361
  }
362
- #defaultBranch() {
363
- const repository = this.#payload().repository;
364
- const branch = isObject(repository) ? repository.default_branch : undefined;
365
- if (typeof branch !== "string" || !branch) {
366
- throw new CeError("ce: {default} needs repository.default_branch in the event file, which it does not have");
362
+ defaultBranch() {
363
+ const repository = this.#ciEnv("EVENT_PATH") ? this.#payload().repository : undefined;
364
+ const fromEvent = isObject(repository) ? repository.default_branch : undefined;
365
+ const branch = typeof fromEvent === "string" && fromEvent || nonEmpty(this.#env.DEFAULT_BRANCH);
366
+ if (!branch) {
367
+ throw new CeError("ce: {default} needs repository.default_branch in the event file or DEFAULT_BRANCH, and neither is set");
367
368
  }
368
369
  return branch;
369
370
  }
@@ -383,6 +384,21 @@ class CiMetadata {
383
384
  return payload;
384
385
  }
385
386
  }
387
+ var ON_DEFAULT_BRANCH = new Set(["ref", "branch", "default"]);
388
+ var UNKNOWN_ON_DEFAULT_BRANCH = new Set(["revision", "sha"]);
389
+ function onDefaultBranch(vars, branch) {
390
+ return {
391
+ known: (name) => vars.known(name),
392
+ get(name) {
393
+ if (ON_DEFAULT_BRANCH.has(name))
394
+ return branch;
395
+ if (UNKNOWN_ON_DEFAULT_BRANCH.has(name)) {
396
+ throw new CeError(`ce: the default branch's cache scope cannot use {${name}}: this run does not know its commit`);
397
+ }
398
+ return name === "image" || name.startsWith("env.") ? vars.get(name) : undefined;
399
+ }
400
+ };
401
+ }
386
402
 
387
403
  // src/system.ts
388
404
  import { spawnSync } from "node:child_process";
@@ -505,6 +521,11 @@ function checkValue(template, vars, what) {
505
521
  }
506
522
  function expandValue(template, vars, encode, what) {
507
523
  checkValue(template, vars, what);
524
+ for (const part of parse(template, vars)) {
525
+ if ("name" in part && vars.get(part.name) === undefined) {
526
+ throw new CeError(`ce: {${part.name}} has no value here, in the ${what} ${JSON.stringify(template)}`);
527
+ }
528
+ }
508
529
  return expand(template, vars, encode);
509
530
  }
510
531
 
@@ -658,6 +679,7 @@ function ghaFields(value) {
658
679
  const type = fields?.findLast((field) => fieldKey(field) === "type");
659
680
  return type?.slice("type=".length) === "gha" ? fields : null;
660
681
  }
682
+ var isGhaCache = (value) => ghaFields(value) !== null;
661
683
  var scopeIndex = (fields) => fields.findLastIndex((field) => fieldKey(field) === "scope");
662
684
  function cacheScopeName(value) {
663
685
  const fields = ghaFields(value);
@@ -691,6 +713,12 @@ function normalizeCacheScope(value, cachePrefix) {
691
713
  fields[i] = `scope=${scope}`;
692
714
  return csvJoin(fields);
693
715
  }
716
+ function withoutCacheMode(value) {
717
+ const fields = ghaFields(value);
718
+ if (!fields?.some((field) => fieldKey(field) === "mode"))
719
+ return value;
720
+ return csvJoin(fields.filter((field) => fieldKey(field) !== "mode"));
721
+ }
694
722
 
695
723
  // src/build-args.ts
696
724
  function consumeValue(argv, flag) {
@@ -710,7 +738,7 @@ var SUMMARIZED = {
710
738
  "--build-arg": "buildArgs",
711
739
  "--label": "labels"
712
740
  };
713
- function parseBuildArgs(argv, tagPrefix, cachePrefix, { vars = new CiMetadata, labels = {} } = {}) {
741
+ function parseBuildArgs(argv, tagPrefix, cachePrefix, { vars = new CiMetadata, labels = {}, defaultBranch } = {}) {
714
742
  const args = [];
715
743
  const rawCacheFrom = [];
716
744
  const rawCacheTo = [];
@@ -764,7 +792,7 @@ function parseBuildArgs(argv, tagPrefix, cachePrefix, { vars = new CiMetadata, l
764
792
  rawCacheTo.push(attached ?? next());
765
793
  else if (name === "--cache") {
766
794
  const v = attached ?? next();
767
- rawCacheFrom.push(v);
795
+ rawCacheFrom.push(withoutCacheMode(v));
768
796
  rawCacheTo.push(v);
769
797
  } else if (Object.hasOwn(SUMMARIZED, name)) {
770
798
  const flag = name;
@@ -792,11 +820,17 @@ function parseBuildArgs(argv, tagPrefix, cachePrefix, { vars = new CiMetadata, l
792
820
  const added = Object.entries(labels).filter(([key]) => !ownKeys.has(key)).map(([key, value]) => `${key}=${value}`);
793
821
  args.unshift(...added.map((label) => `--label=${label}`));
794
822
  summary.labels.unshift(...added);
823
+ const prefixTemplate = cachePrefix;
795
824
  if (rawCacheFrom.length || rawCacheTo.length)
796
825
  cachePrefix = expandPrefix(cachePrefix, vars, "cache prefix");
797
826
  const toNames = new Set(rawCacheTo.map(cacheScopeName));
798
- for (const c of rawCacheFrom) {
799
- const rewritten = toNames.has(cacheScopeName(c)) ? normalizeCacheScope(c, cachePrefix) : c;
827
+ const cacheFrom = rawCacheFrom.map((c) => toNames.has(cacheScopeName(c)) ? normalizeCacheScope(c, cachePrefix) : c);
828
+ const ghaTo = rawCacheTo.filter(isGhaCache);
829
+ if (defaultBranch && ghaTo.length) {
830
+ const prefix = expandPrefix(prefixTemplate, onDefaultBranch(vars, defaultBranch()), "cache prefix");
831
+ cacheFrom.push(...ghaTo.map((c) => normalizeCacheScope(withoutCacheMode(c), prefix)));
832
+ }
833
+ for (const rewritten of new Set(cacheFrom)) {
800
834
  args.push(`--cache-from=${rewritten}`);
801
835
  summary.cacheFrom.push(rewritten);
802
836
  }
@@ -1163,17 +1197,46 @@ function envFlag(name) {
1163
1197
  return true;
1164
1198
  throw new CeError(`ce: ${name} must be true or false, not ${JSON.stringify(process.env[name])}`);
1165
1199
  }
1166
- async function runEngine(engine, argv, tagPrefix, cachePrefix, ociLabels) {
1200
+ function shellQuote(arg) {
1201
+ return /^[A-Za-z0-9_@%+:,./-][A-Za-z0-9_@%+=:,./-]*$/u.test(arg) ? arg : `'${arg.replaceAll("'", `'\\''`)}'`;
1202
+ }
1203
+ function engineCommand(engine, args) {
1204
+ if (engine === "docker" || !system.which("gosu"))
1205
+ return [engine, ...args];
1206
+ const user = process.env.USER ?? "";
1207
+ if (!user) {
1208
+ logError("ce: gosu is available but USER is not set; cannot drop privileges. Set USER or remove gosu from PATH.");
1209
+ return null;
1210
+ }
1211
+ return ["gosu", user, engine, ...args];
1212
+ }
1213
+ async function runEngine(engine, argv, tagPrefix, cachePrefix, options = {}) {
1214
+ const dryRun = options.dryRun ?? envFlag("CE_DRY_RUN");
1167
1215
  const command = findBuildCommand(engine, argv);
1168
1216
  let args = argv;
1169
1217
  let summary;
1170
1218
  if (command) {
1171
1219
  const vars = new CiMetadata;
1172
- const labels = !command.tagsOnly && (ociLabels ?? envFlag("OCI_LABELS")) ? vars.labels() : {};
1173
- const parsed = parseBuildArgs(argv.slice(command.start), tagPrefix, cachePrefix, { vars, labels });
1220
+ const labels = !command.tagsOnly && (options.ociLabels ?? envFlag("OCI_LABELS")) ? vars.labels() : {};
1221
+ const fromDefault = !command.tagsOnly && (options.cacheFromDefault ?? envFlag("CACHE_FROM_DEFAULT"));
1222
+ const parsed = parseBuildArgs(argv.slice(command.start), tagPrefix, cachePrefix, {
1223
+ vars,
1224
+ labels,
1225
+ defaultBranch: fromDefault ? () => vars.defaultBranch() : undefined
1226
+ });
1174
1227
  args = [...argv.slice(0, command.start), ...parsed.args];
1175
1228
  summary = parsed.summary;
1176
1229
  }
1230
+ if (dryRun) {
1231
+ const run = engineCommand(engine, args);
1232
+ if (!run)
1233
+ return 1;
1234
+ if (summary)
1235
+ printBuildSummary(engine, args, summary);
1236
+ io.stdout(`${run.map(shellQuote).join(" ")}
1237
+ `);
1238
+ return 0;
1239
+ }
1177
1240
  ensureHelperOnPath();
1178
1241
  if (!system.which(engine)) {
1179
1242
  logError(`Engine not found: ${engine}`);
@@ -1183,17 +1246,8 @@ async function runEngine(engine, argv, tagPrefix, cachePrefix, ociLabels) {
1183
1246
  return 1;
1184
1247
  if (summary)
1185
1248
  printBuildSummary(engine, args, summary);
1186
- if (engine === "docker")
1187
- return system.exec("docker", ["docker", ...args]);
1188
- if (system.which("gosu")) {
1189
- const user = process.env.USER ?? "";
1190
- if (!user) {
1191
- logError("ce: gosu is available but USER is not set; cannot drop privileges. Set USER or remove gosu from PATH.");
1192
- return 1;
1193
- }
1194
- return system.exec("gosu", ["gosu", user, engine, ...args]);
1195
- }
1196
- return system.exec(engine, [engine, ...args]);
1249
+ const run = engineCommand(engine, args);
1250
+ return run ? system.exec(run[0], run) : 1;
1197
1251
  }
1198
1252
 
1199
1253
  // src/login.ts
@@ -1203,14 +1257,18 @@ function registryGroups() {
1203
1257
  const suffixed = Object.keys(process.env).filter((name) => name.startsWith(prefix) && name.length > prefix.length).sort().map((name) => name.slice(prefix.length));
1204
1258
  return [...process.env.REGISTRY_HOST ? [""] : [], ...suffixed];
1205
1259
  }
1260
+ function unsetOrEmpty(name) {
1261
+ return `${name} is ${process.env[name] === undefined ? "unset" : "empty"}`;
1262
+ }
1206
1263
  function oidcConfig() {
1207
1264
  const audience = process.env.AUD ?? "";
1208
1265
  if (!audience)
1209
1266
  return null;
1210
1267
  const requestUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL ?? "";
1211
1268
  const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN ?? "";
1212
- if (!(requestUrl && requestToken)) {
1213
- return "Forgejo OIDC login config is incomplete: AUD requires ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN";
1269
+ const missing = ["ACTIONS_ID_TOKEN_REQUEST_URL", "ACTIONS_ID_TOKEN_REQUEST_TOKEN"].filter((name) => !process.env[name]);
1270
+ if (missing.length) {
1271
+ return `Forgejo OIDC login config is incomplete: AUD is set, but ${missing.map(unsetOrEmpty).join("; ")}`;
1214
1272
  }
1215
1273
  try {
1216
1274
  assertAbsoluteHttpUrl(requestUrl, "must be an absolute HTTP(S) URL");
@@ -1257,36 +1315,46 @@ async function login() {
1257
1315
  let failed = false;
1258
1316
  for (const group of groups) {
1259
1317
  const suffix = group ? `__${group}` : "";
1260
- const host = process.env[`REGISTRY_HOST${suffix}`] ?? "";
1261
- const user = process.env[`REGISTRY_USER${suffix}`] ?? "";
1262
- const password = process.env[`REGISTRY_PASSWORD${suffix}`] ?? "";
1318
+ const hostName = `REGISTRY_HOST${suffix}`;
1319
+ const userName = `REGISTRY_USER${suffix}`;
1320
+ const passwordName = `REGISTRY_PASSWORD${suffix}`;
1321
+ const host = process.env[hostName] ?? "";
1322
+ const user = process.env[userName] ?? "";
1323
+ const password = process.env[passwordName] ?? "";
1263
1324
  const label = group || "default";
1264
- const incomplete = `Login config for '${label}' is incomplete: host/user/password not all set`;
1265
- if (!(host && user)) {
1266
- logError(incomplete);
1267
- failed = true;
1268
- continue;
1269
- }
1270
- for (const rawRegistry of host.split(/\s+/u).filter(Boolean)) {
1271
- let authority;
1272
- let registry;
1325
+ const hosts = host.split(/\s+/u).filter(Boolean);
1326
+ const registries = [];
1327
+ for (const rawRegistry of hosts) {
1273
1328
  try {
1274
- authority = urlAuthority(rawRegistry);
1275
- registry = registryServer(rawRegistry);
1329
+ const authority = urlAuthority(rawRegistry);
1330
+ const registry = registryServer(rawRegistry);
1331
+ registries.push({ registry, useOidc: oidc !== null && sameAuthority(authority, oidc.authority) });
1276
1332
  } catch (exc) {
1277
1333
  if (!(exc instanceof CeError))
1278
1334
  throw exc;
1279
- logError(`Invalid registry host for '${label}' (${rawRegistry}): ${exc.message}`);
1335
+ logError(rawRegistry.includes("@") ? `Invalid registry host for '${label}' in ${hostName}, not shown as it has an "@" and may hold a password` : `Invalid registry host for '${label}' (${rawRegistry}): ${exc.message}`);
1280
1336
  failed = true;
1281
- continue;
1282
- }
1283
- const useOidc = oidc !== null && sameAuthority(authority, oidc.authority);
1284
- if (!useOidc && !password) {
1285
- logError(incomplete);
1286
- failed = true;
1287
- continue;
1288
1337
  }
1289
- targets.push({ registry, label, user, password, useOidc });
1338
+ }
1339
+ const missing = [];
1340
+ if (!host)
1341
+ missing.push(unsetOrEmpty(hostName));
1342
+ else if (!hosts.length)
1343
+ missing.push(`${hostName} has no registry in it`);
1344
+ if (!user)
1345
+ missing.push(unsetOrEmpty(userName));
1346
+ const needPassword = registries.filter((r) => !r.useOidc).map((r) => r.registry);
1347
+ if (!password && needPassword.length)
1348
+ missing.push(`${unsetOrEmpty(passwordName)}, which ${needPassword.join(", ")} needs`);
1349
+ if (missing.length) {
1350
+ logError(`Login config for '${label}' is incomplete: ${missing.join("; ")}`);
1351
+ failed = true;
1352
+ }
1353
+ if (!user)
1354
+ continue;
1355
+ for (const { registry, useOidc } of registries) {
1356
+ if (useOidc || password)
1357
+ targets.push({ registry, label, user, password, useOidc });
1290
1358
  }
1291
1359
  }
1292
1360
  let jwt = null;
@@ -1332,7 +1400,8 @@ async function login() {
1332
1400
 
1333
1401
  // src/cli.ts
1334
1402
  function helpText() {
1335
- return `Usage: ${io.argv0()} [--tag-prefix=<prefix>] [--cache-prefix=<prefix>] [--oci-labels] [command|engine] [args]
1403
+ return `Usage: ${io.argv0()} [--tag-prefix=<prefix>] [--cache-prefix=<prefix>] [--oci-labels]
1404
+ [--cache-from-default] [--dry-run] [command|engine] [args]
1336
1405
 
1337
1406
  Options:
1338
1407
  --tag-prefix=<prefix>
@@ -1348,10 +1417,21 @@ Options:
1348
1417
  Also available via CACHE_PREFIX environment variable.
1349
1418
  --cache=<spec> Shorthand for passing <spec> to both --cache-from and
1350
1419
  --cache-to. Supports the same forms as --cache-from/--cache-to.
1420
+ A gha cache-from made this way leaves out mode=, which
1421
+ only applies to export. The same cache-from given twice
1422
+ is passed once.
1423
+ --cache-from-default
1424
+ Also read each gha --cache-to from its scope on the
1425
+ default branch, from the cache prefix as it is there,
1426
+ so a new branch or PR starts from its cache.
1427
+ Also available via CACHE_FROM_DEFAULT=true.
1351
1428
  --oci-labels Add org.opencontainers.image.* labels (source, revision,
1352
1429
  version, title, description) from the Forgejo Actions
1353
1430
  run to builds. A --label with the same key wins.
1354
1431
  Also available via OCI_LABELS=true.
1432
+ --dry-run Print the build summary and the engine command, and
1433
+ exit without running it. Template errors still exit 2.
1434
+ Also available via CE_DRY_RUN=true.
1355
1435
 
1356
1436
  Placeholders, in build tags and in the tag and cache prefixes:
1357
1437
  {name} The value of name; an error if it has none in this run.
@@ -1361,7 +1441,8 @@ Placeholders, in build tags and in the tag and cache prefixes:
1361
1441
  {ref} Branch name, pr-<number>, or tag name
1362
1442
  {branch} {pr} {tag}
1363
1443
  Set only when building that kind of ref
1364
- {default} Set only when building the default branch
1444
+ {default} Set only when building the default branch, named by
1445
+ the event file or else DEFAULT_BRANCH
1365
1446
  {revision} {sha} The commit, in full and as 7 characters
1366
1447
  {version} {major} {minor} {patch}
1367
1448
  From a SemVer tag such as v1.2.3 (v optional)
@@ -1407,7 +1488,7 @@ Environment variables (used by login):
1407
1488
  `;
1408
1489
  }
1409
1490
  async function main(argv) {
1410
- let ociLabels;
1491
+ const options = {};
1411
1492
  let tagPrefix = process.env.TAG_PREFIX ?? "";
1412
1493
  let cachePrefix = process.env.CACHE_PREFIX ?? process.env.TAG_PREFIX ?? "";
1413
1494
  for (;; ) {
@@ -1421,7 +1502,11 @@ async function main(argv) {
1421
1502
  else if (a?.startsWith("--cache-prefix="))
1422
1503
  [cachePrefix, argv] = [a.slice("--cache-prefix=".length), argv.slice(1)];
1423
1504
  else if (a === "--oci-labels")
1424
- [ociLabels, argv] = [true, argv.slice(1)];
1505
+ [options.ociLabels, argv] = [true, argv.slice(1)];
1506
+ else if (a === "--cache-from-default")
1507
+ [options.cacheFromDefault, argv] = [true, argv.slice(1)];
1508
+ else if (a === "--dry-run")
1509
+ [options.dryRun, argv] = [true, argv.slice(1)];
1425
1510
  else
1426
1511
  break;
1427
1512
  }
@@ -1435,7 +1520,7 @@ async function main(argv) {
1435
1520
  if (command === "credential-helper")
1436
1521
  return credentialHelper(rest);
1437
1522
  if (isEngine(command))
1438
- return runEngine(command, rest, tagPrefix, cachePrefix, ociLabels);
1523
+ return runEngine(command, rest, tagPrefix, cachePrefix, options);
1439
1524
  io.stderr(helpText());
1440
1525
  return 1;
1441
1526
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@izykitten/ce",
3
- "version": "0.0.3-dev.0",
3
+ "version": "0.0.3-dev.2",
4
4
  "description": "Container engine wrapper: tag/cache rewriting for docker, podman and buildah, registry login, and a Forgejo OIDC Docker credential helper",
5
5
  "type": "module",
6
6
  "license": "BSD-3-Clause",