feat: system-agnostic package manager — 5 inits, 2 bootloaders, parallel scheduler
Complete rewrite of kappa from a sequential build tool into a
system-agnostic package manager with runtime init switching.
Core additions:
- 5 init system backends: systemd, openrc, s6, runit, dinit
(service file generation, enable/disable, init_paths)
- 2 bootloader backends: grub, limine (config generation, fallback entries)
- Parallel scheduler with worker pool, depth-based priority (Beta/Alpha/Zeta),
atomic claiming, dependency tracking, deduplication, and failure propagation
- Init-switch impact analysis: only rebuild packages using ${enabledinit}
- Init-agnostic service definitions: flat NamedService blocks replace
per-init nesting
- Package conflicts: mutual incompatibility detection in resolver
- System groups: init-agnostic group creation in DSL
- Init-agnostic hostname/timezone: direct /etc/hostname and /etc/localtime writes
- Source tarball caching at /kappa/cache/ with atomic write-then-rename
- Package recipe caching with remote fetching and version comparison
- remotes = [...] block in system config for package repositories
- Auto-fetch: rebuild resolves missing packages from remotes
- uninstall phase in package definitions
- ${enabledinit} eval variable for init-conditional builds
- Shared util module (to_lower, shell_escape)
- 54 integration tests across two shell test suites
- Comprehensive README and CONTRIBUTING guide
Bug fixes from review:
- CRITICAL: Replace std::system() with fork+execvp (command injection)
- CRITICAL: Fix scheduler deadlock on successful completion
- CRITICAL: Fix rebuild init/kernel/bootloader change detection
- HIGH: Fix path traversal via unsanitized package names in cache
- HIGH: Fix TOCTOU race in cache write with atomic rename
- HIGH: Fix formatter dropping remotes/imports blocks
- HIGH: Fix formatter stripping empty-string assert values
- HIGH: Fix formatter non-idempotent output (sorted key iteration)
- HIGH: Populate ${enabledinit} from boot.init in BuildStep
- MEDIUM: Fix data race on non-atomic scheduler stop flag
- MEDIUM: Fix compute_depths() traversal direction
- MEDIUM: Add runit to doctor supported-init warning
- MEDIUM: Extract to_lower/shell_escape to shared kappa::util
- MEDIUM: Consolidate generator declarations in headers
This commit is contained in:
+137
@@ -0,0 +1,137 @@
|
||||
# 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 `-Werror`. 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.
|
||||
|
||||
```cpp
|
||||
// ✓ 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.
|
||||
|
||||
```cpp
|
||||
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.
|
||||
|
||||
```sh
|
||||
./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
|
||||
|
||||
1. Fork the repo
|
||||
2. Create a branch: `feat/my-thing` or `fix/my-bug`
|
||||
3. Write code that follows the rules above
|
||||
4. Run `./test.sh && ./test-init-switch.sh` — both must pass
|
||||
5. Open a PR against `main`
|
||||
|
||||
### PR requirements
|
||||
|
||||
- Build must pass: `cmake --build build` with 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 backend` not `Added runit backend`
|
||||
|
||||
## What we won't merge
|
||||
|
||||
- **`systemd`-only features.** If it can't work on at least two init systems,
|
||||
it goes in a `systemd` package 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.
|
||||
Reference in New Issue
Block a user