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:
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "kappa/boot/types.hpp"
|
||||||
|
|
||||||
|
namespace kappa::boot {
|
||||||
|
|
||||||
|
struct BootSpec {
|
||||||
|
std::string kernel_path; // e.g. "/kappa/boot/kernel"
|
||||||
|
std::string init_path; // e.g. "/kappa/boot/init" (the symlink, stable across init swaps)
|
||||||
|
std::string root; // e.g. "/dev/sda1" or "PARTUUID=xxx"
|
||||||
|
std::string kernel_params; // extra kernel cmdline params
|
||||||
|
std::string init_prev; // previous init symlink for fallback entry; empty = no fallback
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string generate_bootloader_config(Bootloader bl, const BootSpec& spec);
|
||||||
|
std::string generate_limine_config(const BootSpec& spec);
|
||||||
|
std::string generate_grub_config(const BootSpec& spec);
|
||||||
|
|
||||||
|
struct BootloaderInstallResult {
|
||||||
|
bool ok;
|
||||||
|
std::string path;
|
||||||
|
std::string error;
|
||||||
|
};
|
||||||
|
|
||||||
|
BootloaderInstallResult install_bootloader_config(Bootloader bl,
|
||||||
|
const BootSpec& spec,
|
||||||
|
std::string_view prefix = "/");
|
||||||
|
|
||||||
|
} // namespace kappa::boot
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace kappa::boot {
|
||||||
|
|
||||||
|
enum class Bootloader : std::uint8_t {
|
||||||
|
Grub,
|
||||||
|
Limine,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
Bootloader parse_bootloader(std::string_view name);
|
||||||
|
|
||||||
|
std::string_view to_string(Bootloader bl);
|
||||||
|
|
||||||
|
bool is_supported(std::string_view name);
|
||||||
|
|
||||||
|
std::vector<Bootloader> all_bootloaders();
|
||||||
|
|
||||||
|
std::string_view bootloader_description(Bootloader bl);
|
||||||
|
|
||||||
|
struct BootloaderPaths {
|
||||||
|
std::string config_path;
|
||||||
|
std::string install_cmd;
|
||||||
|
};
|
||||||
|
|
||||||
|
BootloaderPaths bootloader_paths(Bootloader bl, std::string_view prefix = "/");
|
||||||
|
|
||||||
|
} // namespace kappa::boot
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kappa/dsl/ast.hpp"
|
||||||
|
#include "kappa/dsl/system.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace kappa::config {
|
||||||
|
|
||||||
|
struct AssertFailure {
|
||||||
|
std::string message;
|
||||||
|
std::string field;
|
||||||
|
std::string expected;
|
||||||
|
std::string actual;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string resolve_field(const dsl::SystemConfig& cfg, std::string_view path);
|
||||||
|
|
||||||
|
std::vector<AssertFailure> evaluate_assertions(const dsl::SystemConfig& cfg);
|
||||||
|
|
||||||
|
std::unordered_map<std::string, dsl::NamedService> resolve_services(
|
||||||
|
const dsl::SystemConfig& cfg,
|
||||||
|
const std::unordered_map<std::string, dsl::PackageDef>& packages);
|
||||||
|
|
||||||
|
} // namespace kappa::config
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kappa/dsl/ast.hpp"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace kappa::fetch {
|
||||||
|
|
||||||
|
struct FetchResult {
|
||||||
|
std::filesystem::path work_dir;
|
||||||
|
std::string error;
|
||||||
|
bool ok() const { return error.empty(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
FetchResult fetch(const dsl::PackageDef& pkg);
|
||||||
|
|
||||||
|
} // namespace kappa::fetch
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace kappa::fetch {
|
||||||
|
|
||||||
|
struct RecipeResult {
|
||||||
|
bool ok = false;
|
||||||
|
std::string path; // path to the cached .kap file
|
||||||
|
std::string version; // version of the fetched package
|
||||||
|
bool updated = false; // true if the cache was updated (remote was newer)
|
||||||
|
std::string error;
|
||||||
|
};
|
||||||
|
|
||||||
|
RecipeResult fetch_recipe(const std::string& name,
|
||||||
|
const std::vector<std::string>& remotes);
|
||||||
|
|
||||||
|
} // namespace kappa::fetch
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kappa/resolve/plan.hpp"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace kappa::install {
|
||||||
|
|
||||||
|
struct InstallResult {
|
||||||
|
bool ok = false;
|
||||||
|
std::string hash;
|
||||||
|
std::filesystem::path store_path;
|
||||||
|
std::string error;
|
||||||
|
};
|
||||||
|
|
||||||
|
InstallResult install(const resolve::BuildStep& step,
|
||||||
|
const std::filesystem::path& work_dir);
|
||||||
|
|
||||||
|
struct DbEntry {
|
||||||
|
std::string name;
|
||||||
|
std::string version;
|
||||||
|
std::string hash;
|
||||||
|
std::string config_hash; // FNV-1a hash of features+config, empty if unknown
|
||||||
|
std::vector<std::string> provides;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<DbEntry> read_installed();
|
||||||
|
bool write_installed(const std::vector<DbEntry>& entries);
|
||||||
|
bool record_generation(const std::vector<std::string>& hashes, int keep);
|
||||||
|
|
||||||
|
std::string compute_config_hash(
|
||||||
|
const std::unordered_map<std::string, dsl::FeatureDef>& features,
|
||||||
|
const std::unordered_map<std::string, std::string>& config);
|
||||||
|
|
||||||
|
} // namespace kappa::install
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kappa/dsl/system.hpp"
|
||||||
|
#include "kappa/resolve/plan.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace kappa::rebuild {
|
||||||
|
|
||||||
|
struct ChangeSet {
|
||||||
|
std::vector<std::string> added;
|
||||||
|
std::vector<std::string> changed;
|
||||||
|
std::vector<std::string> removed;
|
||||||
|
bool kernel_changed = false;
|
||||||
|
bool init_changed = false;
|
||||||
|
bool services_changed = false;
|
||||||
|
bool bootloader_changed = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InitImpact {
|
||||||
|
std::vector<std::string> service_rebuild; // packages needing full rebuild (use ${enabledinit})
|
||||||
|
std::vector<std::string> service_only; // packages needing only service file regeneration
|
||||||
|
std::vector<std::string> skipped; // packages with no services — no action needed
|
||||||
|
};
|
||||||
|
|
||||||
|
ChangeSet compute_changes(const dsl::SystemConfig& cfg);
|
||||||
|
|
||||||
|
InitImpact compute_init_impact(const dsl::SystemConfig& cfg,
|
||||||
|
const resolve::Registry& registry);
|
||||||
|
|
||||||
|
} // namespace kappa::rebuild
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kappa/config/merge.hpp"
|
||||||
|
#include "kappa/dsl/ast.hpp"
|
||||||
|
#include "kappa/dsl/system.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace kappa::resolve {
|
||||||
|
|
||||||
|
struct ResolvedDep {
|
||||||
|
std::string name;
|
||||||
|
std::string version_constraint;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuildStep {
|
||||||
|
std::string name;
|
||||||
|
const dsl::PackageDef* package = nullptr;
|
||||||
|
config::ResolvedPackage resolved;
|
||||||
|
std::vector<ResolvedDep> dependencies;
|
||||||
|
std::string enabled_init; // boot.init value for ${enabledinit}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuildPlan {
|
||||||
|
std::vector<BuildStep> steps;
|
||||||
|
std::vector<std::string> cycles;
|
||||||
|
std::vector<std::string> conflicts;
|
||||||
|
std::vector<std::string> missing;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Registry = std::unordered_map<std::string, dsl::PackageDef>;
|
||||||
|
|
||||||
|
BuildPlan resolve(const dsl::SystemConfig& cfg, const Registry& registry);
|
||||||
|
|
||||||
|
} // namespace kappa::resolve
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kappa/resolve/plan.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace kappa::sched {
|
||||||
|
|
||||||
|
struct SchedResult {
|
||||||
|
bool ok = false;
|
||||||
|
std::vector<std::string> built;
|
||||||
|
std::vector<std::string> failed;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run the build plan with worker-level (-w) and job-level (-j) parallelism.
|
||||||
|
// Workers atomically claim ready packages, build them, and notify dependents.
|
||||||
|
// The same package is never built twice — deduplicated in the dependency graph.
|
||||||
|
//
|
||||||
|
// -w N package-level parallelism (concurrent package builds)
|
||||||
|
// -j N job-level parallelism (make -jN per package)
|
||||||
|
//
|
||||||
|
// The scheduler uses depth-based priority groups (Beta → Alpha → Zeta)
|
||||||
|
// so that deep dependencies unblock as many packages as possible first.
|
||||||
|
SchedResult run(const resolve::BuildPlan& plan,
|
||||||
|
const std::string& work_root,
|
||||||
|
int workers = 1,
|
||||||
|
int jobs = 1);
|
||||||
|
|
||||||
|
} // namespace kappa::sched
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kappa/dsl/ast.hpp"
|
||||||
|
#include "kappa/service/types.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace kappa::service {
|
||||||
|
|
||||||
|
struct ServiceSpec {
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
std::string exec;
|
||||||
|
std::string type;
|
||||||
|
std::string user;
|
||||||
|
std::vector<int> ports;
|
||||||
|
std::unordered_map<std::string, std::string> env;
|
||||||
|
std::string after;
|
||||||
|
std::string restart_policy;
|
||||||
|
std::string working_dir;
|
||||||
|
|
||||||
|
static ServiceSpec from_service_init(const dsl::NamedService& ns);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string generate_systemd_service(const ServiceSpec& spec);
|
||||||
|
std::string generate_s6_service(const ServiceSpec& spec);
|
||||||
|
std::string generate_openrc_service(const ServiceSpec& spec);
|
||||||
|
std::string generate_dinit_service(const ServiceSpec& spec);
|
||||||
|
std::string generate_runit_service(const ServiceSpec& spec);
|
||||||
|
std::string generate_service_file(InitSystem is, const ServiceSpec& spec);
|
||||||
|
|
||||||
|
struct ServiceInstallResult {
|
||||||
|
bool ok;
|
||||||
|
std::string path;
|
||||||
|
std::string error;
|
||||||
|
};
|
||||||
|
|
||||||
|
ServiceInstallResult install_service(
|
||||||
|
InitSystem is,
|
||||||
|
const ServiceSpec& spec,
|
||||||
|
std::string_view prefix = "/");
|
||||||
|
|
||||||
|
} // namespace kappa::service
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace kappa::service {
|
||||||
|
|
||||||
|
enum class InitSystem : std::uint8_t {
|
||||||
|
Systemd,
|
||||||
|
OpenRC,
|
||||||
|
S6,
|
||||||
|
Runit,
|
||||||
|
Dinit,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
InitSystem parse_init_system(std::string_view name);
|
||||||
|
|
||||||
|
std::string_view to_string(InitSystem is);
|
||||||
|
|
||||||
|
bool is_supported(std::string_view name);
|
||||||
|
|
||||||
|
std::vector<InitSystem> all_systems();
|
||||||
|
|
||||||
|
std::string_view init_description(InitSystem is);
|
||||||
|
|
||||||
|
struct InitPaths {
|
||||||
|
std::string service_dir;
|
||||||
|
std::string enable_cmd;
|
||||||
|
std::string disable_cmd;
|
||||||
|
};
|
||||||
|
|
||||||
|
InitPaths init_paths(InitSystem is, std::string_view prefix = "/");
|
||||||
|
|
||||||
|
// Shebangs for s6 execline-based run scripts.
|
||||||
|
// Extracted here so backends share a single definition.
|
||||||
|
inline constexpr std::string_view s6_execline_shebang = "#!/bin/execlineb -P\n";
|
||||||
|
|
||||||
|
} // namespace kappa::service
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kappa/dsl/system.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace kappa::system {
|
||||||
|
|
||||||
|
struct ActivateResult {
|
||||||
|
bool ok;
|
||||||
|
std::string error;
|
||||||
|
};
|
||||||
|
|
||||||
|
ActivateResult write_hostname(const std::string& hostname,
|
||||||
|
std::string_view prefix = "/");
|
||||||
|
|
||||||
|
ActivateResult write_timezone(const std::string& timezone,
|
||||||
|
std::string_view prefix = "/");
|
||||||
|
|
||||||
|
} // namespace kappa::system
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kappa/dsl/ast.hpp"
|
||||||
|
#include "kappa/dsl/system.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace kappa::tools {
|
||||||
|
|
||||||
|
enum class DiagSeverity { Warning, Error };
|
||||||
|
|
||||||
|
struct Diagnostic {
|
||||||
|
DiagSeverity severity;
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Diagnostic> check_package(const dsl::PackageDef& pkg);
|
||||||
|
std::vector<Diagnostic> check_config(const dsl::SystemConfig& cfg);
|
||||||
|
|
||||||
|
} // namespace kappa::tools
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kappa/dsl/ast.hpp"
|
||||||
|
#include "kappa/dsl/system.hpp"
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace kappa::tools {
|
||||||
|
|
||||||
|
void format_package(std::ostream& os, const dsl::PackageDef& pkg);
|
||||||
|
void format_config(std::ostream& os, const dsl::SystemConfig& cfg);
|
||||||
|
|
||||||
|
} // namespace kappa::tools
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace kappa::util {
|
||||||
|
std::string to_lower(std::string_view sv);
|
||||||
|
std::string shell_escape(std::string_view s);
|
||||||
|
}
|
||||||
+100
-41
@@ -9,11 +9,16 @@
|
|||||||
#include "kappa/paths.hpp"
|
#include "kappa/paths.hpp"
|
||||||
#include "kappa/rebuild/rebuild.hpp"
|
#include "kappa/rebuild/rebuild.hpp"
|
||||||
#include "kappa/resolve/plan.hpp"
|
#include "kappa/resolve/plan.hpp"
|
||||||
|
#include "kappa/boot/bootloader.hpp"
|
||||||
#include "kappa/boot/types.hpp"
|
#include "kappa/boot/types.hpp"
|
||||||
|
#include "kappa/sched/scheduler.hpp"
|
||||||
|
#include "kappa/service/service.hpp"
|
||||||
#include "kappa/service/types.hpp"
|
#include "kappa/service/types.hpp"
|
||||||
|
#include "kappa/system/activate.hpp"
|
||||||
#include "kappa/tools/doctor.hpp"
|
#include "kappa/tools/doctor.hpp"
|
||||||
#include "kappa/tools/format.hpp"
|
#include "kappa/tools/format.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <format>
|
#include <format>
|
||||||
@@ -51,7 +56,10 @@ Subcommands:
|
|||||||
Options:
|
Options:
|
||||||
-h, --help Show this help message
|
-h, --help Show this help message
|
||||||
-V, --version Show version information
|
-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)
|
--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;
|
const char* file_arg = nullptr;
|
||||||
|
bool dry_run = false;
|
||||||
for (int i = arg_start + 1; i < argc; ++i) {
|
for (int i = arg_start + 1; i < argc; ++i) {
|
||||||
if (std::string_view(argv[i]) == "-h"
|
if (std::string_view(argv[i]) == "-h"
|
||||||
|| std::string_view(argv[i]) == "--help") {
|
|| std::string_view(argv[i]) == "--help") {
|
||||||
@@ -210,7 +219,8 @@ int main(int argc, char* argv[]) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (std::string_view(argv[i]) == "-j"
|
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;
|
++i;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -218,6 +228,10 @@ int main(int argc, char* argv[]) {
|
|||||||
paths::set_root(argv[++i]);
|
paths::set_root(argv[++i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (std::string_view(argv[i]) == "--dry-run") {
|
||||||
|
dry_run = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!is_flag(argv[i])) {
|
if (!is_flag(argv[i])) {
|
||||||
file_arg = argv[i];
|
file_arg = argv[i];
|
||||||
break;
|
break;
|
||||||
@@ -572,58 +586,103 @@ int main(int argc, char* argv[]) {
|
|||||||
return 0;
|
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()
|
std::cout << cs.added.size() + cs.changed.size()
|
||||||
<< " packages to rebuild\n";
|
<< " packages to rebuild\n";
|
||||||
|
|
||||||
for (auto& name : cs.changed) {
|
auto work_root = paths::temp_dir();
|
||||||
std::cout << " rebuilding " << name << " (changed)\n";
|
auto sr = sched::run(plan, work_root.string(), workers, jobs);
|
||||||
}
|
|
||||||
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 registry = build_registry(cfg);
|
if (!sr.ok) {
|
||||||
auto impact = rebuild::compute_init_impact(cfg, registry);
|
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()) {
|
// Install built packages
|
||||||
std::cout << " full rebuild (uses ${enabledinit}): "
|
for (auto& step : plan.steps) {
|
||||||
<< impact.service_rebuild.size()
|
if (std::find(sr.built.begin(), sr.built.end(), step.name) == sr.built.end())
|
||||||
<< " packages\n";
|
continue;
|
||||||
}
|
auto work_dir = work_root / step.name;
|
||||||
if (!impact.service_only.empty()) {
|
auto inst = install::install(step, work_dir);
|
||||||
std::cout << " service files only: "
|
if (!inst.ok) {
|
||||||
<< impact.service_only.size()
|
std::cerr << "install failed for " << step.name << ": " << inst.error << "\n";
|
||||||
<< " packages\n";
|
return 1;
|
||||||
}
|
|
||||||
if (!impact.skipped.empty()) {
|
|
||||||
std::cout << " no services — skipped: "
|
|
||||||
<< impact.skipped.size() << " packages\n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
if (cs.bootloader_changed) {
|
||||||
auto bl_name = cfg.boot.bootloader;
|
auto bl = kappa::boot::parse_bootloader(cfg.boot.bootloader);
|
||||||
auto bl = kappa::boot::parse_bootloader(bl_name);
|
|
||||||
if (bl != kappa::boot::Bootloader::Unknown) {
|
if (bl != kappa::boot::Bootloader::Unknown) {
|
||||||
std::cout << " bootloader: " << bl_name << " ("
|
boot::BootSpec boot_spec;
|
||||||
<< kappa::boot::bootloader_description(bl) << ")\n";
|
boot_spec.kernel_path = cfg.boot.kernel;
|
||||||
} else {
|
boot_spec.init_path = cfg.boot.init;
|
||||||
std::cout << " bootloader changed (" << bl_name
|
boot_spec.root = cfg.boot.root;
|
||||||
<< " — unrecognized)\n";
|
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;
|
return 0;
|
||||||
} catch (const std::runtime_error& e) {
|
} catch (const std::runtime_error& e) {
|
||||||
handle_parse_error(file_arg, source, e);
|
handle_parse_error(file_arg, source, e);
|
||||||
|
|||||||
+2
-2
@@ -235,7 +235,7 @@ config_template "s6" > /tmp/kappa-test-rebuild-s6.kap
|
|||||||
# The packages list includes "s6" which isn't in the installed DB,
|
# The packages list includes "s6" which isn't in the installed DB,
|
||||||
# so rebuild correctly shows it as a new package to build.
|
# so rebuild correctly shows it as a new package to build.
|
||||||
check "rebuild detects new init package" \
|
check "rebuild detects new init package" \
|
||||||
"$KAPPA_BIN rebuild /tmp/kappa-test-rebuild-s6.kap 2>&1" \
|
"$KAPPA_BIN rebuild --dry-run /tmp/kappa-test-rebuild-s6.kap 2>&1" \
|
||||||
'packages to rebuild'
|
'packages to rebuild'
|
||||||
|
|
||||||
# Now test switching FROM s6 TO systemd
|
# Now test switching FROM s6 TO systemd
|
||||||
@@ -244,7 +244,7 @@ config_template "systemd" > /tmp/kappa-test-rebuild-systemd.kap
|
|||||||
# The rebuild should detect init_changed
|
# The rebuild should detect init_changed
|
||||||
# Note: this requires the installed DB to have the old init hash
|
# Note: this requires the installed DB to have the old init hash
|
||||||
check "rebuild detects init change" \
|
check "rebuild detects init change" \
|
||||||
"$KAPPA_BIN rebuild /tmp/kappa-test-rebuild-systemd.kap 2>&1" \
|
"$KAPPA_BIN rebuild --dry-run /tmp/kappa-test-rebuild-systemd.kap 2>&1" \
|
||||||
'packages to rebuild'
|
'packages to rebuild'
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in New Issue
Block a user