feat: add repos DSL, index format, and kappa index command

- Add repos { } block to system config with named repos, channels,
  mirrors, and priority

- Add index "name" { } file format for package indexes

- Add kappa index <dir> subcommand that scans .kap files and
  generates index.kap

- parse_index() and build_index() in DSL module

- format_index() roundtrip support in tools module

- validate and format subcommands handle all three formats
  (package, config, index) with automatic detection

- Backward compatible: remotes = [...] still works unchanged
This commit is contained in:
2026-08-04 13:48:25 -04:00
parent 86f578bd4a
commit 4f1ce17ec8
9 changed files with 285 additions and 11 deletions
+55 -9
View File
@@ -52,6 +52,7 @@ Subcommands:
rebuild <config> Compare config to installed state, rebuild changed
list List installed packages
rollback Show available generations
index <dir> Build an index.kap from .kap files in a directory
Options:
-h, --help Show this help message
@@ -196,8 +197,9 @@ int main(int argc, char* argv[]) {
|| (subcommand == "build")
|| (subcommand == "rebuild")
|| (subcommand == "list")
|| (subcommand == "fetch-package")
|| (subcommand == "rollback");
|| (subcommand == "fetch-package")
|| (subcommand == "rollback")
|| (subcommand == "index");
if (!valid_subcommand) {
std::cerr << "error: unknown subcommand '" << subcommand << "'\n\n";
@@ -276,6 +278,29 @@ int main(int argc, char* argv[]) {
return 0;
}
if (subcommand == "index") {
if (file_arg == nullptr) {
std::cerr << "error: no directory specified\n";
return 1;
}
try {
auto idx = dsl::build_index(file_arg);
auto out_path = std::filesystem::path(file_arg) / "index.kap";
std::ofstream out(out_path);
if (!out) {
std::cerr << "error: cannot write " << out_path.string() << "\n";
return 1;
}
tools::format_index(out, idx);
std::cout << idx.packages.size() << " packages indexed → "
<< out_path.string() << "\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "index error: " << e.what() << "\n";
return 1;
}
}
if (subcommand == "fetch-package") {
if (file_arg == nullptr) {
std::cerr << "error: no package name specified\n";
@@ -331,7 +356,8 @@ int main(int argc, char* argv[]) {
if (file_arg == nullptr
&& subcommand != "list"
&& subcommand != "rollback") {
&& subcommand != "rollback"
&& subcommand != "index") {
std::cerr << "error: no input file specified\n";
return 1;
}
@@ -378,6 +404,12 @@ int main(int argc, char* argv[]) {
}
std::cout << "\n";
}
if (!cfg.repos.empty()) {
int total_channels = 0;
for (auto& r : cfg.repos) total_channels += r.channels.size();
std::cout << " repos: " << cfg.repos.size()
<< " (" << total_channels << " channels)\n";
}
return 0;
} catch (const std::runtime_error& e) {
handle_parse_error(file_arg, source, e);
@@ -414,9 +446,17 @@ int main(int argc, char* argv[]) {
return 1;
}
return 0;
} catch (const std::runtime_error& e) {
handle_parse_error(file_arg, source, e);
return 1;
} catch (const std::runtime_error&) {
try {
auto idx = dsl::parse_index(source);
std::cout << file_arg << ": valid index ("
<< idx.name << ", "
<< idx.packages.size() << " packages)\n";
return 0;
} catch (const std::runtime_error& e) {
handle_parse_error(file_arg, source, e);
return 1;
}
}
}
}
@@ -472,9 +512,15 @@ int main(int argc, char* argv[]) {
auto cfg = dsl::parse_system_config(source);
tools::format_config(std::cout, cfg);
return 0;
} catch (const std::runtime_error& e) {
handle_parse_error(file_arg, source, e);
return 1;
} catch (const std::runtime_error&) {
try {
auto idx = dsl::parse_index(source);
tools::format_index(std::cout, idx);
return 0;
} catch (const std::runtime_error& e) {
handle_parse_error(file_arg, source, e);
return 1;
}
}
}
}