|
|
|
@ -58,6 +58,7 @@
|
|
|
|
|
#include "DropshellScriptManager.hpp"
|
|
|
|
|
#include "GetbinClient.hpp"
|
|
|
|
|
#include "MigrationManager.hpp"
|
|
|
|
|
#include "ServerManager.hpp"
|
|
|
|
|
#include "archive_tgz.hpp"
|
|
|
|
|
#include "hash.hpp"
|
|
|
|
|
#include <iostream>
|
|
|
|
@ -164,25 +165,47 @@ int install_tool(int argc, char* argv[]) {
|
|
|
|
|
std::filesystem::path configDir = std::filesystem::path(home) / ".config/getpkg";
|
|
|
|
|
std::filesystem::path binDir = std::filesystem::path(home) / ".getpkg" / toolName;
|
|
|
|
|
std::filesystem::path archivePath = tempDir.path() / (toolName + ".tgz");
|
|
|
|
|
std::filesystem::path toolInfoPath = configDir / (toolName + ".json");
|
|
|
|
|
|
|
|
|
|
// Initialize ServerManager and get server list
|
|
|
|
|
ServerManager serverManager;
|
|
|
|
|
if (!serverManager.loadConfiguration()) {
|
|
|
|
|
std::cerr << "Failed to load server configuration" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> servers = serverManager.getServers();
|
|
|
|
|
if (servers.empty()) {
|
|
|
|
|
std::cerr << "No servers configured" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize PackageMetadataManager
|
|
|
|
|
PackageMetadataManager packageManager(configDir);
|
|
|
|
|
if (!packageManager.ensurePackagesDirectory()) {
|
|
|
|
|
std::cerr << "Failed to create packages directory" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if tool needs update or install
|
|
|
|
|
if (std::filesystem::exists(toolInfoPath)) {
|
|
|
|
|
bool isUpdate = false;
|
|
|
|
|
PackageMetadata existingMetadata;
|
|
|
|
|
if (packageManager.packageExists(toolName)) {
|
|
|
|
|
// Tool exists, check if update needed
|
|
|
|
|
std::ifstream tfile(toolInfoPath);
|
|
|
|
|
json toolInfo;
|
|
|
|
|
tfile >> toolInfo;
|
|
|
|
|
tfile.close();
|
|
|
|
|
existingMetadata = packageManager.loadPackageMetadata(toolName);
|
|
|
|
|
if (!existingMetadata.isValid()) {
|
|
|
|
|
std::cerr << "Warning: Invalid existing package metadata for " << toolName << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string localHash = toolInfo.value("hash", "");
|
|
|
|
|
std::string localArch = toolInfo.value("arch", arch);
|
|
|
|
|
std::string localHash = existingMetadata.hash;
|
|
|
|
|
std::string localArch = existingMetadata.arch.empty() ? arch : existingMetadata.arch;
|
|
|
|
|
|
|
|
|
|
// Get remote hash to compare - use the same arch that was originally installed
|
|
|
|
|
GetbinClient getbin;
|
|
|
|
|
// Get remote hash to compare - use multi-server GetbinClient
|
|
|
|
|
GetbinClient getbin(servers);
|
|
|
|
|
std::string remoteHash;
|
|
|
|
|
if (getbin.getHash(toolName, localArch, remoteHash) && !remoteHash.empty()) {
|
|
|
|
|
if (localHash != remoteHash) {
|
|
|
|
|
std::cout << "Updating " << toolName << "..." << std::endl;
|
|
|
|
|
isUpdate = true;
|
|
|
|
|
} else {
|
|
|
|
|
std::cout << toolName << " is already up to date." << std::endl;
|
|
|
|
|
return 0;
|
|
|
|
@ -190,6 +213,7 @@ int install_tool(int argc, char* argv[]) {
|
|
|
|
|
} else {
|
|
|
|
|
// If we can't get remote hash, assume update is needed
|
|
|
|
|
std::cout << "Updating " << toolName << "..." << std::endl;
|
|
|
|
|
isUpdate = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
std::cout << "Installing " << toolName << "..." << std::endl;
|
|
|
|
@ -209,9 +233,10 @@ int install_tool(int argc, char* argv[]) {
|
|
|
|
|
if (std::filesystem::exists(binDir))
|
|
|
|
|
std::filesystem::remove_all(binDir);
|
|
|
|
|
|
|
|
|
|
// Download tool - try arch-specific version first, then universal fallback
|
|
|
|
|
GetbinClient getbin2;
|
|
|
|
|
// Download tool using multi-server GetbinClient - try arch-specific version first, then universal fallback
|
|
|
|
|
GetbinClient getbin2(servers);
|
|
|
|
|
std::string downloadArch = arch;
|
|
|
|
|
std::string sourceServer;
|
|
|
|
|
|
|
|
|
|
// Progress callback for downloads
|
|
|
|
|
auto progressCallback = [&toolName](size_t downloaded, size_t total) -> bool {
|
|
|
|
@ -236,6 +261,12 @@ int install_tool(int argc, char* argv[]) {
|
|
|
|
|
}
|
|
|
|
|
clearAndPrint("Downloading " + toolName + "... done\n");
|
|
|
|
|
|
|
|
|
|
// Find which server provided the package
|
|
|
|
|
if (!getbin2.findPackageServer(toolName, downloadArch, sourceServer)) {
|
|
|
|
|
// Fallback to first server if we can't determine the source
|
|
|
|
|
sourceServer = servers[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unpack tool
|
|
|
|
|
std::cout << "Unpacking..." << std::flush;
|
|
|
|
|
if (!common::unpack_tgz(archivePath.string(), binDir.string())) {
|
|
|
|
@ -271,16 +302,11 @@ int install_tool(int argc, char* argv[]) {
|
|
|
|
|
std::cerr << "Warning: Failed to get version for " << toolName << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save tool info
|
|
|
|
|
json toolInfo = {
|
|
|
|
|
{"name", toolName},
|
|
|
|
|
{"version", version},
|
|
|
|
|
{"hash", hash},
|
|
|
|
|
{"arch", downloadArch}
|
|
|
|
|
};
|
|
|
|
|
std::ofstream toolInfoFile(toolInfoPath);
|
|
|
|
|
toolInfoFile << toolInfo.dump(2);
|
|
|
|
|
toolInfoFile.close();
|
|
|
|
|
// Create and save enhanced package metadata
|
|
|
|
|
PackageMetadata metadata(toolName, version, hash, downloadArch, sourceServer);
|
|
|
|
|
if (!packageManager.savePackageMetadata(metadata)) {
|
|
|
|
|
std::cerr << "Warning: Failed to save package metadata for " << toolName << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run setup script if exists
|
|
|
|
|
std::filesystem::path setupScriptPath = binDir / "setup_script.sh";
|
|
|
|
@ -296,11 +322,27 @@ int install_tool(int argc, char* argv[]) {
|
|
|
|
|
|
|
|
|
|
int publish_tool(int argc, char* argv[]) {
|
|
|
|
|
if (argc < 4) {
|
|
|
|
|
std::cerr << "Usage: getpkg publish <tool_name:ARCH> <folder>" << std::endl;
|
|
|
|
|
std::cerr << "Usage: getpkg publish [--server <url>] <tool_name:ARCH> <folder>" << std::endl;
|
|
|
|
|
std::cerr << " getpkg publish <tool_name:ARCH> <folder>" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
std::string labeltag = argv[2];
|
|
|
|
|
std::string folder = argv[3];
|
|
|
|
|
|
|
|
|
|
// Parse arguments for --server option
|
|
|
|
|
std::string targetServer;
|
|
|
|
|
std::string labeltag;
|
|
|
|
|
std::string folder;
|
|
|
|
|
int argIndex = 2;
|
|
|
|
|
|
|
|
|
|
if (argc >= 5 && std::string(argv[2]) == "--server") {
|
|
|
|
|
targetServer = argv[3];
|
|
|
|
|
labeltag = argv[4];
|
|
|
|
|
folder = argv[5];
|
|
|
|
|
argIndex = 5;
|
|
|
|
|
} else {
|
|
|
|
|
labeltag = argv[2];
|
|
|
|
|
folder = argv[3];
|
|
|
|
|
argIndex = 3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If no ARCH is provided (no colon in labeltag), append ":universal" for cross-platform tools
|
|
|
|
|
if (labeltag.find(':') == std::string::npos) {
|
|
|
|
@ -315,6 +357,49 @@ int publish_tool(int argc, char* argv[]) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize ServerManager
|
|
|
|
|
ServerManager serverManager;
|
|
|
|
|
if (!serverManager.loadConfiguration()) {
|
|
|
|
|
std::cerr << "Failed to load server configuration" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Determine target server
|
|
|
|
|
std::string publishServer;
|
|
|
|
|
if (!targetServer.empty()) {
|
|
|
|
|
// User specified a server, validate it exists in configuration
|
|
|
|
|
std::vector<std::string> servers = serverManager.getServers();
|
|
|
|
|
if (std::find(servers.begin(), servers.end(), targetServer) == servers.end()) {
|
|
|
|
|
std::cerr << "Error: Server '" << targetServer << "' is not configured" << std::endl;
|
|
|
|
|
std::cerr << "Use 'getpkg server add " << targetServer << "' to add it first" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
publishServer = targetServer;
|
|
|
|
|
} else {
|
|
|
|
|
// Use default publish server (first server with write token)
|
|
|
|
|
publishServer = serverManager.getDefaultPublishServer();
|
|
|
|
|
if (publishServer.empty()) {
|
|
|
|
|
std::cerr << "Error: No servers with write tokens configured" << std::endl;
|
|
|
|
|
std::cerr << "Use 'getpkg server add <url>' and provide a write token" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get write token for the target server
|
|
|
|
|
std::string token = serverManager.getWriteToken(publishServer);
|
|
|
|
|
if (token.empty()) {
|
|
|
|
|
// Check environment variable as fallback
|
|
|
|
|
const char* envToken = std::getenv("SOS_WRITE_TOKEN");
|
|
|
|
|
if (envToken && std::strlen(envToken) > 0) {
|
|
|
|
|
token = envToken;
|
|
|
|
|
} else {
|
|
|
|
|
std::cerr << "Error: No write token found for server '" << publishServer << "'" << std::endl;
|
|
|
|
|
std::cerr << "Set SOS_WRITE_TOKEN environment variable or configure token for this server" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string home = get_home();
|
|
|
|
|
std::filesystem::path archivePath = std::filesystem::path(home) / ".tmp" / (labeltag + ".tgz");
|
|
|
|
|
std::filesystem::create_directories(archivePath.parent_path());
|
|
|
|
@ -323,24 +408,10 @@ int publish_tool(int argc, char* argv[]) {
|
|
|
|
|
std::cerr << "Failed to create archive." << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
std::string token;
|
|
|
|
|
const char* envToken = std::getenv("SOS_WRITE_TOKEN");
|
|
|
|
|
if (envToken && std::strlen(envToken) > 0) {
|
|
|
|
|
token = envToken;
|
|
|
|
|
} else {
|
|
|
|
|
std::filesystem::path tokenPath = std::filesystem::path(home) / ".config/getpkg.xyz/write_token.txt";
|
|
|
|
|
if (std::filesystem::exists(tokenPath)) {
|
|
|
|
|
std::ifstream tfile(tokenPath);
|
|
|
|
|
std::getline(tfile, token);
|
|
|
|
|
} else {
|
|
|
|
|
std::cout << "Enter getpkg.xyz write token: ";
|
|
|
|
|
std::getline(std::cin, token);
|
|
|
|
|
std::filesystem::create_directories(tokenPath.parent_path());
|
|
|
|
|
std::ofstream tfile(tokenPath);
|
|
|
|
|
tfile << token << std::endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
GetbinClient getbin;
|
|
|
|
|
|
|
|
|
|
// Initialize GetbinClient with server list
|
|
|
|
|
std::vector<std::string> servers = serverManager.getServers();
|
|
|
|
|
GetbinClient getbin(servers);
|
|
|
|
|
std::string url, hash;
|
|
|
|
|
|
|
|
|
|
// Progress callback for upload
|
|
|
|
@ -354,13 +425,14 @@ int publish_tool(int argc, char* argv[]) {
|
|
|
|
|
return true; // Continue upload
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::cout << "Publishing to " << publishServer << "..." << std::endl;
|
|
|
|
|
std::cout << "Uploading..." << std::flush;
|
|
|
|
|
if (!getbin.upload(archivePath.string(), url, hash, token, uploadProgressCallback)) {
|
|
|
|
|
std::cerr << "\rFailed to upload archive." << std::endl;
|
|
|
|
|
if (!getbin.upload(publishServer, archivePath.string(), url, hash, token, uploadProgressCallback)) {
|
|
|
|
|
std::cerr << "\rFailed to upload archive to " << publishServer << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
clearAndPrint("Uploading... done\n");
|
|
|
|
|
std::cout << "Published! URL: " << url << "\nHash: " << hash << std::endl;
|
|
|
|
|
std::cout << "Published to " << publishServer << "! URL: " << url << "\nHash: " << hash << std::endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -368,6 +440,25 @@ int update_tool(int argc, char* argv[]) {
|
|
|
|
|
std::string home = get_home();
|
|
|
|
|
std::filesystem::path configDir = std::filesystem::path(home) / ".config/getpkg";
|
|
|
|
|
|
|
|
|
|
// Initialize ServerManager and PackageMetadataManager
|
|
|
|
|
ServerManager serverManager;
|
|
|
|
|
if (!serverManager.loadConfiguration()) {
|
|
|
|
|
std::cerr << "Failed to load server configuration" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> servers = serverManager.getServers();
|
|
|
|
|
if (servers.empty()) {
|
|
|
|
|
std::cerr << "No servers configured" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PackageMetadataManager packageManager(configDir);
|
|
|
|
|
if (!packageManager.ensurePackagesDirectory()) {
|
|
|
|
|
std::cerr << "Failed to create packages directory" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Structure to hold tool information
|
|
|
|
|
struct ToolInfo {
|
|
|
|
|
std::string name;
|
|
|
|
@ -375,29 +466,43 @@ int update_tool(int argc, char* argv[]) {
|
|
|
|
|
std::string remoteHash;
|
|
|
|
|
std::string arch;
|
|
|
|
|
std::string version;
|
|
|
|
|
std::string sourceServer;
|
|
|
|
|
bool needsUpdate = false;
|
|
|
|
|
std::string status = "Up to date";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::vector<ToolInfo> tools;
|
|
|
|
|
|
|
|
|
|
// Collect all 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();
|
|
|
|
|
|
|
|
|
|
ToolInfo tool;
|
|
|
|
|
tool.name = tname;
|
|
|
|
|
|
|
|
|
|
// Read local tool info
|
|
|
|
|
std::ifstream tfile(entry.path());
|
|
|
|
|
// Collect all installed tools using PackageMetadataManager
|
|
|
|
|
std::vector<std::string> installedPackages = packageManager.listInstalledPackages();
|
|
|
|
|
for (const std::string& toolName : installedPackages) {
|
|
|
|
|
ToolInfo tool;
|
|
|
|
|
tool.name = toolName;
|
|
|
|
|
|
|
|
|
|
// Load package metadata
|
|
|
|
|
PackageMetadata metadata = packageManager.loadPackageMetadata(toolName);
|
|
|
|
|
if (metadata.isValid()) {
|
|
|
|
|
tool.localHash = metadata.hash;
|
|
|
|
|
tool.arch = metadata.arch.empty() ? get_arch() : metadata.arch;
|
|
|
|
|
tool.version = metadata.version;
|
|
|
|
|
tool.sourceServer = metadata.sourceServer;
|
|
|
|
|
|
|
|
|
|
if (tool.version.empty() || tool.version == "-") {
|
|
|
|
|
tool.version = "installed";
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Fallback to legacy format if new format fails
|
|
|
|
|
std::filesystem::path legacyPath = configDir / (toolName + ".json");
|
|
|
|
|
if (std::filesystem::exists(legacyPath)) {
|
|
|
|
|
std::ifstream tfile(legacyPath);
|
|
|
|
|
if (tfile.good()) {
|
|
|
|
|
json toolInfo;
|
|
|
|
|
tfile >> toolInfo;
|
|
|
|
|
tool.localHash = toolInfo.value("hash", "");
|
|
|
|
|
tool.arch = toolInfo.value("arch", get_arch());
|
|
|
|
|
tool.version = toolInfo.value("version", "-");
|
|
|
|
|
tool.sourceServer = "getpkg.xyz"; // Default for legacy
|
|
|
|
|
|
|
|
|
|
if (!tool.version.empty() && tool.version.back() == '\n') {
|
|
|
|
|
tool.version.pop_back();
|
|
|
|
|
}
|
|
|
|
@ -405,10 +510,10 @@ int update_tool(int argc, char* argv[]) {
|
|
|
|
|
tool.version = "installed";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tools.push_back(tool);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tools.push_back(tool);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tools.empty()) {
|
|
|
|
@ -419,14 +524,14 @@ int update_tool(int argc, char* argv[]) {
|
|
|
|
|
// Step 1: Check for updates (with progress)
|
|
|
|
|
std::cout << "Checking " << tools.size() << " tools for updates..." << std::endl;
|
|
|
|
|
|
|
|
|
|
GetbinClient getbin;
|
|
|
|
|
GetbinClient getbin(servers);
|
|
|
|
|
for (size_t i = 0; i < tools.size(); ++i) {
|
|
|
|
|
auto& tool = tools[i];
|
|
|
|
|
|
|
|
|
|
// Show progress
|
|
|
|
|
std::cout << "\r[" << (i + 1) << "/" << tools.size() << "] Checking " << tool.name << "..." << std::flush;
|
|
|
|
|
|
|
|
|
|
// Check remote hash
|
|
|
|
|
// Check remote hash - use multi-server fallback
|
|
|
|
|
std::string remoteHash;
|
|
|
|
|
if (getbin.getHash(tool.name, tool.arch, remoteHash) && !remoteHash.empty()) {
|
|
|
|
|
tool.remoteHash = remoteHash;
|
|
|
|
@ -498,16 +603,10 @@ int update_tool(int argc, char* argv[]) {
|
|
|
|
|
tool.status = "Updated";
|
|
|
|
|
clearAndPrint("Updated\n");
|
|
|
|
|
|
|
|
|
|
// Re-read version after update
|
|
|
|
|
std::filesystem::path toolInfoPath = configDir / (tool.name + ".json");
|
|
|
|
|
if (std::filesystem::exists(toolInfoPath)) {
|
|
|
|
|
std::ifstream tfile(toolInfoPath);
|
|
|
|
|
json toolInfo;
|
|
|
|
|
tfile >> toolInfo;
|
|
|
|
|
tool.version = toolInfo.value("version", tool.version);
|
|
|
|
|
if (!tool.version.empty() && tool.version.back() == '\n') {
|
|
|
|
|
tool.version.pop_back();
|
|
|
|
|
}
|
|
|
|
|
// Re-read version after update using PackageMetadataManager
|
|
|
|
|
PackageMetadata updatedMetadata = packageManager.loadPackageMetadata(tool.name);
|
|
|
|
|
if (updatedMetadata.isValid()) {
|
|
|
|
|
tool.version = updatedMetadata.version;
|
|
|
|
|
if (tool.version.empty() || tool.version == "-") {
|
|
|
|
|
tool.version = "installed";
|
|
|
|
|
}
|
|
|
|
@ -621,38 +720,73 @@ int hash_command(int argc, char* argv[]) {
|
|
|
|
|
|
|
|
|
|
int unpublish_tool(int argc, char* argv[]) {
|
|
|
|
|
if (argc < 3) {
|
|
|
|
|
std::cerr << "Usage: getpkg unpublish <tool_name[:ARCH]>" << std::endl;
|
|
|
|
|
std::cerr << "Usage: getpkg unpublish [--server <url>] <tool_name[:ARCH]>" << std::endl;
|
|
|
|
|
std::cerr << " getpkg unpublish [--server <url>] <hash>" << std::endl;
|
|
|
|
|
std::cerr << " getpkg unpublish <tool_name[:ARCH]>" << std::endl;
|
|
|
|
|
std::cerr << " getpkg unpublish <hash>" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
std::string target = argv[2];
|
|
|
|
|
|
|
|
|
|
// Get token
|
|
|
|
|
std::string token;
|
|
|
|
|
const char* envToken = std::getenv("SOS_WRITE_TOKEN");
|
|
|
|
|
if (envToken && std::strlen(envToken) > 0) {
|
|
|
|
|
token = envToken;
|
|
|
|
|
} else {
|
|
|
|
|
std::string home = get_home();
|
|
|
|
|
std::filesystem::path tokenPath = std::filesystem::path(home) / ".config/getpkg.xyz/write_token.txt";
|
|
|
|
|
if (std::filesystem::exists(tokenPath)) {
|
|
|
|
|
std::ifstream tfile(tokenPath);
|
|
|
|
|
std::getline(tfile, token);
|
|
|
|
|
} else {
|
|
|
|
|
std::cout << "Enter getpkg.xyz write token: ";
|
|
|
|
|
std::getline(std::cin, token);
|
|
|
|
|
std::filesystem::create_directories(tokenPath.parent_path());
|
|
|
|
|
std::ofstream tfile(tokenPath);
|
|
|
|
|
tfile << token << std::endl;
|
|
|
|
|
// Parse arguments for --server option
|
|
|
|
|
std::string targetServer;
|
|
|
|
|
std::string target;
|
|
|
|
|
|
|
|
|
|
if (argc >= 4 && std::string(argv[2]) == "--server") {
|
|
|
|
|
if (argc < 5) {
|
|
|
|
|
std::cerr << "Usage: getpkg unpublish --server <url> <tool_name[:ARCH]|hash>" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
targetServer = argv[3];
|
|
|
|
|
target = argv[4];
|
|
|
|
|
} else {
|
|
|
|
|
target = argv[2];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (token.empty()) {
|
|
|
|
|
std::cerr << "Error: No write token provided" << std::endl;
|
|
|
|
|
// Initialize ServerManager
|
|
|
|
|
ServerManager serverManager;
|
|
|
|
|
if (!serverManager.loadConfiguration()) {
|
|
|
|
|
std::cerr << "Failed to load server configuration" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GetbinClient getbin;
|
|
|
|
|
// Determine target server
|
|
|
|
|
std::string unpublishServer;
|
|
|
|
|
if (!targetServer.empty()) {
|
|
|
|
|
// User specified a server, validate it exists in configuration
|
|
|
|
|
std::vector<std::string> servers = serverManager.getServers();
|
|
|
|
|
if (std::find(servers.begin(), servers.end(), targetServer) == servers.end()) {
|
|
|
|
|
std::cerr << "Error: Server '" << targetServer << "' is not configured" << std::endl;
|
|
|
|
|
std::cerr << "Use 'getpkg server add " << targetServer << "' to add it first" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
unpublishServer = targetServer;
|
|
|
|
|
} else {
|
|
|
|
|
// Use default publish server (first server with write token)
|
|
|
|
|
unpublishServer = serverManager.getDefaultPublishServer();
|
|
|
|
|
if (unpublishServer.empty()) {
|
|
|
|
|
std::cerr << "Error: No servers with write tokens configured" << std::endl;
|
|
|
|
|
std::cerr << "Use 'getpkg server add <url>' and provide a write token" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get write token for the target server
|
|
|
|
|
std::string token = serverManager.getWriteToken(unpublishServer);
|
|
|
|
|
if (token.empty()) {
|
|
|
|
|
// Check environment variable as fallback
|
|
|
|
|
const char* envToken = std::getenv("SOS_WRITE_TOKEN");
|
|
|
|
|
if (envToken && std::strlen(envToken) > 0) {
|
|
|
|
|
token = envToken;
|
|
|
|
|
} else {
|
|
|
|
|
std::cerr << "Error: No write token found for server '" << unpublishServer << "'" << std::endl;
|
|
|
|
|
std::cerr << "Set SOS_WRITE_TOKEN environment variable or configure token for this server" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize GetbinClient with server list
|
|
|
|
|
std::vector<std::string> servers = serverManager.getServers();
|
|
|
|
|
GetbinClient getbin(servers);
|
|
|
|
|
std::string hash = target;
|
|
|
|
|
|
|
|
|
|
// Check if target looks like a hash (all digits) or a tool name
|
|
|
|
@ -677,8 +811,8 @@ int unpublish_tool(int argc, char* argv[]) {
|
|
|
|
|
|
|
|
|
|
// If a specific architecture was requested, only unpublish that one
|
|
|
|
|
if (!specificArch.empty()) {
|
|
|
|
|
if (!getbin.getHash(toolName, specificArch, hash)) {
|
|
|
|
|
std::cerr << "Failed to get hash for " << target << std::endl;
|
|
|
|
|
if (!getbin.getHash(unpublishServer, toolName, specificArch, hash)) {
|
|
|
|
|
std::cerr << "Failed to get hash for " << target << " on server " << unpublishServer << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -702,14 +836,14 @@ int unpublish_tool(int argc, char* argv[]) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << "Found hash " << hash << " for " << target << std::endl;
|
|
|
|
|
std::cout << "Found hash " << hash << " for " << target << " on " << unpublishServer << std::endl;
|
|
|
|
|
|
|
|
|
|
// Delete the specific architecture
|
|
|
|
|
if (getbin.deleteObject(hash, token)) {
|
|
|
|
|
std::cout << "Successfully unpublished " << target << " (hash: " << hash << ")" << std::endl;
|
|
|
|
|
std::cout << "Successfully unpublished " << target << " from " << unpublishServer << " (hash: " << hash << ")" << std::endl;
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
std::cerr << "Failed to unpublish " << target << std::endl;
|
|
|
|
|
std::cerr << "Failed to unpublish " << target << " from " << unpublishServer << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@ -1104,14 +1238,15 @@ void show_help() {
|
|
|
|
|
std::cout << " uninstall <tool_name> Remove an installed tool" << std::endl;
|
|
|
|
|
std::cout << " Removes tool files, PATH entries, and autocomplete" << std::endl;
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
std::cout << " publish <tool_name[:ARCH]> <folder> Upload a tool to getpkg.xyz" << std::endl;
|
|
|
|
|
std::cout << " publish [--server <url>] <tool_name[:ARCH]> <folder>" << std::endl;
|
|
|
|
|
std::cout << " Upload a tool to a package server" << std::endl;
|
|
|
|
|
std::cout << " ARCH is optional (defaults to 'universal')" << std::endl;
|
|
|
|
|
std::cout << " Requires SOS_WRITE_TOKEN environment variable" << std::endl;
|
|
|
|
|
std::cout << " Uses default publish server if --server not specified" << std::endl;
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
std::cout << " unpublish <tool_name> Remove ALL architectures of a tool" << std::endl;
|
|
|
|
|
std::cout << " unpublish <tool_name:ARCH> Remove specific architecture only" << std::endl;
|
|
|
|
|
std::cout << " unpublish <hash> Remove a tool by hash" << std::endl;
|
|
|
|
|
std::cout << " Requires SOS_WRITE_TOKEN environment variable" << std::endl;
|
|
|
|
|
std::cout << " unpublish [--server <url>] <tool_name> Remove ALL architectures of a tool" << std::endl;
|
|
|
|
|
std::cout << " unpublish [--server <url>] <tool_name:ARCH> Remove specific architecture only" << std::endl;
|
|
|
|
|
std::cout << " unpublish [--server <url>] <hash> Remove a tool by hash" << std::endl;
|
|
|
|
|
std::cout << " Uses default publish server if --server not specified" << std::endl;
|
|
|
|
|
std::cout << " Without :ARCH, removes x86_64, aarch64, and universal versions" << std::endl;
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
std::cout << " update Update getpkg and all installed tools" << std::endl;
|
|
|
|
@ -1128,6 +1263,15 @@ void show_help() {
|
|
|
|
|
std::cout << " clean Clean up orphaned configs and symlinks" << std::endl;
|
|
|
|
|
std::cout << " Removes unused config files and dangling symlinks" << std::endl;
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
std::cout << " server add <url> Add a new package server" << std::endl;
|
|
|
|
|
std::cout << " Adds a server to the configuration for package discovery" << std::endl;
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
std::cout << " server remove <url> Remove a package server" << std::endl;
|
|
|
|
|
std::cout << " Removes a server from the configuration" << std::endl;
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
std::cout << " server list List all configured servers" << std::endl;
|
|
|
|
|
std::cout << " Shows all servers with their status and write token info" << std::endl;
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
std::cout << " version Show getpkg version" << std::endl;
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
std::cout << " help Show this help message" << std::endl;
|
|
|
|
@ -1137,10 +1281,15 @@ void show_help() {
|
|
|
|
|
std::cout << " getpkg install myapp Install myapp" << std::endl;
|
|
|
|
|
std::cout << " getpkg publish myapp:x86_64 ./build Publish architecture-specific build" << std::endl;
|
|
|
|
|
std::cout << " getpkg publish myapp ./build Publish universal build" << std::endl;
|
|
|
|
|
std::cout << " getpkg publish --server example.com myapp ./build Publish to specific server" << std::endl;
|
|
|
|
|
std::cout << " getpkg unpublish myapp Remove ALL architectures of myapp" << std::endl;
|
|
|
|
|
std::cout << " getpkg unpublish myapp:x86_64 Remove only x86_64 version" << std::endl;
|
|
|
|
|
std::cout << " getpkg unpublish --server example.com myapp Remove from specific server" << std::endl;
|
|
|
|
|
std::cout << " getpkg uninstall myapp Remove myapp from system" << std::endl;
|
|
|
|
|
std::cout << " getpkg update Update everything" << std::endl;
|
|
|
|
|
std::cout << " getpkg server add packages.example.com Add a custom package server" << std::endl;
|
|
|
|
|
std::cout << " getpkg server remove packages.example.com Remove a package server" << std::endl;
|
|
|
|
|
std::cout << " getpkg server list List all configured servers" << std::endl;
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
std::cout << "ENVIRONMENT:" << std::endl;
|
|
|
|
|
std::cout << " SOS_WRITE_TOKEN Auth token for publishing tools" << std::endl;
|
|
|
|
@ -1151,6 +1300,163 @@ void show_help() {
|
|
|
|
|
std::cout << " ~/.local/bin/getpkg/ Installed tool binaries" << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int server_command(int argc, char* argv[]) {
|
|
|
|
|
if (argc < 3) {
|
|
|
|
|
std::cerr << "Usage: getpkg server <add|remove|list> [args...]" << std::endl;
|
|
|
|
|
std::cerr << " getpkg server add <url> Add a new server" << std::endl;
|
|
|
|
|
std::cerr << " getpkg server remove <url> Remove a server" << std::endl;
|
|
|
|
|
std::cerr << " getpkg server list List all configured servers" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string subcommand = argv[2];
|
|
|
|
|
ServerManager serverManager;
|
|
|
|
|
|
|
|
|
|
// Load existing configuration
|
|
|
|
|
if (!serverManager.loadConfiguration()) {
|
|
|
|
|
std::cerr << "Failed to load server configuration" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (subcommand == "add") {
|
|
|
|
|
if (argc < 4) {
|
|
|
|
|
std::cerr << "Usage: getpkg server add <url>" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string serverUrl = argv[3];
|
|
|
|
|
|
|
|
|
|
// Validate server URL format
|
|
|
|
|
if (serverUrl.empty()) {
|
|
|
|
|
std::cerr << "Error: Server URL cannot be empty" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove protocol if provided (we'll add it internally)
|
|
|
|
|
if (serverUrl.find("http://") == 0) {
|
|
|
|
|
serverUrl = serverUrl.substr(7);
|
|
|
|
|
} else if (serverUrl.find("https://") == 0) {
|
|
|
|
|
serverUrl = serverUrl.substr(8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove trailing slash if present
|
|
|
|
|
if (!serverUrl.empty() && serverUrl.back() == '/') {
|
|
|
|
|
serverUrl.pop_back();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << "Adding server: " << serverUrl << std::endl;
|
|
|
|
|
|
|
|
|
|
if (serverManager.addServer(serverUrl)) {
|
|
|
|
|
std::cout << "Successfully added server: " << serverUrl << std::endl;
|
|
|
|
|
|
|
|
|
|
// Ask if user wants to add a write token
|
|
|
|
|
std::cout << "Would you like to add a write token for this server? (y/N): ";
|
|
|
|
|
std::string response;
|
|
|
|
|
std::getline(std::cin, response);
|
|
|
|
|
|
|
|
|
|
if (response == "y" || response == "Y" || response == "yes" || response == "Yes") {
|
|
|
|
|
std::cout << "Enter write token for " << serverUrl << ": ";
|
|
|
|
|
std::string token;
|
|
|
|
|
std::getline(std::cin, token);
|
|
|
|
|
|
|
|
|
|
if (!token.empty()) {
|
|
|
|
|
if (serverManager.setWriteToken(serverUrl, token)) {
|
|
|
|
|
std::cout << "Write token added successfully" << std::endl;
|
|
|
|
|
} else {
|
|
|
|
|
std::cerr << "Failed to save write token" << std::endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
std::cerr << "Failed to add server: " << serverUrl << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (subcommand == "remove") {
|
|
|
|
|
if (argc < 4) {
|
|
|
|
|
std::cerr << "Usage: getpkg server remove <url>" << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string serverUrl = argv[3];
|
|
|
|
|
|
|
|
|
|
// Remove protocol if provided
|
|
|
|
|
if (serverUrl.find("http://") == 0) {
|
|
|
|
|
serverUrl = serverUrl.substr(7);
|
|
|
|
|
} else if (serverUrl.find("https://") == 0) {
|
|
|
|
|
serverUrl = serverUrl.substr(8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove trailing slash if present
|
|
|
|
|
if (!serverUrl.empty() && serverUrl.back() == '/') {
|
|
|
|
|
serverUrl.pop_back();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << "Removing server: " << serverUrl << std::endl;
|
|
|
|
|
|
|
|
|
|
if (serverManager.removeServer(serverUrl)) {
|
|
|
|
|
std::cout << "Successfully removed server: " << serverUrl << std::endl;
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
std::cerr << "Failed to remove server: " << serverUrl << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (subcommand == "list") {
|
|
|
|
|
std::vector<std::string> servers = serverManager.getServers();
|
|
|
|
|
|
|
|
|
|
if (servers.empty()) {
|
|
|
|
|
std::cout << "No servers configured" << std::endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
std::cout << "Configured servers:" << std::endl;
|
|
|
|
|
std::cout << "+" << std::string(30, '-') << "+" << std::string(12, '-') << "+" << std::string(15, '-') << "+" << std::endl;
|
|
|
|
|
std::cout << "|" << std::setw(30) << std::left << " Server URL"
|
|
|
|
|
<< "|" << std::setw(12) << std::left << " Default"
|
|
|
|
|
<< "|" << std::setw(15) << std::left << " Write Token"
|
|
|
|
|
<< "|" << std::endl;
|
|
|
|
|
std::cout << "+" << std::string(30, '-') << "+" << std::string(12, '-') << "+" << std::string(15, '-') << "+" << std::endl;
|
|
|
|
|
|
|
|
|
|
std::string defaultServer = serverManager.getDefaultServer();
|
|
|
|
|
|
|
|
|
|
for (const auto& server : servers) {
|
|
|
|
|
bool isDefault = (server == defaultServer);
|
|
|
|
|
bool hasToken = serverManager.hasWriteToken(server);
|
|
|
|
|
|
|
|
|
|
std::string displayUrl = server;
|
|
|
|
|
if (displayUrl.length() > 29) {
|
|
|
|
|
displayUrl = displayUrl.substr(0, 26) + "...";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << "|" << std::setw(30) << std::left << (" " + displayUrl)
|
|
|
|
|
<< "|" << std::setw(12) << std::left << (isDefault ? " Yes" : " No")
|
|
|
|
|
<< "|" << std::setw(15) << std::left << (hasToken ? " Yes" : " No")
|
|
|
|
|
<< "|" << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << "+" << std::string(30, '-') << "+" << std::string(12, '-') << "+" << std::string(15, '-') << "+" << std::endl;
|
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
std::cout << "Total servers: " << servers.size() << std::endl;
|
|
|
|
|
|
|
|
|
|
// Show default publish server if different from default
|
|
|
|
|
std::string defaultPublishServer = serverManager.getDefaultPublishServer();
|
|
|
|
|
if (defaultPublishServer != defaultServer) {
|
|
|
|
|
std::cout << "Default publish server: " << defaultPublishServer << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
std::cerr << "Unknown server subcommand: " << subcommand << std::endl;
|
|
|
|
|
std::cerr << "Use 'getpkg server' for usage information." << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int autocomplete_command(int argc, char* argv[]) {
|
|
|
|
|
std::vector<std::string> args(argv + 2, argv + argc);
|
|
|
|
|
|
|
|
|
@ -1166,6 +1472,7 @@ int autocomplete_command(int argc, char* argv[]) {
|
|
|
|
|
std::cout << "hash\n";
|
|
|
|
|
std::cout << "list\n";
|
|
|
|
|
std::cout << "clean\n";
|
|
|
|
|
std::cout << "server\n";
|
|
|
|
|
std::cout << "help\n";
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -1180,6 +1487,35 @@ int autocomplete_command(int argc, char* argv[]) {
|
|
|
|
|
} else if (subcommand == "uninstall") {
|
|
|
|
|
// For uninstall, list installed tools
|
|
|
|
|
std::filesystem::path configDir = std::filesystem::path(std::getenv("HOME")) / ".config" / "getpkg";
|
|
|
|
|
if (std::filesystem::exists(configDir)) {
|
|
|
|
|
for (const auto& entry : std::filesystem::directory_iterator(configDir)) {
|
|
|
|
|
if (entry.path().extension() == ".json") {
|
|
|
|
|
std::cout << entry.path().stem().string() << "\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (subcommand == "server") {
|
|
|
|
|
// Handle server subcommand autocompletion
|
|
|
|
|
if (args.size() == 1) {
|
|
|
|
|
// Show server subcommands
|
|
|
|
|
std::cout << "add\n";
|
|
|
|
|
std::cout << "remove\n";
|
|
|
|
|
std::cout << "list\n";
|
|
|
|
|
} else if (args.size() == 2 && args[1] == "remove") {
|
|
|
|
|
// For server remove, list configured servers
|
|
|
|
|
ServerManager serverManager;
|
|
|
|
|
if (serverManager.loadConfiguration()) {
|
|
|
|
|
std::vector<std::string> servers = serverManager.getServers();
|
|
|
|
|
for (const auto& server : servers) {
|
|
|
|
|
std::cout << server << "\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (subcommand == "unpublish") {
|
|
|
|
|
// For unpublish, we could suggest installed tools
|
|
|
|
|
std::filesystem::path configDir = std::filesystem::path(std::getenv("HOME")) / ".config" / "getpkg";
|
|
|
|
|
if (std::filesystem::exists(configDir)) {
|
|
|
|
|
for (const auto& entry : std::filesystem::directory_iterator(configDir)) {
|
|
|
|
|
if (entry.path().extension() == ".json") {
|
|
|
|
@ -1327,6 +1663,8 @@ int main(int argc, char* argv[]) {
|
|
|
|
|
return list_packages(argc, argv);
|
|
|
|
|
} else if (command == "clean") {
|
|
|
|
|
return clean_tool(argc, argv);
|
|
|
|
|
} else if (command == "server") {
|
|
|
|
|
return server_command(argc, argv);
|
|
|
|
|
} else if (command == "help") {
|
|
|
|
|
show_help();
|
|
|
|
|
} else {
|
|
|
|
|