Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ uv run poe test tests/test_boot.py::test_execlineb_installed # Single test
```bash
uv run poe package # Create dist/ignity.tar.gz tarball from src/
uv run poe build # Static (musl) execline/s6/s6-portable-utils + src/ into dist/ignity, in a container
uv run poe package:deb # Also package:rpm, package:apk (Linux only): nfpm, dist/ignity-<version>-linux-<arch>.<format>
uv run poe package:deb # Also package:rpm, package:apk (Linux only): nfpm, dist/ignity-{core,utils}-<version>-linux-<arch>.<format>
IGNITY_INSTALL=package DOCKER_BASE_IMAGE=alpine:3 uv run poe test # Suite against the package in dist/
```

**Releasing** (GitHub Actions, workflow_dispatch, same flow as techcode-io/terranova):
`Pre release` (version) opens a bump PR (`release:pre`) -> `Release` (next `x.y.z-dev`) reuses CI, tags `x.y.z` (no `v`), attaches the packages (`release`) and opens the next iteration PR (`release:post`). The version lives in `pyproject.toml` and `distributions/packages/nfpm.{amd64,arm64}.yaml`.
`Pre release` (version) opens a bump PR (`release:pre`) -> `Release` (next `x.y.z-dev`) reuses CI, tags `x.y.z` (no `v`), attaches the packages (`release`) and opens the next iteration PR (`release:post`). The version lives in `pyproject.toml` only (passed to nfpm as `VERSION`, with `ARCH`).

All commands require:
- uv (modern Python package manager)
Expand Down Expand Up @@ -88,7 +88,7 @@ Containers of a pod share files through a user-mounted volume. No env is handed
**Installation:**
- `/usr/src/install-ignity.sh` — Compiles and installs s6, execline, skalibs from source (Debian/Ubuntu). Its `readonly *_VERSION`/`*_URL` are the single source of truth for the skarnet versions, also read by `distributions/build/build-ignity.sh` and bumped by `poe project:upgrade`
- execline, s6 and s6-portable-utils live in `/usr/lib/ignity/{bin,libexec}` (both installs, built with `--enable-absolute-paths`), never in `/bin`. `/init` puts `/usr/lib/ignity/bin` first in PATH via `/usr/lib/ignity/libexec/with-ignity-path`; entry points that can run outside /init (`preboot`, `fix-perms`, `gen-accounts`, `load-envfile`, `with-retries`) start with it and find the other tools through the caller's PATH. Ignity's own scripts use `#!/usr/lib/ignity/bin/execlineb`; `/bin/execlineb` is only a compat link for user scripts, created when absent
- Linux packages (deb/rpm/apk, amd64/arm64): `distributions/build/` builds the bundle (static binaries), `distributions/packages/nfpm.<arch>.yaml` packages it, `postinstall.sh`/`preremove.sh` manage the `/bin/execlineb` link. No Conflicts/Provides with the distribution's execline/s6
- Linux packages (deb/rpm/apk, amd64/arm64): `distributions/build/` builds the bundle (static binaries), `distributions/packages/nfpm.{core,utils}.yaml` package it in two: `ignity-core` (all but fix-perms, depends on the virtual `ignity-utils-impl`) and `ignity-utils` (execline `fix-perms`, provides `ignity-utils-impl`; techcode-io/ignity-utils-rs is the Rust alternative providing it too). Both conflict with/replace the `ignity` package of <= 0.1.0. `postinstall.sh`/`preremove.sh` manage the `/bin/execlineb` link (also rpm posttrans, as the obsoleted `ignity` preun runs after `%post`). No Conflicts/Provides with the distribution's execline/s6

## Container Configuration (User-Provided)

Expand Down
28 changes: 20 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ uv run poe package:apk
```

- `build` compiles static (musl) `execline`, `s6` and `s6-portable-utils` in a container, into `dist/ignity`.
- `package:<format>` packages it with [nfpm](https://nfpm.goreleaser.com) into `dist/ignity-<version>-linux-<arch>.<format>`.
- `package:<format>` packages it with [nfpm](https://nfpm.goreleaser.com) into `dist/ignity-core-<version>-linux-<arch>.<format>` and `dist/ignity-utils-<version>-linux-<arch>.<format>`.
- To run the tests with ignity installed from these packages instead of from source:

```bash
Expand Down Expand Up @@ -133,35 +133,47 @@ IGNITY_INSTALL=package DOCKER_BASE_IMAGE=alpine:3 uv run poe test
- Each [release](https://github.com/techcode-io/ignity/releases) ships `.deb`, `.rpm` and `.apk` packages for `amd64` and `arm64`.
- They bundle static builds of `execline`, `s6` and `s6-portable-utils`: nothing is compiled in your image.
- They install them in `/usr/lib/ignity` (see "Where ignity installs its binaries"): nothing conflicts with the packages of the distribution.
- ignity comes in two packages, to install together:
- `ignity-core`: the boot stages, `execline`, `s6`, `s6-portable-utils` and the utilities, except `fix-perms`.
- `ignity-utils`: the execline implementation of `fix-perms`.

```Dockerfile
# Debian / Ubuntu
ARG IGNITY_VERSION=0.1.0
ARG TARGETARCH
ADD https://github.com/techcode-io/ignity/releases/download/${IGNITY_VERSION}/ignity-${IGNITY_VERSION}-linux-${TARGETARCH}.deb /tmp/ignity.deb
ADD https://github.com/techcode-io/ignity/releases/download/${IGNITY_VERSION}/ignity-core-${IGNITY_VERSION}-linux-${TARGETARCH}.deb \
https://github.com/techcode-io/ignity/releases/download/${IGNITY_VERSION}/ignity-utils-${IGNITY_VERSION}-linux-${TARGETARCH}.deb \
/tmp/ignity/
RUN apt-get update \
&& apt-get install -y --no-install-recommends /tmp/ignity.deb \
&& rm -rf /tmp/ignity.deb /var/lib/apt/lists/*
&& apt-get install -y --no-install-recommends /tmp/ignity/*.deb \
&& rm -rf /tmp/ignity /var/lib/apt/lists/*
```

```Dockerfile
# Fedora / RHEL / UBI
ARG IGNITY_VERSION=0.1.0
ARG TARGETARCH
ADD https://github.com/techcode-io/ignity/releases/download/${IGNITY_VERSION}/ignity-${IGNITY_VERSION}-linux-${TARGETARCH}.rpm /tmp/ignity.rpm
RUN dnf install -y /tmp/ignity.rpm && dnf clean all && rm -f /tmp/ignity.rpm
ADD https://github.com/techcode-io/ignity/releases/download/${IGNITY_VERSION}/ignity-core-${IGNITY_VERSION}-linux-${TARGETARCH}.rpm \
https://github.com/techcode-io/ignity/releases/download/${IGNITY_VERSION}/ignity-utils-${IGNITY_VERSION}-linux-${TARGETARCH}.rpm \
/tmp/ignity/
RUN dnf install -y /tmp/ignity/*.rpm && dnf clean all && rm -rf /tmp/ignity
```

```Dockerfile
# Alpine (the packages aren't signed, their provenance is attested instead)
ARG IGNITY_VERSION=0.1.0
ARG TARGETARCH
ADD https://github.com/techcode-io/ignity/releases/download/${IGNITY_VERSION}/ignity-${IGNITY_VERSION}-linux-${TARGETARCH}.apk /tmp/ignity.apk
RUN apk add --no-cache --allow-untrusted /tmp/ignity.apk && rm -f /tmp/ignity.apk
ADD https://github.com/techcode-io/ignity/releases/download/${IGNITY_VERSION}/ignity-core-${IGNITY_VERSION}-linux-${TARGETARCH}.apk \
https://github.com/techcode-io/ignity/releases/download/${IGNITY_VERSION}/ignity-utils-${IGNITY_VERSION}-linux-${TARGETARCH}.apk \
/tmp/ignity/
RUN apk add --no-cache --allow-untrusted /tmp/ignity/*.apk && rm -rf /tmp/ignity
```

- `TARGETARCH` is set by `docker buildx` and `podman build`: otherwise, set it to `amd64` or `arm64`.
- The provenance of a package can be checked with `gh attestation verify <file> --repo techcode-io/ignity`.
- `ignity-core` depends on the virtual `ignity-utils-impl` package, provided by `ignity-utils`.
[ignity-utils-rs](https://github.com/techcode-io/ignity-utils-rs) provides it too, with a static Rust `fix-perms` at the same path: install it instead of `ignity-utils`.
- Up to `0.1.0`, ignity came as a single `ignity` package: `ignity-core` and `ignity-utils` replace it on upgrade (on Alpine, `apk del ignity` first: they conflict with it).

### How to integrate ignity in your project from source

Expand Down
66 changes: 0 additions & 66 deletions distributions/packages/nfpm.arm64.yaml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: "ignity"
arch: "amd64"
version: "0.1.1-dev"
# ARCH and VERSION are set by `poe package:<format>`
name: "ignity-core"
arch: "${ARCH}"
version: "${VERSION}"
section: "default"
priority: "extra"
maintainer: "Adrien Mannocci <adrien.mannocci@gmail.com>"
Expand All @@ -10,28 +11,40 @@ description: |
vendor: "techcode.io"
homepage: "https://github.com/techcode-io/ignity"
license: "Apache-2.0"
# Takes over the single package of ignity <= 0.1.0
conflicts:
- "ignity"
replaces:
- "ignity"
overrides:
# useradd, groupadd and getent (preboot, gen-accounts), GNU find (fix-perms) and grep (gen-accounts)
# fix-perms (ignity-utils or ignity-utils-rs, which both provide ignity-utils-impl), useradd,
# groupadd and getent (preboot, gen-accounts) and grep (gen-accounts)
deb:
depends:
- "ignity-utils-impl"
- "passwd"
- "findutils"
- "grep"
rpm:
depends:
- "ignity-utils-impl"
- "shadow-utils"
- "findutils"
- "grep"
apk:
# nfpm drops conflicts for apk, where they are `!` dependencies
depends:
- "ignity-utils-impl"
- "shadow"
- "findutils"
- "grep"
- "!ignity"
# execline, s6 and s6-portable-utils are installed in /usr/lib/ignity: nothing conflicts with the
# distribution, /init puts them first in PATH
scripts:
postinstall: ./distributions/packages/postinstall.sh
preremove: ./distributions/packages/preremove.sh
rpm:
scripts:
# The obsoleted ignity package is erased after %post, and its %preun removes the link
posttrans: ./distributions/packages/postinstall.sh
contents:
- src: ./dist/ignity/usr/lib/ignity
dst: /usr/lib/ignity
Expand All @@ -44,8 +57,20 @@ contents:
dst: /etc/s6
type: tree

- src: ./dist/ignity/usr/bin/*
dst: /usr/bin/
- src: ./dist/ignity/usr/bin/gen-accounts
dst: /usr/bin/gen-accounts

- src: ./dist/ignity/usr/bin/load-envfile
dst: /usr/bin/load-envfile

- src: ./dist/ignity/usr/bin/preboot
dst: /usr/bin/preboot

- src: ./dist/ignity/usr/bin/with-env
dst: /usr/bin/with-env

- src: ./dist/ignity/usr/bin/with-retries
dst: /usr/bin/with-retries

- dst: /etc/ignity/envs
type: dir
Expand Down
37 changes: 37 additions & 0 deletions distributions/packages/nfpm.utils.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# ARCH and VERSION are set by `poe package:<format>`
name: "ignity-utils"
arch: "${ARCH}"
version: "${VERSION}"
section: "default"
priority: "extra"
maintainer: "Adrien Mannocci <adrien.mannocci@gmail.com>"
description: |
Execline implementation of the ignity utilities: fix-perms.
ignity-utils-rs is a drop-in replacement, written in Rust.
vendor: "techcode.io"
homepage: "https://github.com/techcode-io/ignity"
license: "Apache-2.0"
# What ignity-core depends on, also provided by ignity-utils-rs
provides:
- "ignity-utils-impl"
# Takes over fix-perms from the single package of ignity <= 0.1.0
conflicts:
- "ignity"
- "ignity-utils-rs"
replaces:
- "ignity"
# execlineb (shebang, from ignity-core) and GNU find (fix-perms)
depends:
- "ignity-core"
- "findutils"
overrides:
apk:
# nfpm drops conflicts for apk, where they are `!` dependencies
depends:
- "ignity-core"
- "findutils"
- "!ignity"
- "!ignity-utils-rs"
contents:
- src: ./dist/ignity/usr/bin/fix-perms
dst: /usr/bin/fix-perms
9 changes: 8 additions & 1 deletion scripts/binds/nfpm.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
from collections.abc import Mapping

from scripts.binds.container import Container


class Nfpm(Container):
"""Represents a bind to the `goreleaser/nfpm` packaging container."""

def package(self, config: str, target: str, packager: str) -> None:
def package(
self, config: str, target: str, packager: str, env: Mapping[str, str]
) -> None:
"""Package a build bundle into an OS package using nfpm."""
# Expanded by nfpm in the config
env_args = [arg for key, value in env.items() for arg in ("-e", f"{key}={value}")]
self._exec(
"run",
"--rm",
"-v",
".:/tmp",
"-w",
"/tmp",
*env_args,
"goreleaser/nfpm",
"package",
"--config",
Expand Down
17 changes: 11 additions & 6 deletions scripts/tasks/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
from scripts.binds.nfpm import Nfpm

DISTRIBUTIONS_PACKAGES_PATH = "distributions/packages"
# ignity-core, and ignity-utils providing ignity-utils-impl (like ignity-utils-rs)
PACKAGES = ("core", "utils")
PYPROJECT_PATH = Path("pyproject.toml")


def __build_package(packager: str) -> None:
"""Package the current architecture bundle with nfpm."""
"""Package the current architecture bundle with nfpm, one package per PACKAGES."""
arch = platform.machine().lower()
arch = "amd64" if arch == "x86_64" else arch
arch = "arm64" if arch == "aarch64" else arch
Expand All @@ -18,11 +20,14 @@ def __build_package(packager: str) -> None:
with PYPROJECT_PATH.open("rb") as file:
version = tomllib.load(file)["project"]["version"]

Nfpm().package(
config=f"{DISTRIBUTIONS_PACKAGES_PATH}/nfpm.{arch}.yaml",
target=f"/tmp/dist/ignity-{version}-linux-{arch}.{packager}",
packager=packager,
)
nfpm = Nfpm()
for package in PACKAGES:
nfpm.package(
config=f"{DISTRIBUTIONS_PACKAGES_PATH}/nfpm.{package}.yaml",
target=f"/tmp/dist/ignity-{package}-{version}-linux-{arch}.{packager}",
packager=packager,
env={"ARCH": arch, "VERSION": version},
)


def deb() -> None:
Expand Down
16 changes: 1 addition & 15 deletions scripts/tasks/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@
from scripts.binds.git import Git

PYPROJECT_PATH: Final[Path] = Path("pyproject.toml")
DISTRIBUTIONS_NFPM_AMD64_PATH: Final[Path] = Path(
"distributions/packages/nfpm.amd64.yaml"
)
DISTRIBUTIONS_NFPM_ARM64_PATH: Final[Path] = Path(
"distributions/packages/nfpm.arm64.yaml"
)
# Packages built for the release: deb, rpm and apk
RELEASE_ASSETS_PATTERNS: Final[tuple[str, ...]] = (
"ignity*.deb",
Expand Down Expand Up @@ -47,20 +41,12 @@ def __update_file_version(file_path: Path, pattern: str, replacement: str) -> No


def __set_version(version: str) -> None:
"""Update version in pyproject.toml and the nfpm package configs."""
"""Update version in pyproject.toml, which the nfpm package configs are given."""
# Update project version
__update_file_version(
PYPROJECT_PATH, r"version = \"(.+)\"", f'version = "{version}"'
)

# Update nfpm package versions
__update_file_version(
DISTRIBUTIONS_NFPM_AMD64_PATH, r"version: \"(.+)\"", f'version: "{version}"'
)
__update_file_version(
DISTRIBUTIONS_NFPM_ARM64_PATH, r"version: \"(.+)\"", f'version: "{version}"'
)


def __project_version() -> str:
"""Return the project version from pyproject.toml."""
Expand Down
Loading