diff --git a/CLAUDE.md b/CLAUDE.md index 4cf7601..d0849fd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -60,12 +60,10 @@ export CMAKE_BUILD_TYPE="Release" getpkg manages a tool ecosystem by: - Installing tools to `~/.local/bin/getpkg//` -- Managing bash completions and aliases via `~/.bashrc_dropshell_tool` +- Managing bash completions and aliases via `~/.bashrc_getpkg` - Storing tool metadata in `~/.config/getpkg/` - Publishing/downloading tools via getbin.xyz object storage -Each tool includes a `getpkg-config.json` with aliases and setup scripts. - ## Publishing Requirements Publishing requires the `SOS_WRITE_TOKEN` environment variable for authentication to the object storage system. \ No newline at end of file diff --git a/getpkg/src/BashrcEditor.cpp b/getpkg/src/BashrcEditor.cpp index 7c45a34..6b212c4 100644 --- a/getpkg/src/BashrcEditor.cpp +++ b/getpkg/src/BashrcEditor.cpp @@ -10,7 +10,7 @@ namespace dropshelltool { - const std::filesystem::path DROPSHELL_RC_PATH = std::filesystem::path(std::getenv("HOME")) / ".bashrc_dropshell_tool"; + const std::filesystem::path DROPSHELL_RC_PATH = std::filesystem::path(std::getenv("HOME")) / ".bashrc_getpkg"; static const std::filesystem::path BASHRC_PATH = std::filesystem::path(std::getenv("HOME")) / ".bashrc"; std::string removeWhitespace(const std::string &s) diff --git a/getpkg/src/GetbinClient.cpp b/getpkg/src/GetbinClient.cpp index 60313f6..e939282 100644 --- a/getpkg/src/GetbinClient.cpp +++ b/getpkg/src/GetbinClient.cpp @@ -14,7 +14,7 @@ using json = nlohmann::json; -static constexpr const char* SERVER_HOST = "tools.dropshell.app"; +static constexpr const char* SERVER_HOST = "getpkg.xyz"; GetbinClient::GetbinClient() {} diff --git a/getpkg/src/assert.hpp b/getpkg/src/assert.hpp index eb421dd..2620e1e 100644 --- a/getpkg/src/assert.hpp +++ b/getpkg/src/assert.hpp @@ -4,6 +4,7 @@ #include #include #include +#include // for std::remove_if // execinfo.h not available in Alpine Linux - using alternative approach #include #include diff --git a/getpkg/src/main.cpp b/getpkg/src/main.cpp index a8c76be..d06c781 100644 --- a/getpkg/src/main.cpp +++ b/getpkg/src/main.cpp @@ -4,26 +4,26 @@ getpkg - installs the specified tool (same as getpkg install ) - confirms getpkg is fully initialised: - - adds a line to the user's .bashrc file to source the getpkg ~/.bashrc_dropshell_tool script, if it doesn't exist - - creates an empty ~/.bashrc_dropshell_tool script if it doesn't exist + - adds a line to the user's .bashrc file to source the getpkg ~/.bashrc_getpkg script, if it doesn't exist + - creates an empty ~/.bashrc_getpkg script if it doesn't exist - creates the ~/.config/getpkg directory, if it does not exist - creates the ~/.local/bin/getpkg directory, if it does not exist - checks if ~/.config/getpkg/tool_name.json file exists. If it does, it will update the tool if the version is older than the remote version. If it doesn't exist, it will install the tool. - - removes the tool from the user's system if it is already installed, and the tool's entries in the ~/.bashrc_dropshell_tool script - - downloads the tool archive (tgz) from tools.dropshell.app (tool_name:ARCH), where ARCH is the architecture of the user's system, and unpacks it to the new tool directory: ~/.local/bin/getpkg// - - sets the PATH to include the tool directory, by modifying the ~/.bashrc_dropshell_tool script - - adds an entry for autocompletion for the tool to the ~/.bashrc_dropshell_tool script, where autocomplete just runs autocomplete - - creates a ~/.config/getpkg/tool_name.json file, which contains the tool's name, version (by running version), hash from tools.dropshell.app, and architecture + - removes the tool from the user's system if it is already installed, and the tool's entries in the ~/.bashrc_getpkg script + - downloads the tool archive (tgz) from getpkg.xyz (tool_name:ARCH), where ARCH is the architecture of the user's system, and unpacks it to the new tool directory: ~/.local/bin/getpkg// + - sets the PATH to include the tool directory, by modifying the ~/.bashrc_getpkg script + - adds an entry for autocompletion for the tool to the ~/.bashrc_getpkg script, where autocomplete just runs autocomplete + - creates a ~/.config/getpkg/tool_name.json file, which contains the tool's name, version (by running version), hash from getpkg.xyz, and architecture - if setup_script.sh exists, run the script getpkg install - legacy syntax for installing a tool (same as getpkg ) getpkg publish - - creates a tgz archive of the folder, and uploads it to tools.dropshell.app, a simple object server. + - creates a tgz archive of the folder, and uploads it to getpkg.xyz, a simple object server. - prints the URL and hash of the uploaded archive - - uses the token from env variable SOS_WRITE_TOKEN to write to tools.dropshell.app + - uses the token from env variable SOS_WRITE_TOKEN to write to getpkg.xyz getpkg update - updates getpkg itself, and then runs install on all already installed tools @@ -268,12 +268,12 @@ int publish_tool(int argc, char* argv[]) { if (envToken && std::strlen(envToken) > 0) { token = envToken; } else { - std::filesystem::path tokenPath = std::filesystem::path(home) / ".config/tools.dropshell.app/write_token.txt"; + 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 tools.dropshell.app write token: "; + std::cout << "Enter getpkg.xyz write token: "; std::getline(std::cin, token); std::filesystem::create_directories(tokenPath.parent_path()); std::ofstream tfile(tokenPath); diff --git a/getpkg/test.sh b/getpkg/test.sh index 11bab76..73f2e52 100755 --- a/getpkg/test.sh +++ b/getpkg/test.sh @@ -1,11 +1,259 @@ #!/bin/bash -set -euo pipefail +# Don't use set -e because we want to continue even if tests fail +set -uo pipefail PROJECT="getpkg" - SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +GETPKG="${SCRIPT_DIR}/output/${PROJECT}" +TEST_DIR="${SCRIPT_DIR}/test_temp" +TEST_TOOL_NAME="test-tool-$RANDOM" +TEST_ARCH=$(uname -m | sed 's/x86_64/x86_64/;s/arm64/aarch64/;s/aarch64/aarch64/') -VERSION=$("${SCRIPT_DIR}/output/${PROJECT}" version) +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color -echo "Version: $VERSION" +# Test counters +TESTS_PASSED=0 +TESTS_FAILED=0 + +# Function to print test results +print_test_result() { + local test_name="$1" + local result="$2" + if [ "$result" -eq 0 ]; then + echo -e "${GREEN}✓${NC} $test_name" + ((TESTS_PASSED++)) + else + echo -e "${RED}✗${NC} $test_name" + ((TESTS_FAILED++)) + fi +} + +# Function to cleanup test artifacts +cleanup() { + echo -e "\n${YELLOW}Cleaning up test artifacts...${NC}" + + # Remove local test directories + rm -rf "$TEST_DIR" + rm -rf ~/.config/getpkg/"${TEST_TOOL_NAME}.json" 2>/dev/null || true + rm -rf ~/.local/bin/getpkg/"${TEST_TOOL_NAME}" 2>/dev/null || true + + # Remove test tool from bashrc_getpkg if it exists + if [ -f ~/.bashrc_getpkg ]; then + sed -i "/${TEST_TOOL_NAME}/d" ~/.bashrc_getpkg 2>/dev/null || true + fi + + # Clean up from getpkg.xyz if we have write access + if [ -n "${SOS_WRITE_TOKEN:-}" ]; then + echo "Attempting to clean up test objects from getpkg.xyz..." + + # Get the hash of our test tool if it exists + HASH=$(curl -s "https://getpkg.xyz/hash/${TEST_TOOL_NAME}:${TEST_ARCH}" 2>/dev/null || echo "") + if [ -n "$HASH" ] && [ "$HASH" != "null" ] && [ "$HASH" != "Not found" ]; then + echo "Found test tool hash: $HASH" + # Delete the object + curl -s -H "Authorization: Bearer ${SOS_WRITE_TOKEN}" \ + "https://getpkg.xyz/deleteobject?hash=${HASH}" >/dev/null 2>&1 || true + echo "Cleaned up test tool from getpkg.xyz" + fi + else + echo "Note: SOS_WRITE_TOKEN not set, cannot clean up remote test objects" + fi +} + +# Set up trap to ensure cleanup runs +trap cleanup EXIT + +# Create test directory +mkdir -p "$TEST_DIR" + +echo -e "${YELLOW}Running getpkg tests...${NC}\n" + +# Test 1: Version command +echo "Test 1: Version command" +VERSION=$("$GETPKG" version 2>&1) || VERSION="" +if [[ "$VERSION" =~ ^[0-9]{4}\.[0-9]{4}\.[0-9]{4}$ ]]; then + print_test_result "Version format (YYYY.MMDD.HHMM)" 0 +else + print_test_result "Version format (YYYY.MMDD.HHMM)" 1 +fi + +# Test 2: Help command +echo -e "\nTest 2: Help command" +HELP_OUTPUT=$("$GETPKG" help 2>&1) || HELP_OUTPUT="" +if [[ "$HELP_OUTPUT" =~ "Usage: getpkg" ]]; then + print_test_result "Help command output" 0 +else + print_test_result "Help command output" 1 +fi + +# Test 3: Autocomplete command +echo -e "\nTest 3: Autocomplete command" +AUTOCOMPLETE_OUTPUT=$("$GETPKG" autocomplete 2>&1) || AUTOCOMPLETE_OUTPUT="" +if [[ "$AUTOCOMPLETE_OUTPUT" =~ "install" ]] && [[ "$AUTOCOMPLETE_OUTPUT" =~ "publish" ]]; then + print_test_result "Autocomplete command output" 0 +else + print_test_result "Autocomplete command output" 1 +fi + +# Test 4: Create command +echo -e "\nTest 4: Create command" +CREATE_DIR="${TEST_DIR}/${TEST_TOOL_NAME}" +"$GETPKG" create "$TEST_TOOL_NAME" "$CREATE_DIR" >/dev/null 2>&1 || true +if [ -d "$CREATE_DIR" ] && [ -f "$CREATE_DIR/setup_script.sh" ]; then + print_test_result "Create tool directory" 0 +else + print_test_result "Create tool directory" 1 +fi + +# Test 5: Create test tool binary +echo -e "\nTest 5: Create test tool" +cat > "$CREATE_DIR/${TEST_TOOL_NAME}" << 'EOF' +#!/bin/bash +case "$1" in + version) + echo "1.0.0" + ;; + autocomplete) + echo "subcommand1" + echo "subcommand2" + ;; + *) + echo "Test tool - $1" + ;; +esac +EOF +chmod +x "$CREATE_DIR/${TEST_TOOL_NAME}" + + +if [ -x "$CREATE_DIR/${TEST_TOOL_NAME}" ]; then + print_test_result "Create test tool binary" 0 +else + print_test_result "Create test tool binary" 1 +fi + +# Test 6: Publish command (requires SOS_WRITE_TOKEN) +if [ -n "${SOS_WRITE_TOKEN:-}" ]; then + echo -e "\nTest 6: Publish command" + PUBLISH_OUTPUT=$("$GETPKG" publish "${TEST_TOOL_NAME}:${TEST_ARCH}" "$CREATE_DIR" 2>&1) || PUBLISH_OUTPUT="" + if [[ "$PUBLISH_OUTPUT" =~ "Published!" ]] && [[ "$PUBLISH_OUTPUT" =~ "URL:" ]] && [[ "$PUBLISH_OUTPUT" =~ "Hash:" ]]; then + print_test_result "Publish tool to getpkg.xyz" 0 + + # Extract hash for later cleanup + PUBLISHED_HASH=$(echo "$PUBLISH_OUTPUT" | grep "Hash:" | cut -d' ' -f2) + + # Test 7: Check if published tool exists + echo -e "\nTest 7: Check published tool exists" + EXISTS_CHECK=$(curl -s "https://getpkg.xyz/exists/${TEST_TOOL_NAME}:${TEST_ARCH}" 2>/dev/null || echo "error") + if [[ "$EXISTS_CHECK" != "error" ]] && [[ "$EXISTS_CHECK" != "false" ]]; then + print_test_result "Published tool exists on server" 0 + else + print_test_result "Published tool exists on server" 1 + fi + + # Test 8: Install command + echo -e "\nTest 8: Install command" + INSTALL_OUTPUT=$("$GETPKG" install "$TEST_TOOL_NAME" 2>&1) || INSTALL_OUTPUT="" + if [[ "$INSTALL_OUTPUT" =~ "Installed ${TEST_TOOL_NAME} successfully" ]] || [[ "$INSTALL_OUTPUT" =~ "${TEST_TOOL_NAME} is already up to date" ]]; then + print_test_result "Install tool from getpkg.xyz" 0 + + # Test 9: Check installed files + echo -e "\nTest 9: Check installed files" + if [ -f ~/.config/getpkg/"${TEST_TOOL_NAME}.json" ] && [ -d ~/.local/bin/getpkg/"${TEST_TOOL_NAME}" ]; then + print_test_result "Tool files installed correctly" 0 + else + print_test_result "Tool files installed correctly" 1 + fi + + # Test 10: Check bashrc_getpkg modifications + echo -e "\nTest 10: Check bashrc modifications" + if [ -f ~/.bashrc_getpkg ] && grep -q "${TEST_TOOL_NAME}" ~/.bashrc_getpkg; then + print_test_result "Bashrc modifications for PATH and autocomplete" 0 + else + print_test_result "Bashrc modifications for PATH and autocomplete" 1 + fi + + # Test 11: Direct tool name install (shortcut syntax) + echo -e "\nTest 11: Direct tool install syntax" + # First remove the tool + rm -rf ~/.local/bin/getpkg/"${TEST_TOOL_NAME}" + rm -f ~/.config/getpkg/"${TEST_TOOL_NAME}.json" + + DIRECT_INSTALL_OUTPUT=$("$GETPKG" "$TEST_TOOL_NAME" 2>&1) || DIRECT_INSTALL_OUTPUT="" + if [[ "$DIRECT_INSTALL_OUTPUT" =~ "Installed ${TEST_TOOL_NAME} successfully" ]] || [[ "$DIRECT_INSTALL_OUTPUT" =~ "${TEST_TOOL_NAME} is already up to date" ]]; then + print_test_result "Direct tool name install syntax" 0 + else + print_test_result "Direct tool name install syntax" 1 + fi + + # Test 12: Update already installed tool (should say up to date) + echo -e "\nTest 12: Update check for installed tool" + UPDATE_OUTPUT=$("$GETPKG" install "$TEST_TOOL_NAME" 2>&1) || UPDATE_OUTPUT="" + if [[ "$UPDATE_OUTPUT" =~ "${TEST_TOOL_NAME} is already up to date" ]]; then + print_test_result "Update check recognizes up-to-date tool" 0 + else + print_test_result "Update check recognizes up-to-date tool" 1 + fi + + else + print_test_result "Install tool from getpkg.xyz" 1 + # Skip dependent tests + print_test_result "Tool files installed correctly" 1 + print_test_result "Bashrc modifications for PATH and autocomplete" 1 + print_test_result "Direct tool name install syntax" 1 + print_test_result "Update check recognizes up-to-date tool" 1 + fi + + else + print_test_result "Publish tool to getpkg.xyz" 1 + # Skip dependent tests + print_test_result "Published tool exists on server" 1 + print_test_result "Install tool from getpkg.xyz" 1 + print_test_result "Tool files installed correctly" 1 + print_test_result "Bashrc modifications for PATH and autocomplete" 1 + print_test_result "Direct tool name install syntax" 1 + print_test_result "Update check recognizes up-to-date tool" 1 + fi +else + echo -e "\n${YELLOW}Skipping publish/install tests (SOS_WRITE_TOKEN not set)${NC}" + echo "To run full tests, set SOS_WRITE_TOKEN environment variable" +fi + +# Test 13: Invalid tool name validation +echo -e "\nTest 13: Invalid tool name validation" +INVALID_OUTPUT=$("$GETPKG" install "../evil-tool" 2>&1) || INVALID_OUTPUT="" +if [[ "$INVALID_OUTPUT" =~ "Invalid tool name" ]]; then + print_test_result "Invalid tool name rejection" 0 +else + print_test_result "Invalid tool name rejection" 1 +fi + +# Test 14: Update command (if we have tools installed) +if [ -d ~/.config/getpkg ] && [ "$(ls -A ~/.config/getpkg/*.json 2>/dev/null | wc -l)" -gt 0 ]; then + echo -e "\nTest 14: Update command" + UPDATE_ALL_OUTPUT=$("$GETPKG" update 2>&1) || UPDATE_ALL_OUTPUT="" + if [[ "$UPDATE_ALL_OUTPUT" =~ "Update complete" ]]; then + print_test_result "Update all tools command" 0 + else + print_test_result "Update all tools command" 1 + fi +else + echo -e "\n${YELLOW}Skipping update all test (no tools installed)${NC}" +fi + +# Print summary +echo -e "\n${YELLOW}Test Summary:${NC}" +echo -e "Tests passed: ${GREEN}${TESTS_PASSED}${NC}" +echo -e "Tests failed: ${RED}${TESTS_FAILED}${NC}" + +if [ "$TESTS_FAILED" -eq 0 ]; then + echo -e "\n${GREEN}All tests passed!${NC}" + exit 0 +else + echo -e "\n${RED}Some tests failed!${NC}" + exit 1 +fi \ No newline at end of file diff --git a/sos/sos b/sos/sos index 6011122..d5fe274 100755 --- a/sos/sos +++ b/sos/sos @@ -20,7 +20,7 @@ Usage: sos upload [label:tag ...] Example: - sos upload tools.dropshell.app ./file.txt file:latest + sos upload getbin.xyz ./file.txt file:latest This will upload the file to the server, and return the download URL.