diff --git a/src/main.cpp b/src/main.cpp index b6a9027..14bc196 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -58,6 +58,7 @@ Subcommands: --features "key=on,key2=off" Set feature flags --config "key=val,key2=val2" Set config values remove Remove a package from system config + upgrade Check indexes for newer versions of installed packages Options: -h, --help Show this help message @@ -226,7 +227,8 @@ int main(int argc, char* argv[]) { || (subcommand == "index") || (subcommand == "search") || (subcommand == "add") - || (subcommand == "remove"); + || (subcommand == "remove") + || (subcommand == "upgrade"); if (!valid_subcommand) { std::cerr << "error: unknown subcommand '" << subcommand << "'\n\n"; @@ -540,6 +542,109 @@ int main(int argc, char* argv[]) { } } + if (subcommand == "upgrade") { + auto installed = install::read_installed(); + if (installed.empty()) { + std::cout << "no packages installed\n"; + return 0; + } + + // Build a map of latest versions from cached indexes + std::unordered_map latest; + auto idx_dir = paths::cache_dir() / "indexes"; + std::error_code ec; + if (std::filesystem::exists(idx_dir)) { + for (auto& entry : std::filesystem::directory_iterator(idx_dir, ec)) { + if (ec) break; + if (entry.path().extension() != ".kap") continue; + std::ifstream in(entry.path()); + if (!in) continue; + std::ostringstream buf; + buf << in.rdbuf(); + try { + auto idx = dsl::parse_index(buf.str()); + for (auto& pkg : idx.packages) { + auto it = latest.find(pkg.name); + if (it == latest.end() || pkg.version > it->second) { + latest[pkg.name] = pkg.version; + } + } + } catch (...) { continue; } + } + } + + if (latest.empty()) { + std::cout << "no cached indexes — run kappa fetch-package first\n"; + return 0; + } + + std::vector> upgrades; + for (auto& e : installed) { + auto it = latest.find(e.name); + if (it != latest.end() && it->second > e.version) { + upgrades.emplace_back(e.name, it->second); + } + } + + if (upgrades.empty()) { + std::cout << "all " << installed.size() << " packages up to date\n"; + return 0; + } + + std::cout << upgrades.size() << " upgrade(s) available:\n"; + for (auto& [name, ver] : upgrades) { + // Find old version + std::string old_ver; + for (auto& e : installed) { + if (e.name == name) { old_ver = e.version; break; } + } + std::cout << " " << name << " " << old_ver << " → " << ver << "\n"; + } + + if (dry_run) { + std::cout << "run kappa add for each to apply\n"; + return 0; + } + + // Apply: update system config with new versions + auto config_path = std::filesystem::path(paths::system_dir()) / "config.kap"; + dsl::SystemConfig cfg; + if (std::filesystem::exists(config_path)) { + auto src = read_file(config_path.c_str()); + try { + cfg = dsl::parse_system_config(src); + } catch (...) { cfg = {}; } + } + + for (auto& [name, ver] : upgrades) { + bool found = false; + for (auto& p : cfg.packages) { + if (p.name == name) { + p.version = ver; + found = true; + break; + } + } + if (!found) { + dsl::PackageRef pref; + pref.name = name; + pref.version = ver; + cfg.packages.push_back(std::move(pref)); + } + } + + std::filesystem::create_directories(paths::system_dir(), ec); + std::ofstream out(config_path); + if (!out) { + std::cerr << "error: cannot write config\n"; + return 1; + } + tools::format_config(out, cfg); + + std::cout << "config updated — run kappa rebuild to apply\n"; + return 0; + } + if (subcommand == "fetch-package") { if (file_arg == nullptr) { std::cerr << "error: no package name specified\n";