Portability (Linux distro-agnostic): - Remove hardcoded Clang compiler enforcement; GCC now builds - Add find_package(Threads REQUIRED) for older glibc - Add cmake install() target - FHS 3.0 default root: /kappa -> /usr/local/kappa - fs::path operator/ for all init/bootloader paths (fixes prefix fragility) - Multi-distro zoneinfo search (FHS, NixOS, Guix, alt) - Portable tar extraction (drop GNU-only --no-same-permissions) - Runit enable/disable commands now prefix-aware - --root CLI flag before/after subcommand, lazy directory creation - Shebang constants de-duplicated to types.hpp Correctness (race conditions, UB, corruption): - Fix CWD race in scheduler: per-child chdir() instead of process-global - Fix UB const_cast in exec_cmd/exec_capture: mutable argv buffers - Fix non-atomic installed DB writes: tmp+rename pattern - Fix read_file() no longer calls exit(1), throws instead - Fix silent catch(...) parse errors now print diagnostics - Fix rebuild false positives with config_hash change detection - Fix s6 disable_cmd copy-paste bug (was identical to enable) - Fix runit enable_cmd incomplete, disable_cmd wrong target - Fix dinit env vars: functional env-file + companion .env Quality: - Add -Wall -Wextra -Wpedantic to CMake, fix 2 pre-existing warnings - Move parse_int from error.hpp to parse_util.hpp - Fix hash verification guard checks all three hash types - Check patch return code in fetch.cpp - Add explicit system_dir creation in ensure_directories() - Add resolve to needs_dirs for build_registry() consistency - Update stale /kappa path references in examples - Remove inaccurate -Werror claim in CONTRIBUTING.md - Add build-gcc/ and agent dirs to .gitignore - Suppress clang-tidy portability-avoid-pragma-once - Fix .gitignore /kappa pattern (was matching include/kappa/) - Delete stale vcpkg_installed/ directory 54/54 tests pass. Builds on Clang and GCC with 0 warnings.
4.4 KiB
Contributing to kappa
We're building a package manager that doesn't care about your init system, your bootloader, or your life choices. If that sounds like your kind of project, keep reading.
The rules
These aren't guidelines. They're the deal.
1. C++23 or don't bother
We compile with Clang, -std=c++23, and zero warnings. If your code needs a
polyfill for std::format or can't handle designated initializers, it
doesn't belong here. The standard library is your only dependency. Zero
external C++ libraries. Not even Boost.
// ✓ yes
auto msg = std::format("building {} (depth={})", name, depth);
// ✗ no
auto msg = fmt::format("building {} (depth={})", name, depth);
2. Every new module mirrors the existing structure
include/kappa/{module}/
├── types.hpp # enums, structs, parse/validate declarations
├── {feature}.hpp # public interface
src/{module}/
├── types.cpp # implementations
├── backend_a.cpp # per-variant generators
├── backend_b.cpp
└── install.cpp # dispatch + orchestration
If you're adding a feature, look at src/service/ or src/boot/ for the
pattern. If your new module doesn't look like those, you're doing it wrong.
3. Never suppress type safety
There is no as any, no @ts-ignore, no reinterpret_cast abuse, and
no void* unless you're talking to the kernel. If the type system is
fighting you, you're fighting the design. Fix the design.
4. Use the namespace. All of it.
namespace kappa::module {
// everything goes here
} // namespace kappa::module
No using namespace std; at file scope. No anonymous namespaces for
functions that are used across files — extract to util.hpp instead.
5. Thread safety is not optional
The scheduler is multithreaded. If you touch shared state, you own the
lock. std::mutex, std::atomic, std::condition_variable — use them
correctly or don't use them at all. If you don't know what memory_order
means, stay out of the scheduler.
6. Tests are shell scripts. For now.
Integration tests live in test.sh and test-init-switch.sh. They
exercise the CLI. If you add a subcommand, add a test. C++ unit tests
are welcome — set up Google Test or Catch2 in CMake and we'll merge it.
./test.sh # 31 tests, must pass
./test-init-switch.sh # 23 tests, must pass
7. Backward compatibility is mandatory
The .kap DSL is the contract. You can add keywords. You cannot remove
them. You can extend syntax. You cannot break existing configs. If your
change means someone's config.kap stops parsing, it doesn't ship.
How to contribute
Pick something
Good first issues:
- Adding a 6th init system backend
- Adding a 3rd bootloader backend
- C++ unit test framework setup
- Shell completion scripts
Ambitious issues:
- Binary package support (pre-built caches)
- Remote build farm (distcc-style)
- Signed package verification
- Filesystem overlay activation (like Nix profiles)
Send a PR
- Fork the repo
- Create a branch:
feat/my-thingorfix/my-bug - Write code that follows the rules above
- Run
./test.sh && ./test-init-switch.sh— both must pass - Open a PR against
main
PR requirements
- Build must pass:
cmake --build buildwith zero warnings - Tests must pass: both shell test suites
- No commented-out code. No dead code. No TODO without a date.
- Commit messages in imperative:
Add runit backendnotAdded runit backend
What we won't merge
systemd-only features. If it can't work on at least two init systems, it goes in asystemdpackage definition, not in kappa.- Dependency on a specific distro. Kappa runs on any Linux kernel. No
hardcoded paths to
/usr/lib/systemd, no assumptions about/etc/os-release. - Abstract nonsense. FactoryFactoryBuilder patterns. Premature generalization. If you need three layers of indirection to add a feature, the feature is too complicated.
- AI slop. If it looks like ChatGPT wrote it, it gets rejected. We can tell. Write code like a human who's been doing this for a decade.
Communication
We don't have a Discord. We don't have a forum. Open an issue. Write a clear title, a reproduction case, and what you expected. We'll respond when we respond.
If you want to propose a major feature, open an issue first. Surprise PRs that rewrite half the codebase get closed without review.
Kappa is 0.1.0. Everything is subject to change except the rules above.