@izykitten/ce 0.0.1 → 0.0.3-dev.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/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ Copyright (c) 2026 izy
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4
+
5
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6
+
7
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8
+
9
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10
+
11
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md CHANGED
@@ -2,16 +2,16 @@
2
2
 
3
3
  Container engine wrapper for CI: registry login (including Forgejo OIDC), tag and cache rewriting for `docker`, `podman` and `buildah`, and a Docker credential helper that refreshes the Forgejo token.
4
4
 
5
- A Bun port of the Python `ce` script from the `code.izy.sh/library/runner` image. Same commands, flags, environment variables, messages, exit codes and on-disk files, so a workflow only has to swap `ce` for `bunx`. Any job with Bun and an engine CLI can use it; it no longer needs the runner image.
5
+ Started as a port of the Python `ce` script from the `code.izy.sh/library/runner` image, with the same commands, flags, environment variables and exit codes, so a workflow only has to swap `ce` for `npx` or `bunx`. It runs on Node (22.15 or later) and Bun. Any job with Node or Bun and an engine CLI can use it; it no longer needs the runner image.
6
6
 
7
7
  ## Usage
8
8
 
9
9
  ```sh
10
- bunx @izykitten/ce login
11
- bunx @izykitten/ce docker buildx build --push --cache type=gha,mode=max -t "$SERVER/$REPO:$REF,latest" .
10
+ npx @izykitten/ce login
11
+ npx @izykitten/ce docker buildx build --push --cache type=gha,mode=max -t "$SERVER/$REPO:$REF,latest" .
12
12
  ```
13
13
 
14
- When installed as a dependency, call it as `ce` (`bunx ce …`). Run `ce` with no arguments for the full help, including every environment variable `login` reads.
14
+ `bunx @izykitten/ce …` works the same. The scripts run under `node`; `bun` runs them too when invoked as `bun node_modules/.bin/ce …` or through `bunx --bun`. When installed as a dependency, call it as `ce` (`npx ce …`). Run `ce` with no arguments for the full help, including every environment variable `login` reads.
15
15
 
16
16
  In a Forgejo workflow:
17
17
 
@@ -24,20 +24,81 @@ In a Forgejo workflow:
24
24
  REGISTRY_HOST: ${{ forgejo.server_url }}
25
25
  run: bunx @izykitten/ce login
26
26
  - name: Container Build & Push
27
- run: bunx @izykitten/ce docker buildx build --push -t ${{ forgejo.server_url }}/${{ forgejo.repository }}:${{ forgejo.ref_name }} .
27
+ run: bunx @izykitten/ce docker buildx build --push -t '{image}:{ref},{?default}latest' .
28
28
  ```
29
29
 
30
+ Only build commands are rewritten: `build`, `buildx build` and `image build` for Docker and Podman, `build` and `bud` for Buildah, and the tags of `docker buildx imagetools create`. Everything else, such as `docker run -t`, reaches the engine unchanged.
31
+
32
+ ## Tags from the Forgejo run
33
+
34
+ Tags and the `TAG_PREFIX`/`CACHE_PREFIX` values can use placeholders, filled from the Forgejo Actions environment and its event file. ce makes no API calls and does not look at the checkout.
35
+
36
+ | Placeholder | Value |
37
+ |---|---|
38
+ | `{image}` | registry host and repository, lowercased, e.g. `code.izy.sh/library/gateway` |
39
+ | `{ref}` | the branch name, `pr-<number>` for a pull request, or the tag name |
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 |
42
+ | `{revision}`, `{sha}` | the commit, in full and as its first 7 characters |
43
+ | `{version}`, `{major}`, `{minor}`, `{patch}` | from a SemVer tag such as `v1.2.3` or `1.2.3-rc.1` |
44
+ | `{stable}` | set only for a SemVer tag without a pre-release |
45
+ | `{env.NAME}` | the environment variable `NAME`, e.g. a value from an earlier step |
46
+
47
+ `{name}` must have a value (an empty variable has none): if it has none in this run, ce fails with exit 2 before anything runs. To leave a tag out instead, guard it with `{?name}`, which expands to nothing and keeps the tag only when `name` has a value. Every guard in a tag has to pass, and a tag's other values are only needed when it is kept. Names are checked in every tag, so a typo fails even in one a guard leaves out. If the guards leave out every tag, ce fails rather than build an untagged image.
48
+
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
+
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.
52
+
53
+ Put templates in single quotes: `?` is a glob character in zsh.
54
+
55
+ A branch, PR and default-branch build:
56
+
57
+ ```yaml
58
+ - name: Container Build & Push
59
+ env:
60
+ CACHE_PREFIX: '{ref}'
61
+ CADDY_VERSION: ${{ steps.caddy.outputs.version }}
62
+ run: >
63
+ bunx @izykitten/ce docker buildx build --push --cache type=gha,mode=max
64
+ -t '{image}:{ref},{?default}latest,{?default}caddy-{env.CADDY_VERSION}' .
65
+ ```
66
+
67
+ A release on a SemVer tag, with the version aliases only for stable releases:
68
+
69
+ ```sh
70
+ -t '{image}:{ref},{?version}{version},{?stable}{major}.{minor},{?stable}{major},sha-{sha}'
71
+ ```
72
+
73
+ `{stable}` does not mean newest: rebuilding an older release moves its aliases back to it.
74
+
75
+ ### OCI labels
76
+
77
+ 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.
78
+
30
79
  ## The credential helper
31
80
 
32
81
  With `AUD` set (and `enable-openid-connect: true` on the job), `ce login` logs in to the Forgejo registry with a short-lived OIDC token. It then writes `"credHelpers": {"<host>": "ce-forgejo"}` to the Docker config. From then on, Docker gets credentials for that host by running `docker-credential-ce-forgejo get`, which returns the saved token or fetches a fresh one when it is within five minutes of expiry. That is what lets a long build still push.
33
82
 
34
83
  The package installs `docker-credential-ce-forgejo` next to `ce`. `ce` puts that directory on `PATH` for the engines it starts, so `ce docker …` finds the helper. A plain `docker` command in a later step does not get that `PATH`, so run pushes to an OIDC registry through `ce`.
35
84
 
36
- ## Differences from the Python version
85
+ ## Compatibility
86
+
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:
88
+
89
+ - commands, flags and the environment variables `login` reads;
90
+ - the arguments the engine receives after tag and cache rewriting, and its exit code, signals and PID (`ce` replaces itself with the engine);
91
+ - `ce`'s own exit codes: 0 for help, 1 for a failure, 2 for bad arguments;
92
+ - the order and arguments of `login`'s engine calls, with the password on stdin;
93
+ - the credential-helper protocol, and the Docker config and `ce-forgejo-credentials.json` files `ce` writes.
94
+
95
+ The wording of messages is not part of that and may change.
96
+
97
+ Compared with the Python version:
37
98
 
38
- - Replacing the process with the engine (`execvp`) and the state-file lock (`flock`) go through `bun:ffi` to libc, since Bun has no API for either. Tested on glibc (Debian) and musl (Alpine). It takes the same kernel lock as the Python version, so both can share a Docker config.
39
- - The `Usage:` line shows the absolute path of the script, because Bun reports it that way.
40
- - Error text that comes from the system, network or JSON parser is Bun's wording, not Python's (for example `ENOENT: no such file or directory` rather than `[Errno 2] No such file or directory`). Messages written by `ce` itself are unchanged.
99
+ - 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.
100
+ - Replacing the process with the engine uses `process.execve`. Node aborts rather than report an `execve` failure, so `ce` first checks that the engine is an executable file on `PATH`.
101
+ - Messages are worded differently in places, and the `Usage:` line shows the absolute path of the script.
41
102
  - `docker-credential-ce-forgejo` runs the helper code directly instead of `exec ce credential-helper`, so `ce` does not have to be on `PATH`.
42
103
  - JSON files keep the same structure and sorted keys. Numbers are written as JavaScript writes them (`4102444800` rather than `4102444800.0`), and non-ASCII characters are not `\u`-escaped.
43
104
 
@@ -45,6 +106,7 @@ The package installs `docker-credential-ce-forgejo` next to `ce`. `ce` puts that
45
106
 
46
107
  ```sh
47
108
  bun install
48
- bun test # the ported test_ce.py suite plus process tests for exec and flock
109
+ bun test # builds dist/, then unit tests plus the contract and process tests under Node and Bun
49
110
  bun check
111
+ bun run build # dist/ce.js, which the bin scripts load; npm pack runs it
50
112
  ```
package/bin/ce CHANGED
@@ -1,6 +1,6 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env node
2
2
  import path from "node:path";
3
- import { entry } from "../src/ce.ts";
3
+ import { entry } from "../dist/ce.js";
4
4
 
5
5
  // The wrapper is only meaningful when invoked as `ce`; reject any other basename.
6
6
  const self = path.basename(process.argv[1] ?? "");
@@ -1,5 +1,5 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env node
2
2
  // Docker runs `docker-credential-<suffix>` for `"credHelpers": {"<host>": "ce-forgejo"}`.
3
- import { entry } from "../src/ce.ts";
3
+ import { entry } from "../dist/ce.js";
4
4
 
5
5
  await entry(["credential-helper", ...process.argv.slice(2)]);