cc-libs/docs/adrs/adr-0010-ccpm-package-manager.md

114 lines
5.9 KiB
Markdown

# ADR 0010: ccpm Package Manager
## Status
Accepted
## Date
2026-06-08
## Context
The previous install flow (a `LIST_FILES` table inside `install.lua` and a single flat
`manifest.json` read by `wget`) was all-or-nothing. There was no way to install just
networking or just the UI, and no way to add or remove pieces of the OS after the
initial install. The project also outgrew the "Trap's ComputerCraft APIs" framing into
a small in-game OS — TrapOS — with a name, a version, a boot banner, and a persisted
beta channel; that motion is preserved here even though the original install
mechanism is fully replaced.
We want a package manager, `ccpm` ("ComputerCraft Package Manager"), installed first
as a standalone user-facing step. After that, a machine can `ccpm update`,
`ccpm install trapos`, `ccpm install trapos-net`, `ccpm uninstall trapos-ui`, and manage
where packages come from. TrapOS itself is installed through a `trapos` meta-package;
the `wget run .../install-ccpm.lua` bootstrap exists only to install `ccpm`.
## Decision
### Packages are descriptors over the existing tree
Source files stay where they are (`apis/`, `programs/`, `servers/`, `startup/`); their
install targets remain the same absolute CC paths, so `require` paths and the dev
mounts are unchanged. A package is a descriptor that *references* those files:
`packages/<name>/ccpm.json` with `{ name, version, description, dependencies, files,
autostart }`. `packages/index.json` lists the packages a registry offers (for
`ccpm search`). There is no `ccpm.json` at the repo root.
The split is finer-grained than the install examples imply:
| package | contents | deps |
|----------|-----------------------------------------------------------------|----------|
| trapos-core | ccpm, libccpm, eventloop, upgrade, events | — |
| trapos-test | libtest, runtest | trapos-core |
| trapos-boot | motd, servers (startup) | trapos-core |
| trapos-net | net, router, ping, ping-server | trapos-core |
| trapos-ui | libtui, tuidemo | trapos-core |
| trapos-ai | AI client for opencode serve | trapos-core |
| trapos | full TrapOS meta-package | trapos-boot, trapos-net, trapos-ui, trapos-test, trapos-ai |
### Two files for ccpm, "manifest" reserved for the OS
To avoid colliding with the OS `manifest.json`, ccpm never uses the word "manifest".
Local state lives under `/trapos`:
- `ccpm.json` — ordered registry list `{ registries = { { name, type, branch } } }`.
`type` is `gitea` (resolves to `git.trapcloud.fr/<name>/raw/branch/<branch>/`, the
default seeded by the bootstrap), `github` (resolves to
`raw.githubusercontent.com/<name>/<branch>/`, deprecated but still supported), or
`http`/`https` (the `name` is a base URL).
- `ccpm.lock.json` — installed packages `{ packages = { <name> = { version, registry,
files, dependencies, autostart } } }`, used by `ls`, `uninstall`, and `reinstall`.
- `ccpm.cache.json` — packages advertised by configured registries, written by
`ccpm update` from each registry's `packages/index.json`, used by `ccpm search`,
`ccpm available`, and `ccpm upgrade`.
`apis/libccpm.lua` is the testable core (a factory; `http`/`stateDir`/`installRoot`
are injectable for tests). `programs/ccpm.lua` is a thin CLI over it.
### The bootstrap installs only ccpm
`install-ccpm.lua` resolves only the `trapos-core` package descriptor (pulling any future
dependencies), downloads its files, and writes:
- `/trapos/manifest.json` — the aggregated `{ name, version, branch, files, autostart }`
still consumed by `startup/motd.lua` and `startup/servers.lua` after boot packages
are installed. This is the surviving piece of the previous manifest-driven install:
it is no longer the install source of truth (each package's `ccpm.json` is), but it
is still the local system state used at boot for the colored `TrapOS v<version>`
banner and to read the `autostart` list. `branch` is the persisted beta opt-in
(a single confirmed `--beta` install switches subsequent `ccpm upgrade` runs to
`next` with no flag needed; `--stable` is the symmetric opt-out);
- `/trapos/ccpm.lock.json` — so right after a fresh install `ccpm install trapos-core`
correctly reports "already installed";
- `/trapos/ccpm.json` — seeding/refreshing the default `guillaumearm/cc-libs` registry
to track the install branch.
The install path is:
- `wget run .../install-ccpm.lua` — install `ccpm` (`trapos-core`) and seed the default
registry.
- `ccpm update` — refresh the local package cache.
- `ccpm install trapos` — install the full OS. During beta, `trapos` includes
`trapos-test` by default.
On a subsequent `upgrade`, `programs/upgrade.lua` delegates to `ccpm upgrade`, which
upgrades installed packages using `/trapos/ccpm.cache.json`. Users run `ccpm update`
first to refresh available versions.
## Consequences
- The repo gains a `packages/` descriptor tree; the flat source layout is untouched.
- `just trapos` (formerly `just craftos`; see [ADR-0005](adr-0005-craftos-pc-harness-and-probes.md)) no longer derives mounts
from `manifest.json .files` (it is now `.packages`); it mounts a fixed list of
top-level dirs instead. `just test` was already on fixed mounts and is unaffected.
- ccpm logic is covered by `tests/ccpm.lua` (URL resolution, dependency ordering,
cycle/missing detection, already-installed, registry CRUD, cache update, available
status, upgrade, uninstall dependency guard) with an injected `http` stub — no
network in tests.
## Future Work
- Version ranges (today a single pinned version per package).
- http/https registries beyond a plain base URL (auth, caching).