diff --git a/getpkg/src/main.cpp b/getpkg/src/main.cpp index 964f8ba..680e2a1 100644 --- a/getpkg/src/main.cpp +++ b/getpkg/src/main.cpp @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -324,38 +325,128 @@ int publish_tool(int argc, char* argv[]) { } int update_tool(int argc, char* argv[]) { - std::cout << "Updating getpkg itself..." << std::endl; - - // First try to update getpkg itself - char* fakeArgv[] = {argv[0], (char*)"install", (char*)"getpkg"}; - int result = install_tool(3, fakeArgv); - if (result != 0) { - std::cerr << "Warning: Failed to update getpkg (may not be published)" << std::endl; - // Continue with updating other tools instead of failing - } - - // Then update all installed tools std::string home = get_home(); std::filesystem::path configDir = std::filesystem::path(home) / ".config/getpkg"; - std::cout << "Updating all installed tools..." << std::endl; - bool anyUpdates = false; + // Collect all installed tools + std::vector> updateResults; // name, status, version + + // Capture stdout to process install_tool output + auto processToolUpdate = [&](const std::string& toolName) -> std::tuple { + // Redirect stdout and stderr to capture output + std::stringstream buffer; + std::stringstream errBuffer; + std::streambuf* oldOut = std::cout.rdbuf(buffer.rdbuf()); + std::streambuf* oldErr = std::cerr.rdbuf(errBuffer.rdbuf()); + + char* toolArgv[] = {argv[0], (char*)"install", (char*)toolName.c_str()}; + int result = install_tool(3, toolArgv); + + // Restore stdout and stderr + std::cout.rdbuf(oldOut); + std::cerr.rdbuf(oldErr); + + std::string output = buffer.str(); + std::string status = "Failed"; + std::string version = "-"; + + if (result == 0) { + if (output.find("is already up to date") != std::string::npos) { + status = "Up to date"; + } else if (output.find("Installed " + toolName + " successfully") != std::string::npos) { + // Check if it was an update or fresh install + if (output.find("Updating " + toolName) != std::string::npos) { + status = "Updated"; + } else { + status = "Installed"; + } + } + + // Try to get version from config + std::filesystem::path toolInfoPath = configDir / (toolName + ".json"); + if (std::filesystem::exists(toolInfoPath)) { + std::ifstream tfile(toolInfoPath); + json toolInfo; + tfile >> toolInfo; + version = toolInfo.value("version", "-"); + if (!version.empty() && version.back() == '\n') version.pop_back(); + // If version is empty, try to show something useful + if (version.empty() || version == "-") { + version = "installed"; + } + } + } + + return std::make_tuple(status, version); + }; + + // First update getpkg itself + auto [getpkgStatus, getpkgVersion] = processToolUpdate("getpkg"); + updateResults.push_back(std::make_tuple("getpkg", getpkgStatus, getpkgVersion)); + + // Then update all other installed tools if (std::filesystem::exists(configDir)) { for (const auto& entry : std::filesystem::directory_iterator(configDir)) { if (entry.path().extension() == ".json") { std::string tname = entry.path().stem(); - if (tname != "getpkg") { // Skip getpkg since we already tried it - std::cout << "Checking " << tname << "..." << std::endl; - char* toolArgv[] = {argv[0], (char*)"install", (char*)tname.c_str()}; - if (install_tool(3, toolArgv) == 0) { - anyUpdates = true; - } + if (tname != "getpkg") { // Skip getpkg since we already did it + auto [status, version] = processToolUpdate(tname); + updateResults.push_back(std::make_tuple(tname, status, version)); } } } } - std::cout << "Update complete." << std::endl; + // Display results in a table + std::cout << std::endl; + std::cout << "+" << std::string(25, '-') << "+" << std::string(15, '-') << "+" << std::string(20, '-') << "+" << std::endl; + std::cout << "|" << std::setw(25) << std::left << " Tool" + << "|" << std::setw(15) << std::left << " Status" + << "|" << std::setw(20) << std::left << " Version" + << "|" << std::endl; + std::cout << "+" << std::string(25, '-') << "+" << std::string(15, '-') << "+" << std::string(20, '-') << "+" << std::endl; + + int updatedCount = 0; + int upToDateCount = 0; + int failedCount = 0; + + for (const auto& [name, status, version] : updateResults) { + std::string displayStatus = status; + if (status == "Updated") { + displayStatus = "✓ " + status; + updatedCount++; + } else if (status == "Up to date") { + displayStatus = "• " + status; + upToDateCount++; + } else { + displayStatus = "✗ " + status; + failedCount++; + } + + std::cout << "|" << std::setw(25) << std::left << (" " + name) + << "|" << std::setw(15) << std::left << (" " + displayStatus) + << "|" << std::setw(20) << std::left << (" " + version) + << "|" << std::endl; + } + + std::cout << "+" << std::string(25, '-') << "+" << std::string(15, '-') << "+" << std::string(20, '-') << "+" << std::endl; + std::cout << std::endl; + + // Summary + std::cout << "Update Summary: "; + if (updatedCount > 0) { + std::cout << updatedCount << " updated"; + } + if (upToDateCount > 0) { + if (updatedCount > 0) std::cout << ", "; + std::cout << upToDateCount << " already up to date"; + } + if (failedCount > 0) { + if (updatedCount > 0 || upToDateCount > 0) std::cout << ", "; + std::cout << failedCount << " failed"; + } + std::cout << std::endl; + return 0; }