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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Cell layout and architecture: [the design plan](docs/design/architecture.md). Us
`starlark_fmt` owns the layout of every `.bzl` and `BUCK` file and wraps at 160 columns.

- Leave a trailing comma after the last argument to keep a signature or call broken across lines.
- It sorts dict keys unconditionally; mark a dict whose order is a contract (`disk.bzl:partition` feeds
- It sorts dict keys unconditionally; mark a dict whose order is a contract (`ddi.bzl:partition` feeds
partition UUIDs) `# @unsorted-dict-items`.
- It drops load symbols it cannot see used; annotate type-only imports `# @unused`.

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ image.rootfs_archive(
package_manager = ":image.package-manager",
packages = ["bash", "coreutils"],
ops = [
image.copy("marker.txt", "/etc/tine/marker"),
image.run(["/usr/bin/bash", "-c", "echo built-by-tine >> /etc/tine/marker"], chroot = True),
image.ops.copy("marker.txt", "/etc/tine/marker"),
image.ops.run(["/usr/bin/bash", "-c", "echo built-by-tine >> /etc/tine/marker"], chroot = True),
],
format = "tar",
)
Expand Down
2 changes: 1 addition & 1 deletion catalog/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ box.new(
"e2fsprogs",
"erofs-utils",
"kmod",
# image/kmod.py loads libkmod.so.2 with dlopen(), and no package in this list depends on it.
# kmod/kmod.py loads libkmod.so.2 with dlopen(), and no package in this list depends on it.
"libkmod2",
# The relaxed sandbox binds the host's nsswitch.conf. Without libnss_systemd, getpwuid() cannot find
# a systemd-homed caller, and ssh-keygen aborts.
Expand Down
49 changes: 24 additions & 25 deletions docs/design/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ tine//package_system/ one directory per package system: its repository, reso
extractor, and optional indexer and builder
tine//box/ box bootstrap and sandbox command construction
tine//rootfs/ bind/overlay mounting and stored-delta translation
tine//image/ layers, boot artifacts, composition macros, and VM runners
tine//image_format/ archive, directory, and raw-disk output rules and drivers
tine//image/ layers, compositions, archive, directory, disk and boot artifact rules and
drivers, and VM runners
tine//git/ pinned Git repositories with local checkout overrides
tine//cargo/ vendored crate trees and offline Rust source builds
tine//go/ go.sum-verified module fetches and offline Go source builds
Expand Down Expand Up @@ -1126,7 +1126,7 @@ allowing reusable helpers to return ordered groups of operations. Every rule tha
wrapped in a declaration macro that flattens them the same way, so a helper returning an ordered group
works identically in `image.layer()` and in a composition; the underlying attribute stays a flat, typed
list so Buck can track every embedded source dependency. An `image.install()` target carries `packages`
beside its operations, so a reusable target declares what it needs and `image.install_from()` folds that
beside its operations, so a reusable target declares what it needs and `image.ops.install_from()` folds that
into the installing layer's own request.

`install_langs` narrows the install to the translated files of the named languages, and `install_docs`
Expand Down Expand Up @@ -1170,7 +1170,7 @@ locally backed in later solves. Unlike a static `local_repository`, only the pac
pulls in are built; the universe target itself never forces a package build.

Logical images and terminal outputs are separate rule families. Terminal rules merge the stack only when
needed. The catalog of terminal rules (`image.archive`, `image.directory`, `image.uki`, `image.repart`,
needed. The catalog of terminal rules (`image.archive`, `image.directory`, `image.uki`, `image.ddi`,
`image.bootable`, `image.sysext`, and `image.vm`) and the `image.rootfs_archive`, `image.sysext_image`, and
`image.bootable_disk` composition rules are documented in [images.md](../user/images.md). All of them,
with the operation helpers and conventional partition layouts, are members of the `image` struct exported
Expand Down Expand Up @@ -1207,30 +1207,30 @@ multi-threaded output is byte-identical to its single-threaded output, so this s
uses it, while the kernel-modules cpio that `uki.py` appends stays raw because the distribution already
ships each module compressed; the kernel unpacks the concatenation as independently compressed segments.

`image.repart` deliberately distinguishes `definitions` from `partitions`. Definitions describe new
`image.ddi` deliberately distinguishes `definitions` from `partitions`. Definitions describe new
partitions to populate directly from `image.ImageInfo`: repart mounts the delta stack with a
disposable overlay upper instead of first copying a directory artifact. Partition inputs are
`image.RepartInfo` outputs from an earlier split call; their blocks are copied into the new disk with
`image.DdiInfo` outputs from an earlier split call; their blocks are copied into the new disk with
their resolved type and UUID preserved.
Calls emit a disk by default. `split = True` additionally exposes each newly defined partition with
normalized metadata alongside its block artifact; `disk = False` makes such a call partition-only. No
partial disk is passed between
actions. The partition artifacts are portable, so `image.RepartInfo` carries no box: repart uses the
actions. The partition artifacts are portable, so `image.DdiInfo` carries no box: repart uses the
destination `image.ImageInfo.box`, while standalone conversion and VM rules select a box explicitly.
`image.directory` is an independent terminal view and is never an input to repart.

Partition layouts are always explicit inputs; neither `image.repart` nor `image.bootable_disk` chooses one
Partition layouts are always explicit inputs; neither `image.ddi` nor `image.bootable_disk` chooses one
implicitly. The reusable conventional layouts are listed in [images.md](../user/images.md).

Partition labels may contain `{image_id}` and `{version}` placeholders. `image.format_partition_labels()`
renders them — `image.bootable_disk()` calls it with its own image identity, raw `image.repart()` users
renders them — `image.bootable_disk()` calls it with its own image identity, raw `image.ddi()` users
call it themselves, and unrendered placeholders fail the build. The usr-verity layouts carry such labels
to produce the `<id>_<version>[_verity[_sig]]` names that systemd-sysupdate A/B slot matching expects.
Rendered labels pass through `image.partition()` again, so GPT's 36-character label limit is enforced on the
final value.

Verity data, hash, and optional signature partitions are produced together in the split action. The root or
usr hash is an artifact because its value is known only after execution. `image.RepartInfo.root_hash` carries
usr hash is an artifact because its value is known only after execution. `image.DdiInfo.root_hash` carries
an optional `image.RootHashInfo`, allowing `image.uki` to consume the hash without another top-level
provider. One split call may produce at most one such hash. This artifact boundary also ensures the final
disk contains exactly the partition bytes whose hash was embedded in the UKI. The hash is also available
Expand Down Expand Up @@ -1305,7 +1305,7 @@ UKI is named
`<image_id>_<version>_<arch>.efi` from the image identity. For now an image holds exactly one kernel — the
name (and sysupdate's matching of it) could not distinguish more. If several kernels per image ever become
a requirement, add naming configuration to `image.uki()` to disambiguate them. The ESP layer copies the UKI
directory into `EFI/Linux` and includes the operations returned by `image.install_systemd_boot()`. Those
directory into `EFI/Linux` and includes the operations returned by `image.ops.install_systemd_boot()`. Those
create the ESP path, run the box's `bootctl` with its paths in the command environment, and remove the
random seed. The final repart action creates the ESP while copying the previously split system partitions
into the same disk. It splits nothing itself: the system partitions arrive already split, and nothing updates
Expand All @@ -1330,7 +1330,7 @@ artifact, so one provider covers every encoding instead of one provider type per

Bootability and output format remain independent terminal capabilities, but the artifacts declared by one
`image.bootable_disk()` invocation form one concrete product. The composition publishes one target at
the requested name. Its default output and `image.RepartInfo.disk` are the raw disk; its other terminal
the requested name. Its default output and `image.DdiInfo.disk` are the raw disk; its other terminal
views,
supply-chain metadata, and constituents are lazy subtargets:

Expand Down Expand Up @@ -1358,8 +1358,8 @@ supply-chain metadata, and constituents are lazy subtargets:
```

Typed providers are the composition API; subtargets are the command-line interface. There is no aggregate
bootable-image provider. The rule returns its completed `image.ImageInfo`, `image.RepartInfo`,
`image.InitrdInfo`, `image.ImageDirectoryInfo`, and `image.UkiInfo` independently. `image.RepartInfo`
bootable-image provider. The rule returns its completed `image.ImageInfo`, `image.DdiInfo`,
`image.InitrdInfo`, `image.ImageDirectoryInfo`, and `image.UkiInfo` independently. `image.DdiInfo`
holds the composed raw disk, its independent partitions, and optional `image.RootHashInfo`, rather than
exposing separate top-level providers for these facets.
Its qcow2 and compressed-raw encodings are reachable only through their subtargets, each publishing one
Expand All @@ -1376,7 +1376,7 @@ describe, so a release gathers what a scanner reads while an image's own publish
update transfers.

`image.bootable_disk` is one rule that owns the complete composition action graph. Provider-oriented action
helpers are shared with the standalone `image.layer`, `image.repart`, `image.uki`, and terminal-format
helpers are shared with the standalone `image.layer`, `image.ddi`, `image.uki`, and terminal-format
rules, so the composition reuses their implementations without declaring private sibling targets or
forwarding through a result rule. This aggregation remains scoped to the concrete bootable-image product;
it does not restore a generic `image_result` around every logical `image.ImageInfo`.
Expand Down Expand Up @@ -1426,15 +1426,14 @@ gets the content its operations ask for and nothing else, which is the same reas
package state alone.

Each generator is a no-op for an image that carries none of what it acts on, so none of them needs
per-distribution knowledge: `image.depmod()` is silent without kernels, `image.hwdb()` without `hwdb.d`,
`image.locale_gen()`
without an `/etc/locale.gen` asking for something. That is what lets the compositions, which declare a whole
product rather than one layer, end every image they build with all three at their defaults without knowing
what went into it. A generator named in `ops` replaces the composition's copy instead of adding a second, so
placing or configuring one stays possible. `sysext_image` is the exception that generates nothing: an
extension merges onto a system it does not own, where a database built from the extension's own tree would
shadow that system's while describing only what the extension carries, exactly as its package database
would.
per-distribution knowledge: `image.ops.depmod()` is silent without kernels, `image.ops.hwdb()` without
`hwdb.d`, `image.ops.locale_gen()` without an `/etc/locale.gen` asking for something. That is what lets the
compositions, which declare a whole product rather than one layer, end every image they build with all three
at their defaults without knowing what went into it. A generator named in `ops` replaces the composition's
copy instead of adding a second, so placing or configuring one stays possible. `sysext_image` is the
exception that generates nothing: an extension merges onto a system it does not own, where a database built
from the extension's own tree would shadow that system's while describing only what the extension carries,
exactly as its package database would.

Their tools are the box's, applied to the mounted image through the `--root` interface systemd gives them,
which is the same relationship every other layer driver has to the image and is what lets an image that
Expand Down Expand Up @@ -1754,7 +1753,7 @@ Useful implementation entry points:
`package/{href,installer,transaction}.py`
- each package system's `rules.bzl` and drivers under `package_system/`, listed in its own section above
- `box/{build,runtime}.bzl`, `box/sandbox.py`, and `rootfs/rootfs.py`
- `image/{image,compose,defs,sign,vm}.bzl` and `image_format/{archive,boot,disk,sysext,uki}.bzl`
- `image/{image,compose,defs,sign,vm,archive,boot,ddi,sysext}.bzl`
- `cargo/{defs,rules,vendor}.bzl` and `cargo/{lock,vendor,build}.py`
- `go/rules.bzl` and `go/{fetch,build}.py`
- `tools/catalog.py` and `catalog/BUCK`
Expand Down
4 changes: 2 additions & 2 deletions docs/user/cargo.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ cargo.package(
- `box` is the build environment, declared by the consumer because only the consumer knows what its
projects link against.

The binaries are ordinary artifacts, so an image installs one with an `image.copy()` operation:
The binaries are ordinary artifacts, so an image installs one with an `image.ops.copy()` operation:

```Starlark
image.rootfs_archive(
name = "demo",
package_manager = ":image.package-manager",
packages = ["glibc"],
ops = [image.copy(":hello[hello-cli]", "/usr/bin/hello-cli")],
ops = [image.ops.copy(":hello[hello-cli]", "/usr/bin/hello-cli")],
format = "tar",
)
```
Expand Down
2 changes: 1 addition & 1 deletion docs/user/go.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ go.package(
executables, which the kernel loads at a random address on each run. Unset, the box toolchain's default
applies, which is `"exe"` on x86-64 and AArch64 Linux.

The binaries are ordinary artifacts, so an image installs one with an `image.copy()` operation; see the
The binaries are ordinary artifacts, so an image installs one with an `image.ops.copy()` operation; see the
example.

For example, etcd's build scripts build the server in `server/`, which contains the server module. The
Expand Down
Loading