- foo.kap: document all three env operators (=, ?=, +=) in header, demonstrate += in env block with comments - postgres.kap: demonstrate += for appending security hardening flags - config.kap: demonstrate system-level += for inherited flags
160 lines
5.9 KiB
Plaintext
160 lines
5.9 KiB
Plaintext
/*
|
|
* foo — a web server with optional SSL and GUI support.
|
|
*
|
|
* SERVICE MODEL
|
|
* =============
|
|
* Kappa service definitions are init-system-agnostic. The `service` block
|
|
* describes what the service IS (exec, type, ports, user) — NOT how each init
|
|
* system runs it. The system config's `boot.init` field (see config.kap)
|
|
* determines which init system's service files get generated at install time:
|
|
*
|
|
* boot.init = "systemd" → generates .service unit files
|
|
* boot.init = "openrc" → generates /etc/init.d scripts
|
|
* boot.init = "s6" → generates s6 service directories
|
|
* boot.init = "dinit" → generates dinit service descriptors
|
|
* boot.init = "runit" → generates runit service directories
|
|
*
|
|
* Per-init blocks (service { systemd { ... } s6 { ... } }) do NOT exist.
|
|
* If a package genuinely needs init-specific behaviour (e.g. different
|
|
* ./configure flags for systemd vs. openrc), use ${enabledinit} in the
|
|
* build phase — see examples/postgres.kap for that pattern.
|
|
*
|
|
* SERVICE TYPE VALUES
|
|
* ===================
|
|
* These are semantic, not init-specific. Each backend translates them
|
|
* into its own vocabulary:
|
|
*
|
|
* "simple" — foreground process; init manages lifecycle directly.
|
|
* systemd: Type=simple openrc: command_background=false
|
|
* s6: type=longrun dinit: type=process
|
|
*
|
|
* "forking" — process daemonises itself; init tracks the forked PID.
|
|
* systemd: Type=forking openrc: command_background=true
|
|
* s6: type=longrun dinit: type=bgprocess
|
|
*
|
|
* "notify" — foreground process that signals readiness (sd_notify).
|
|
* systemd: Type=notify openrc: command_background=true
|
|
* s6: type=longrun dinit: type=process
|
|
*
|
|
* "oneshot" — runs once and exits (startup tasks, database migrations).
|
|
* systemd: Type=oneshot openrc: command_background=false
|
|
* s6: type=oneshot dinit: type=scripted
|
|
*
|
|
* "longrun" — long-running supervised process (s6/runit idiom).
|
|
* systemd: Type=simple openrc: command_background=true
|
|
* s6: type=longrun dinit: type=process
|
|
*
|
|
* The backend generators handle all translation. Package authors only
|
|
* need to pick the semantic type that describes their daemon's behaviour.
|
|
*
|
|
*
|
|
* ENV OPERATORS
|
|
* =============
|
|
* key = "val" hard set — overwrites any existing value
|
|
* key ?= "val" soft set — only applied if key is not already set
|
|
* key += "val" append — adds to existing value with a space separator
|
|
*
|
|
* Order matters. Subsequent entries in the same env block can append to
|
|
* earlier ones. System-level env (from config.kap) is applied before
|
|
* package env, so packages can += to flags set globally.
|
|
*/
|
|
package "foo" {
|
|
const version = "1.2.3"
|
|
const source = "https://example.com/foo-${version}.tar.gz"
|
|
sha256 = "e127a709cba24c76de8936cb7083dd768f28cd37eb010492e2f19b71eb1294e4"
|
|
license = "MIT"
|
|
|
|
provides = ["libfoo.so.1", "foo"]
|
|
conflicts = [] // packages this cannot coexist with (e.g. ["eudev"] if this were systemd)
|
|
|
|
patches = [
|
|
{
|
|
url = "https://example.com/fix-build.patch"
|
|
sha256 = "abc123def456"
|
|
level = 1
|
|
},
|
|
"local-fix.patch" // local shorthand, no hash
|
|
]
|
|
outputs = ["bin", "lib", "dev"]
|
|
|
|
depends = [
|
|
{ name = "zlib", version = ">=1.2,<2.0" },
|
|
{ name = "openssl", feature = "ssl" },
|
|
{ name = "gtk", feature = "gui", version = ">=3" },
|
|
"gettext:lib" // name:output shorthand
|
|
]
|
|
|
|
features {
|
|
ssl = { enabled = true, flag = "--with-ssl-dir=${cfg.ssl_dir}" }
|
|
gui = { enabled = false, flag = "--enable-gui" }
|
|
drivers = { enabled = true, force = true }
|
|
debug = false
|
|
}
|
|
|
|
config {
|
|
// Written once, left alone on rebuild if the user edits it.
|
|
file "etc/foo.conf" mode = "default" {
|
|
hostname = ${cfg.hostname !} // required — user must set
|
|
listen_port = ${cfg.port ? 8080} // optional, default 8080
|
|
ssl_enabled = ${cfg.ssl ? true} // optional, default true
|
|
}
|
|
|
|
// Always overwritten on rebuild.
|
|
file "etc/log.conf" mode = "replace" {
|
|
log_level = ${cfg.log_level ? info}
|
|
log_path = ${cfg.log_path ? /var/log/foo}
|
|
}
|
|
|
|
// Three-way diff on rebuild.
|
|
file "etc/limits.conf" mode = "merge" {
|
|
max_connections = ${cfg.max_conn ? 1024}
|
|
}
|
|
}
|
|
|
|
env {
|
|
// hard set — overwrites any existing CFLAGS
|
|
CFLAGS = "-O2 -march=native"
|
|
// append — now CFLAGS is "-O2 -march=native -pipe"
|
|
CFLAGS += "-pipe"
|
|
LDFLAGS = "-Wl,--as-needed"
|
|
// soft set — only added if CFLAGS isn't already defined
|
|
CFLAGS ?= "-g"
|
|
}
|
|
|
|
// --- service ----------------------------------------------------------
|
|
// Init-agnostic service definition. The `type` field is semantic
|
|
// ("forking") — the selected init system's backend translates it into
|
|
// the appropriate native format. If the package ships multiple
|
|
// services, use named blocks (see examples/postgres.kap).
|
|
service {
|
|
exec = "/usr/bin/foo"
|
|
type = "forking" // daemonises itself
|
|
user = "foo"
|
|
ports = [80, 443]
|
|
description = "Foo web server"
|
|
after = "network" // ordering hint — systemd After=, OpenRC need, etc.
|
|
restart = "on-failure" // "always" | "on-failure" | "never"
|
|
}
|
|
|
|
prepare {
|
|
patch "fix-build.patch"
|
|
}
|
|
|
|
build {
|
|
./configure --prefix=${prefix} ${feature.ssl} ${feature.gui}
|
|
make -j${jobs}
|
|
}
|
|
|
|
check {
|
|
make check
|
|
}
|
|
|
|
install {
|
|
make DESTDIR=${destdir} install
|
|
}
|
|
|
|
uninstall {
|
|
make -C build uninstall
|
|
}
|
|
}
|