@izykitten/ce 0.0.3-dev.0 → 0.0.3-dev.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 +10 -2
- package/dist/ce.js +51 -30
- 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,
|
|
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,14 @@ 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. To also read the default branch's cache, add it as a `--cache-from`. ce only prefixes a `--cache-from` whose scope matches a `--cache-to`, so this one is passed on unchanged and only read:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
--cache type=gha,mode=max --cache-from type=gha,scope=main
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
PR 3 then reads `pr-3` and `main` and writes only `pr-3`; main reads its own cache a second time. With a scope of its own, such as `--cache type=gha,mode=max,scope=js`, the cache to read is `main-js`. This only picks what is read: whether it hits depends on what main last wrote.
|
|
74
|
+
|
|
67
75
|
A release on a SemVer tag, with the version aliases only for stable releases:
|
|
68
76
|
|
|
69
77
|
```sh
|
package/dist/ce.js
CHANGED
|
@@ -360,10 +360,11 @@ class CiMetadata {
|
|
|
360
360
|
return ref.kind === "tag" ? SEMVER.exec(ref.name) : null;
|
|
361
361
|
}
|
|
362
362
|
#defaultBranch() {
|
|
363
|
-
const repository = this.#payload().repository;
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
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
|
}
|
|
@@ -505,6 +506,11 @@ function checkValue(template, vars, what) {
|
|
|
505
506
|
}
|
|
506
507
|
function expandValue(template, vars, encode, what) {
|
|
507
508
|
checkValue(template, vars, what);
|
|
509
|
+
for (const part of parse(template, vars)) {
|
|
510
|
+
if ("name" in part && vars.get(part.name) === undefined) {
|
|
511
|
+
throw new CeError(`ce: {${part.name}} has no value here, in the ${what} ${JSON.stringify(template)}`);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
508
514
|
return expand(template, vars, encode);
|
|
509
515
|
}
|
|
510
516
|
|
|
@@ -1203,14 +1209,18 @@ function registryGroups() {
|
|
|
1203
1209
|
const suffixed = Object.keys(process.env).filter((name) => name.startsWith(prefix) && name.length > prefix.length).sort().map((name) => name.slice(prefix.length));
|
|
1204
1210
|
return [...process.env.REGISTRY_HOST ? [""] : [], ...suffixed];
|
|
1205
1211
|
}
|
|
1212
|
+
function unsetOrEmpty(name) {
|
|
1213
|
+
return `${name} is ${process.env[name] === undefined ? "unset" : "empty"}`;
|
|
1214
|
+
}
|
|
1206
1215
|
function oidcConfig() {
|
|
1207
1216
|
const audience = process.env.AUD ?? "";
|
|
1208
1217
|
if (!audience)
|
|
1209
1218
|
return null;
|
|
1210
1219
|
const requestUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL ?? "";
|
|
1211
1220
|
const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN ?? "";
|
|
1212
|
-
|
|
1213
|
-
|
|
1221
|
+
const missing = ["ACTIONS_ID_TOKEN_REQUEST_URL", "ACTIONS_ID_TOKEN_REQUEST_TOKEN"].filter((name) => !process.env[name]);
|
|
1222
|
+
if (missing.length) {
|
|
1223
|
+
return `Forgejo OIDC login config is incomplete: AUD is set, but ${missing.map(unsetOrEmpty).join("; ")}`;
|
|
1214
1224
|
}
|
|
1215
1225
|
try {
|
|
1216
1226
|
assertAbsoluteHttpUrl(requestUrl, "must be an absolute HTTP(S) URL");
|
|
@@ -1257,36 +1267,46 @@ async function login() {
|
|
|
1257
1267
|
let failed = false;
|
|
1258
1268
|
for (const group of groups) {
|
|
1259
1269
|
const suffix = group ? `__${group}` : "";
|
|
1260
|
-
const
|
|
1261
|
-
const
|
|
1262
|
-
const
|
|
1270
|
+
const hostName = `REGISTRY_HOST${suffix}`;
|
|
1271
|
+
const userName = `REGISTRY_USER${suffix}`;
|
|
1272
|
+
const passwordName = `REGISTRY_PASSWORD${suffix}`;
|
|
1273
|
+
const host = process.env[hostName] ?? "";
|
|
1274
|
+
const user = process.env[userName] ?? "";
|
|
1275
|
+
const password = process.env[passwordName] ?? "";
|
|
1263
1276
|
const label = group || "default";
|
|
1264
|
-
const
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
failed = true;
|
|
1268
|
-
continue;
|
|
1269
|
-
}
|
|
1270
|
-
for (const rawRegistry of host.split(/\s+/u).filter(Boolean)) {
|
|
1271
|
-
let authority;
|
|
1272
|
-
let registry;
|
|
1277
|
+
const hosts = host.split(/\s+/u).filter(Boolean);
|
|
1278
|
+
const registries = [];
|
|
1279
|
+
for (const rawRegistry of hosts) {
|
|
1273
1280
|
try {
|
|
1274
|
-
authority = urlAuthority(rawRegistry);
|
|
1275
|
-
registry = registryServer(rawRegistry);
|
|
1281
|
+
const authority = urlAuthority(rawRegistry);
|
|
1282
|
+
const registry = registryServer(rawRegistry);
|
|
1283
|
+
registries.push({ registry, useOidc: oidc !== null && sameAuthority(authority, oidc.authority) });
|
|
1276
1284
|
} catch (exc) {
|
|
1277
1285
|
if (!(exc instanceof CeError))
|
|
1278
1286
|
throw exc;
|
|
1279
|
-
logError(`Invalid registry host for '${label}' (${rawRegistry}): ${exc.message}`);
|
|
1280
|
-
failed = true;
|
|
1281
|
-
continue;
|
|
1282
|
-
}
|
|
1283
|
-
const useOidc = oidc !== null && sameAuthority(authority, oidc.authority);
|
|
1284
|
-
if (!useOidc && !password) {
|
|
1285
|
-
logError(incomplete);
|
|
1287
|
+
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}`);
|
|
1286
1288
|
failed = true;
|
|
1287
|
-
continue;
|
|
1288
1289
|
}
|
|
1289
|
-
|
|
1290
|
+
}
|
|
1291
|
+
const missing = [];
|
|
1292
|
+
if (!host)
|
|
1293
|
+
missing.push(unsetOrEmpty(hostName));
|
|
1294
|
+
else if (!hosts.length)
|
|
1295
|
+
missing.push(`${hostName} has no registry in it`);
|
|
1296
|
+
if (!user)
|
|
1297
|
+
missing.push(unsetOrEmpty(userName));
|
|
1298
|
+
const needPassword = registries.filter((r) => !r.useOidc).map((r) => r.registry);
|
|
1299
|
+
if (!password && needPassword.length)
|
|
1300
|
+
missing.push(`${unsetOrEmpty(passwordName)}, which ${needPassword.join(", ")} needs`);
|
|
1301
|
+
if (missing.length) {
|
|
1302
|
+
logError(`Login config for '${label}' is incomplete: ${missing.join("; ")}`);
|
|
1303
|
+
failed = true;
|
|
1304
|
+
}
|
|
1305
|
+
if (!user)
|
|
1306
|
+
continue;
|
|
1307
|
+
for (const { registry, useOidc } of registries) {
|
|
1308
|
+
if (useOidc || password)
|
|
1309
|
+
targets.push({ registry, label, user, password, useOidc });
|
|
1290
1310
|
}
|
|
1291
1311
|
}
|
|
1292
1312
|
let jwt = null;
|
|
@@ -1361,7 +1381,8 @@ Placeholders, in build tags and in the tag and cache prefixes:
|
|
|
1361
1381
|
{ref} Branch name, pr-<number>, or tag name
|
|
1362
1382
|
{branch} {pr} {tag}
|
|
1363
1383
|
Set only when building that kind of ref
|
|
1364
|
-
{default} Set only when building the default branch
|
|
1384
|
+
{default} Set only when building the default branch, named by
|
|
1385
|
+
the event file or else DEFAULT_BRANCH
|
|
1365
1386
|
{revision} {sha} The commit, in full and as 7 characters
|
|
1366
1387
|
{version} {major} {minor} {patch}
|
|
1367
1388
|
From a SemVer tag such as v1.2.3 (v optional)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@izykitten/ce",
|
|
3
|
-
"version": "0.0.3-dev.
|
|
3
|
+
"version": "0.0.3-dev.1",
|
|
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",
|