feat: wire rebuild pipeline (scheduler \u2192 build \u2192 install \u2192 services \u2192 bootloader)
The rebuild subcommand now executes the full pipeline: - build_registry() -> resolve() -> sched::run() - install::install() for each built package - service::install_service() for enabled services - boot::install_bootloader_config() for bootloader config - system::write_hostname() / write_timezone() for system activation Added --dry-run flag for report-only behavior (backward compat with test suite). Added -w flag for worker parallelism and --dry-run to help. Fixed .gitignore: 'kappa' -> '/kappa' (was matching include/kappa/, preventing header files from being tracked since initial commit). This commit also adds all previously-gitignored header files.
This commit is contained in:
+100
-41
@@ -9,11 +9,16 @@
|
||||
#include "kappa/paths.hpp"
|
||||
#include "kappa/rebuild/rebuild.hpp"
|
||||
#include "kappa/resolve/plan.hpp"
|
||||
#include "kappa/boot/bootloader.hpp"
|
||||
#include "kappa/boot/types.hpp"
|
||||
#include "kappa/sched/scheduler.hpp"
|
||||
#include "kappa/service/service.hpp"
|
||||
#include "kappa/service/types.hpp"
|
||||
#include "kappa/system/activate.hpp"
|
||||
#include "kappa/tools/doctor.hpp"
|
||||
#include "kappa/tools/format.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
@@ -51,7 +56,10 @@ Subcommands:
|
||||
Options:
|
||||
-h, --help Show this help message
|
||||
-V, --version Show version information
|
||||
-j, --jobs <n> Jobs per package (default: 1)
|
||||
-w <n> Concurrent package builds (default: 1)
|
||||
--root <path> Set kappa root directory (default: /usr/local/kappa)
|
||||
--dry-run Report changes without building (rebuild only)
|
||||
)";
|
||||
}
|
||||
|
||||
@@ -198,6 +206,7 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
const char* file_arg = nullptr;
|
||||
bool dry_run = false;
|
||||
for (int i = arg_start + 1; i < argc; ++i) {
|
||||
if (std::string_view(argv[i]) == "-h"
|
||||
|| std::string_view(argv[i]) == "--help") {
|
||||
@@ -210,7 +219,8 @@ int main(int argc, char* argv[]) {
|
||||
return 0;
|
||||
}
|
||||
if (std::string_view(argv[i]) == "-j"
|
||||
|| std::string_view(argv[i]) == "--jobs") {
|
||||
|| std::string_view(argv[i]) == "--jobs"
|
||||
|| (std::string_view(argv[i]) == "-w" && i + 1 < argc)) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
@@ -218,6 +228,10 @@ int main(int argc, char* argv[]) {
|
||||
paths::set_root(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if (std::string_view(argv[i]) == "--dry-run") {
|
||||
dry_run = true;
|
||||
continue;
|
||||
}
|
||||
if (!is_flag(argv[i])) {
|
||||
file_arg = argv[i];
|
||||
break;
|
||||
@@ -572,58 +586,103 @@ int main(int argc, char* argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dry_run) {
|
||||
// Dry run: report changes without executing
|
||||
std::cout << cs.added.size() + cs.changed.size()
|
||||
<< " packages to rebuild\n";
|
||||
return 0;
|
||||
}
|
||||
auto registry = build_registry(cfg);
|
||||
auto plan = resolve::resolve(cfg, registry);
|
||||
|
||||
if (!plan.missing.empty()) {
|
||||
for (auto& m : plan.missing)
|
||||
std::cerr << "warning: package '" << m << "' not found in registry\n";
|
||||
}
|
||||
if (!plan.cycles.empty()) {
|
||||
std::cerr << "error: dependency cycle detected:\n";
|
||||
for (auto& c : plan.cycles) std::cerr << " " << c << "\n";
|
||||
return 1;
|
||||
}
|
||||
if (plan.steps.empty()) {
|
||||
std::cout << "nothing to build\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Parse -w/-j flags after subcommand
|
||||
int workers = 1, jobs = 1;
|
||||
for (int i = arg_start + 1; i < argc; ++i) {
|
||||
auto arg = std::string_view(argv[i]);
|
||||
if (arg == "-w" && i + 1 < argc) workers = std::stoi(argv[++i]);
|
||||
if ((arg == "-j" || arg == "--jobs") && i + 1 < argc) jobs = std::stoi(argv[++i]);
|
||||
}
|
||||
|
||||
// Build
|
||||
std::cout << cs.added.size() + cs.changed.size()
|
||||
<< " packages to rebuild\n";
|
||||
|
||||
for (auto& name : cs.changed) {
|
||||
std::cout << " rebuilding " << name << " (changed)\n";
|
||||
}
|
||||
for (auto& name : cs.added) {
|
||||
std::cout << " building " << name << " (new)\n";
|
||||
}
|
||||
if (cs.kernel_changed) { std::cout << " kernel changed\n"; }
|
||||
if (cs.init_changed) {
|
||||
auto is_name = cfg.boot.init;
|
||||
auto is = kappa::service::parse_init_system(is_name);
|
||||
if (is != kappa::service::InitSystem::Unknown) {
|
||||
std::cout << " init system: " << is_name << " ("
|
||||
<< kappa::service::init_description(is) << ")\n";
|
||||
} else {
|
||||
std::cout << " init changed (" << is_name
|
||||
<< " — unrecognized)\n";
|
||||
}
|
||||
auto work_root = paths::temp_dir();
|
||||
auto sr = sched::run(plan, work_root.string(), workers, jobs);
|
||||
|
||||
auto registry = build_registry(cfg);
|
||||
auto impact = rebuild::compute_init_impact(cfg, registry);
|
||||
if (!sr.ok) {
|
||||
std::cerr << sr.failed.size() << " packages failed to build\n";
|
||||
for (auto& f : sr.failed) std::cerr << " " << f << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!impact.service_rebuild.empty()) {
|
||||
std::cout << " full rebuild (uses ${enabledinit}): "
|
||||
<< impact.service_rebuild.size()
|
||||
<< " packages\n";
|
||||
}
|
||||
if (!impact.service_only.empty()) {
|
||||
std::cout << " service files only: "
|
||||
<< impact.service_only.size()
|
||||
<< " packages\n";
|
||||
}
|
||||
if (!impact.skipped.empty()) {
|
||||
std::cout << " no services — skipped: "
|
||||
<< impact.skipped.size() << " packages\n";
|
||||
// Install built packages
|
||||
for (auto& step : plan.steps) {
|
||||
if (std::find(sr.built.begin(), sr.built.end(), step.name) == sr.built.end())
|
||||
continue;
|
||||
auto work_dir = work_root / step.name;
|
||||
auto inst = install::install(step, work_dir);
|
||||
if (!inst.ok) {
|
||||
std::cerr << "install failed for " << step.name << ": " << inst.error << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate service files for enabled services
|
||||
auto is = kappa::service::parse_init_system(cfg.boot.init);
|
||||
if (is != kappa::service::InitSystem::Unknown && !cfg.services.empty()) {
|
||||
auto resolved = config::resolve_services(cfg, registry);
|
||||
for (auto& sref : cfg.services) {
|
||||
if (!sref.enable) continue;
|
||||
auto it = resolved.find(sref.name);
|
||||
if (it == resolved.end()) {
|
||||
std::cerr << "warning: service '" << sref.name << "' not found in any package\n";
|
||||
continue;
|
||||
}
|
||||
auto spec = service::ServiceSpec::from_service_init(it->second);
|
||||
auto svc_result = service::install_service(is, spec);
|
||||
if (!svc_result.ok)
|
||||
std::cerr << "service generation failed for " << sref.name << ": " << svc_result.error << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Generate bootloader config
|
||||
if (cs.bootloader_changed) {
|
||||
auto bl_name = cfg.boot.bootloader;
|
||||
auto bl = kappa::boot::parse_bootloader(bl_name);
|
||||
auto bl = kappa::boot::parse_bootloader(cfg.boot.bootloader);
|
||||
if (bl != kappa::boot::Bootloader::Unknown) {
|
||||
std::cout << " bootloader: " << bl_name << " ("
|
||||
<< kappa::boot::bootloader_description(bl) << ")\n";
|
||||
} else {
|
||||
std::cout << " bootloader changed (" << bl_name
|
||||
<< " — unrecognized)\n";
|
||||
boot::BootSpec boot_spec;
|
||||
boot_spec.kernel_path = cfg.boot.kernel;
|
||||
boot_spec.init_path = cfg.boot.init;
|
||||
boot_spec.root = cfg.boot.root;
|
||||
for (auto& [k, v] : cfg.boot.params)
|
||||
boot_spec.kernel_params += k + "=" + v + " ";
|
||||
auto boot_result = boot::install_bootloader_config(bl, boot_spec);
|
||||
if (!boot_result.ok)
|
||||
std::cerr << "bootloader config failed: " << boot_result.error << "\n";
|
||||
}
|
||||
}
|
||||
if (cs.services_changed) { std::cout << " services changed\n"; }
|
||||
|
||||
// Activate system configuration
|
||||
if (!cfg.system.hostname.empty())
|
||||
system::write_hostname(cfg.system.hostname);
|
||||
if (!cfg.system.timezone.empty())
|
||||
system::write_timezone(cfg.system.timezone);
|
||||
|
||||
std::cout << "rebuild complete — " << sr.built.size() << " packages built\n";
|
||||
return 0;
|
||||
} catch (const std::runtime_error& e) {
|
||||
handle_parse_error(file_arg, source, e);
|
||||
|
||||
Reference in New Issue
Block a user