CI / build-and-test (push) Successful in 37s
README: - Quick Start shows kappa add workflow instead of manual configs - New features: indexed repos, env operators (+=, ?=), conflict reporting - Updated subcommand table (add, remove, index, validate-index) - Repo maintenance section (kappa index) - Link to STYLEGUIDE.md, test counts, CI info CONTRIBUTING: - Updated test counts (30+23+85 instead of 31+23) - Added test-full.sh to mandatory test suite - Referenced STYLEGUIDE.md in PR requirements - Updated good-first/ambitious issues for current state
204 lines
7.0 KiB
Markdown
204 lines
7.0 KiB
Markdown
<p align="center">
|
|
<img src="https://git.spectoria.dev/repo-avatars/f111d66d4a9972c13eb8aa4dddc7fe2c39aabdf0ab0ff0dc27a31a9aa1c2e968" width="180" alt="kappa mascot" />
|
|
</p>
|
|
|
|
# kappa
|
|
|
|
**Anywhere, any init, anytime.**
|
|
|
|
A declarative, source-based package manager that doesn't care what init system
|
|
you run. Or what bootloader. Or what CPU architecture. Kappa builds your entire
|
|
system from source — and lets you swap the init system like you'd swap a
|
|
wallpaper.
|
|
|
|
---
|
|
|
|
### Why
|
|
|
|
Every other package manager picked a side. `apt` married systemd. `pacman`
|
|
shackled itself to Arch's ecosystem. `emerge` gave you choice but at the cost
|
|
of your weekend. Nix gave you reproducibility but took your filesystem with it.
|
|
|
|
Kappa is what you get when you stop negotiating. You declare what your system
|
|
*is*, and kappa figures out how to build it. Change your mind about the init
|
|
system? Rebuild only the packages that care — the other 800 stay put.
|
|
|
|
### What it does
|
|
|
|
```
|
|
# Your system, in one file:
|
|
boot {
|
|
kernel = "linux"
|
|
init = "s6" # swap to "systemd" anytime
|
|
bootloader = "limine" # or "grub"
|
|
root = "/dev/sda1"
|
|
}
|
|
|
|
packages {
|
|
nginx { version = ">=1.24" }
|
|
postgresql {}
|
|
zlib {}
|
|
}
|
|
|
|
services {
|
|
nginx { enable = true }
|
|
}
|
|
```
|
|
|
|
Then:
|
|
|
|
```sh
|
|
kappa rebuild config.kap # builds everything, generates service files
|
|
kappa rebuild config.kap # boot.init = "openrc" — only 5 packages actually rebuild
|
|
```
|
|
|
|
### Features that nobody else has
|
|
|
|
- **Init-system-as-configuration.** `boot.init = "s6"` → generates s6 service
|
|
directories. Change it to `"systemd"` → regenerates `.service` units. Change
|
|
it to `"openrc"` → generates init.d scripts. The package definitions don't
|
|
know or care which init you picked. That's kappa's problem.
|
|
|
|
- **Post-install init switching.** Change `boot.init`, run `kappa rebuild`, reboot.
|
|
You're now on a different init system. Only packages that actually use
|
|
`${enabledinit}` in their build scripts need recompiling. Everything else
|
|
just gets new service files generated.
|
|
|
|
- **Bootloader rollback.** Every rebuild creates a fallback boot entry pointing
|
|
at the previous generation's init. If the new one doesn't boot, the old one
|
|
is one reboot away.
|
|
|
|
- **Parallel scheduler.** `-w 4 -j 8` means four packages building
|
|
simultaneously, eight jobs each. The scheduler uses depth-based priority
|
|
grouping so leaf dependencies unblock as much work as possible first.
|
|
|
|
- **Package recipe caching with indexed repos.** Declare named repos with
|
|
channels and mirrors in your config. Kappa fetches `index.kap` from each
|
|
repo, caches it, and only re-fetches when the remote changes. Package recipes
|
|
are resolved from the index — fast, offline-tolerant, and mirror-aware.
|
|
|
|
```kap
|
|
repos {
|
|
kappa-os {
|
|
url = "https://packages.kappa-os.org/"
|
|
channels = ["stable"]
|
|
mirrors = [
|
|
"https://cdn.kappa-os.org/",
|
|
"https://eu.kappa-os.org/",
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
`remotes = [...]` still works. Repos are tried first, then legacy remotes.
|
|
|
|
- **Source tarball caching.** Downloaded once, stored at `$KAPPA_ROOT/cache/`
|
|
(default: `/usr/local/kappa/cache/`). Rebuilds don't touch the network
|
|
unless versions change.
|
|
|
|
- **Env operators.** Three ways to set build environment variables:
|
|
`=` (hard set), `+=` (append with space), `?=` (soft set — only if not
|
|
already defined). System-level env propagates to all packages.
|
|
|
|
```kap
|
|
env {
|
|
CFLAGS = "-O2 -march=native" # overwrite
|
|
CFLAGS += "-pipe" # append → "-O2 -march=native -pipe"
|
|
CFLAGS ?= "-g" # soft — only if not set
|
|
}
|
|
```
|
|
|
|
- **Conflicts.** `systemd` declares `conflicts = ["eudev", "elogind"]`. The
|
|
resolver catches mutual incompatibility before a build starts — and now
|
|
actually reports it, rather than silently ignoring it.
|
|
|
|
- **Init-agnostic system config.** `groups { wheel { gid = 998 } }` — kappa
|
|
creates the groups. `system { hostname = "mybox" }` — kappa writes
|
|
`/etc/hostname`. No `systemctl`, no `rc-update`, no init dependency.
|
|
|
|
### 5 init systems. 2 bootloaders. Zero lock-in.
|
|
|
|
| Init | Service location | Enable command |
|
|
|------|-----------------|----------------|
|
|
| systemd | `/etc/systemd/system/{name}.service` | `systemctl enable` |
|
|
| openrc | `/etc/init.d/{name}` | `rc-update add` |
|
|
| s6 | `/etc/s6/sv/{name}/run` | `s6-rc-bundle-update` |
|
|
| runit | `/etc/sv/{name}/run` | `ln -sf /etc/sv/{name} /var/service/` |
|
|
| dinit | `/etc/dinit.d/{name}` | `dinitctl enable` |
|
|
|
|
| Bootloader | Config path |
|
|
|-----------|------------|
|
|
| limine | `/boot/limine.cfg` |
|
|
| grub | `/boot/grub/grub.cfg` |
|
|
|
|
### Quick start
|
|
|
|
> **Note for 0.1.x users**: The default store root has moved from `/kappa` to
|
|
> `/usr/local/kappa` (FHS 3.0). Set `KAPPA_ROOT` to your existing `/kappa`
|
|
> directory to keep using the old location.
|
|
|
|
```sh
|
|
# Build kappa (needs Clang 17+, CMake 3.20+, C++23)
|
|
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
|
|
cmake --build build
|
|
|
|
# Add packages to your system config
|
|
build/kappa add make
|
|
build/kappa add nginx ">=1.24"
|
|
build/kappa add zlib
|
|
|
|
# Rebuild — kappa fetches recipes, resolves deps, builds everything
|
|
build/kappa rebuild $KAPPA_ROOT/system/config.kap
|
|
|
|
# Remove packages
|
|
build/kappa remove zlib
|
|
```
|
|
|
|
No config files to write by hand. `kappa add` writes the `packages {}` block
|
|
for you. `kappa rebuild` handles the rest.
|
|
|
|
### Subcommands
|
|
|
|
| Command | What it does |
|
|
|---------|-------------|
|
|
| `add <pkg> [version]` | Add a package to system config |
|
|
| `remove <pkg>` | Remove a package from system config |
|
|
| `build <package>` | Build a single package from its `.kap` definition |
|
|
| `rebuild <config>` | Diff config against installed state, rebuild changed |
|
|
| `resolve <config>` | Compute a build plan (shows order, deps, conflicts) |
|
|
| `doctor <file>` | Check a file for issues and warnings |
|
|
| `fetch <package>` | Download and verify source tarballs |
|
|
| `fetch-package <name>` | Fetch a package recipe from remotes |
|
|
| `format <file>` | Pretty-print to canonical style |
|
|
| `index <dir>` | Build an `index.kap` from `.kap` files in a directory |
|
|
| `list` | Show installed packages |
|
|
| `parse-config <file>` | Validate a system configuration |
|
|
| `parse-package <file>` | Validate a `.kap` package definition |
|
|
| `rollback` | Show available generations |
|
|
| `validate <file>` | Validate any kappa file (package, config, or index) |
|
|
|
|
### Repo maintenance
|
|
|
|
```sh
|
|
# Generate an index from a directory of .kap files
|
|
kappa index ./packages/
|
|
# → packages/index.kap
|
|
|
|
# Host the directory behind any HTTP server. That's your repo.
|
|
```
|
|
|
|
The index is a tiny text file listing every package and version.
|
|
Clients fetch it once, cache it, and check for updates via HTTP headers.
|
|
|
|
### License
|
|
|
|
BSD 2-Clause. Do whatever you want. Just don't sue us.
|
|
|
|
### Contributing
|
|
|
|
See [CONTRIBUTING.md](CONTRIBUTING.md). We're opinionated but we merge good
|
|
code. See [STYLEGUIDE.md](STYLEGUIDE.md) for code conventions.
|
|
|
|
Tests: 138 integration tests across three suites. CI runs on Arch Linux.
|
|
Everything passes or nothing merges.
|