Tools¶
A step can have programs put on its PATH instead of needing an image that
contains them, the way a setup-* action does, without downloading anything
it already has:
bun = tool(
"bun",
url = "https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-linux-{arch}.zip",
sha256 = {"amd64": "951ee2ae…", "arm64": "a27ffb63…"},
arch_names = {"amd64": "x64", "arm64": "aarch64"},
bin = ["bun-linux-{arch}"],
)
step("test", image = "debian:12-slim", tools = [bun], run = "bun test")
url |
The download: a zip, a tar archive, or the program itself. {arch} becomes the architecture the step's image runs on. |
sha256 |
The download's checksum for amd64 and/or arm64. An architecture not listed is not supported. |
arch_names |
How the publisher spells an architecture, where it differs. |
bin |
Directories inside the archive to put on PATH. Default: its top level. |
The checksum is the tool's identity. A worker keeps one copy per checksum, downloads it once, and mounts it read-only into every step that asks. A step's result is keyed on it, so changing a tool runs the step again and fetching the same tool from a mirror does not. There is no way to ask for "the latest".
This suits programs that stand alone. A tool built against glibc will not run in an Alpine image.
A pin can be checked without running a step:
$ starci validate
inputs
ok test
tools
ok bun amd64
failed bun arm64
tool "bun": https://…/bun-linux-aarch64.zip has checksum 5f0c…, not the a27f… the script pins it to
Every tool the script declares is downloaded, for every architecture it lists
whatever this machine is, and checked three ways: the address answers, what it
serves has the pinned checksum, and each bin directory is in it and holds a
program. It always downloads, since a copy this machine already has says
nothing about whether another could get one, and it works for both modes. It
sees tools declared at a script's top level and in ci(ctx); one declared
only inside pipeline(ctx) is not known until that runs.
Packages¶
For what a tool cannot carry — system libraries, a compiler — a step can ask for apt packages on top of its image:
step("test", image = "debian:12-slim", packages = ["libpq-dev", "git"], run = "...")
A worker builds an image from that base and those packages the first time a step needs it, and reuses it for every step that asks for the same combination. The image must be Debian or Ubuntu based.
Unlike a tool, a package is not pinned: it is whatever version apt offered on
the day that worker built the image, and it stays that version there until
the base image or the list changes. A step's result is keyed on the base image
and the list, so two workers that built at different times will share a result
even though their packages may differ slightly. Write name=version where
that matters.