This commit is contained in:
parent
ce10325a7f
commit
9c64c8cdce
133
getpkg/src/hash.cpp
Normal file
133
getpkg/src/hash.cpp
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#include "hash.hpp"
|
||||||
|
|
||||||
|
#define XXH_INLINE_ALL
|
||||||
|
#include "xxhash.hpp"
|
||||||
|
|
||||||
|
#include "output.hpp"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
uint64_t hash_file(const std::string &path) {
|
||||||
|
// Create hash state
|
||||||
|
XXH64_state_t* const state = XXH64_createState();
|
||||||
|
if (state == nullptr) {
|
||||||
|
error << "Failed to create hash state" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize state with seed 0
|
||||||
|
XXH64_hash_t const seed = 0; /* or any other value */
|
||||||
|
if (XXH64_reset(state, seed) == XXH_ERROR) return 0;
|
||||||
|
|
||||||
|
// Open file
|
||||||
|
std::ifstream file(path, std::ios::binary);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
error << "Failed to open file: " << path << std::endl;
|
||||||
|
XXH64_freeState(state);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read file in chunks and update hash
|
||||||
|
const size_t buffer_size = 4096;
|
||||||
|
char buffer[buffer_size];
|
||||||
|
while (file.read(buffer, buffer_size)) {
|
||||||
|
if (XXH64_update(state, buffer, file.gcount()) == XXH_ERROR) {
|
||||||
|
error << "Failed to update hash" << std::endl;
|
||||||
|
XXH64_freeState(state);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle any remaining bytes
|
||||||
|
if (file.gcount() > 0) {
|
||||||
|
if (XXH64_update(state, buffer, file.gcount()) == XXH_ERROR) {
|
||||||
|
error << "Failed to update hash" << std::endl;
|
||||||
|
XXH64_freeState(state);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get final hash
|
||||||
|
XXH64_hash_t hash = XXH64_digest(state);
|
||||||
|
XXH64_freeState(state);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t hash_directory_recursive(const std::string &path) {
|
||||||
|
// Create hash state
|
||||||
|
XXH64_state_t* const state = XXH64_createState();
|
||||||
|
if (state == nullptr) {
|
||||||
|
error << "Failed to create hash state" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize state with seed 0
|
||||||
|
XXH64_hash_t const seed = 0; /* or any other value */
|
||||||
|
if (XXH64_reset(state, seed) == XXH_ERROR) {
|
||||||
|
error << "Failed to reset hash state" << std::endl;
|
||||||
|
XXH64_freeState(state);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Iterate through all files in directory recursively
|
||||||
|
for (const auto& entry : std::filesystem::recursive_directory_iterator(path)) {
|
||||||
|
if (entry.is_regular_file()) {
|
||||||
|
// Get file hash
|
||||||
|
XXH64_hash_t file_hash = hash_file(entry.path().string());
|
||||||
|
XXH64_update(state, &file_hash, sizeof(file_hash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::filesystem::filesystem_error& e) {
|
||||||
|
error << "Filesystem error: " << e.what() << std::endl;
|
||||||
|
XXH64_freeState(state);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get final hash
|
||||||
|
XXH64_hash_t hash = XXH64_digest(state);
|
||||||
|
XXH64_freeState(state);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t hash_path(const std::string &path) {
|
||||||
|
if (!std::filesystem::exists(path)) {
|
||||||
|
error << "Path does not exist: " << path << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::filesystem::is_directory(path)) {
|
||||||
|
return hash_directory_recursive(path);
|
||||||
|
} else if (std::filesystem::is_regular_file(path)) {
|
||||||
|
return hash_file(path);
|
||||||
|
} else {
|
||||||
|
error << "Path is neither a file nor a directory: " << path << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hash_demo(const std::string & path)
|
||||||
|
{
|
||||||
|
info << "Hashing path: " << path << std::endl;
|
||||||
|
auto start = std::chrono::high_resolution_clock::now();
|
||||||
|
XXH64_hash_t hash = hash_path(path);
|
||||||
|
auto end = std::chrono::high_resolution_clock::now();
|
||||||
|
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||||
|
info << "Hash: " << hash << " (took " << duration.count() << "ms)" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hash_demo_raw(const std::string & path)
|
||||||
|
{
|
||||||
|
if (!std::filesystem::exists(path)) {
|
||||||
|
info << 0 <<std::endl; return 1;
|
||||||
|
}
|
||||||
|
XXH64_hash_t hash = hash_path(path);
|
||||||
|
info << hash << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dropshell
|
22
getpkg/src/hash.hpp
Normal file
22
getpkg/src/hash.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef HASH_HPP
|
||||||
|
#define HASH_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
uint64_t hash_file(const std::string &path);
|
||||||
|
|
||||||
|
uint64_t hash_directory_recursive(const std::string &path);
|
||||||
|
|
||||||
|
uint64_t hash_path(const std::string &path);
|
||||||
|
|
||||||
|
void hash_demo(const std::string & path);
|
||||||
|
|
||||||
|
int hash_demo_raw(const std::string & path);
|
||||||
|
|
||||||
|
} // namespace dropshell
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -59,6 +59,7 @@
|
|||||||
#include "DropshellScriptManager.hpp"
|
#include "DropshellScriptManager.hpp"
|
||||||
#include "GetbinClient.hpp"
|
#include "GetbinClient.hpp"
|
||||||
#include "ArchiveManager.hpp"
|
#include "ArchiveManager.hpp"
|
||||||
|
#include "hash.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -385,6 +386,23 @@ int create_tool(int argc, char* argv[]) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int hash_command(int argc, char* argv[]) {
|
||||||
|
if (argc < 3) {
|
||||||
|
std::cerr << "Usage: getpkg hash <file_or_directory>" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string path = argv[2];
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(path)) {
|
||||||
|
std::cerr << "Error: Path does not exist: " << path << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t hash = dropshell::hash_path(path);
|
||||||
|
std::cout << hash << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int uninstall_tool(int argc, char* argv[]) {
|
int uninstall_tool(int argc, char* argv[]) {
|
||||||
if (argc < 3) {
|
if (argc < 3) {
|
||||||
std::cerr << "Usage: getpkg uninstall <tool_name>" << std::endl;
|
std::cerr << "Usage: getpkg uninstall <tool_name>" << std::endl;
|
||||||
@ -455,6 +473,9 @@ void show_help() {
|
|||||||
std::cout << " create <tool_name> <directory> Create a new tool project" << std::endl;
|
std::cout << " create <tool_name> <directory> Create a new tool project" << std::endl;
|
||||||
std::cout << " Creates directory structure and setup script" << std::endl;
|
std::cout << " Creates directory structure and setup script" << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
std::cout << " hash <file_or_directory> Calculate hash of file or directory" << std::endl;
|
||||||
|
std::cout << " Outputs raw hash value to stdout" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
std::cout << " version Show getpkg version" << std::endl;
|
std::cout << " version Show getpkg version" << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << " help Show this help message" << std::endl;
|
std::cout << " help Show this help message" << std::endl;
|
||||||
@ -500,12 +521,15 @@ publish
|
|||||||
update
|
update
|
||||||
version
|
version
|
||||||
create
|
create
|
||||||
|
hash
|
||||||
help
|
help
|
||||||
)";
|
)";
|
||||||
} else if (command == "version") {
|
} else if (command == "version") {
|
||||||
std::cout << dropshell::VERSION << std::endl;
|
std::cout << dropshell::VERSION << std::endl;
|
||||||
} else if (command == "create") {
|
} else if (command == "create") {
|
||||||
return create_tool(argc, argv);
|
return create_tool(argc, argv);
|
||||||
|
} else if (command == "hash") {
|
||||||
|
return hash_command(argc, argv);
|
||||||
} else if (command == "help") {
|
} else if (command == "help") {
|
||||||
show_help();
|
show_help();
|
||||||
} else {
|
} else {
|
||||||
|
133
getpkg/src/output.cpp
Normal file
133
getpkg/src/output.cpp
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#include "output.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace dropshell
|
||||||
|
{
|
||||||
|
// Mutex to synchronize output
|
||||||
|
std::mutex output_mutex;
|
||||||
|
|
||||||
|
// ANSI colour codes
|
||||||
|
constexpr const char *GREY = "\033[90m";
|
||||||
|
constexpr const char *RESET = "\033[0m";
|
||||||
|
constexpr const char *DEBUG_COLOUR = "\033[36m"; // Cyan
|
||||||
|
// constexpr const char *INFO_COLOUR = "\033[32m"; // Green
|
||||||
|
constexpr const char *INFO_COLOUR = "\033[37m"; // White
|
||||||
|
constexpr const char *WARNING_COLOUR = "\033[33m"; // Yellow
|
||||||
|
constexpr const char *ERROR_COLOUR = "\033[31m"; // Red
|
||||||
|
|
||||||
|
// Tag and colour for each stream
|
||||||
|
struct StreamInfo
|
||||||
|
{
|
||||||
|
const char *tag;
|
||||||
|
const char *colour;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StreamInfo stream_infos[] = {
|
||||||
|
{"[DBG]", DEBUG_COLOUR},
|
||||||
|
{"[INF]", INFO_COLOUR},
|
||||||
|
{"[WRN]", WARNING_COLOUR},
|
||||||
|
{"[ERR]", ERROR_COLOUR}};
|
||||||
|
|
||||||
|
// Custom streambuf to prefix and colour each line
|
||||||
|
class PrefixStreambuf : public std::streambuf
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrefixStreambuf(std::ostream &dest, const char *tag, const char *colour)
|
||||||
|
: dest_(dest), tag_(tag), colour_(colour), at_line_start_(true) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int overflow(int c) override
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(output_mutex);
|
||||||
|
if (c == EOF)
|
||||||
|
return !EOF;
|
||||||
|
if (at_line_start_) // && c != '\n')
|
||||||
|
{
|
||||||
|
dest_ << GREY << tag_ << RESET << ' ' << colour_;
|
||||||
|
at_line_start_ = false;
|
||||||
|
}
|
||||||
|
dest_.put(static_cast<char>(c));
|
||||||
|
if (c == '\n')
|
||||||
|
{
|
||||||
|
dest_ << RESET;
|
||||||
|
at_line_start_ = true;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ostream &dest_;
|
||||||
|
const char *tag_;
|
||||||
|
const char *colour_;
|
||||||
|
bool at_line_start_;
|
||||||
|
};
|
||||||
|
|
||||||
|
PrefixStreambuf debug_buf(std::clog, stream_infos[0].tag, stream_infos[0].colour);
|
||||||
|
PrefixStreambuf info_buf(std::clog, stream_infos[1].tag, stream_infos[1].colour);
|
||||||
|
PrefixStreambuf warning_buf(std::clog, stream_infos[2].tag, stream_infos[2].colour);
|
||||||
|
PrefixStreambuf error_buf(std::cerr, stream_infos[3].tag, stream_infos[3].colour);
|
||||||
|
|
||||||
|
std::ostream debug_stream(&debug_buf);
|
||||||
|
std::ostream info_stream(&info_buf);
|
||||||
|
std::ostream warning_stream(&warning_buf);
|
||||||
|
std::ostream error_stream(&error_buf);
|
||||||
|
|
||||||
|
std::ostream &debug = debug_stream;
|
||||||
|
std::ostream &info = info_stream;
|
||||||
|
std::ostream &warning = warning_stream;
|
||||||
|
std::ostream &error = error_stream;
|
||||||
|
|
||||||
|
std::ostream &rawout = std::cout;
|
||||||
|
std::ostream &rawerr = std::cerr;
|
||||||
|
|
||||||
|
std::ostream &colourstream(sColour colour)
|
||||||
|
{
|
||||||
|
switch (colour)
|
||||||
|
{
|
||||||
|
case sColour::DEBUG:
|
||||||
|
return debug_stream;
|
||||||
|
case sColour::INFO:
|
||||||
|
return info_stream;
|
||||||
|
case sColour::WARNING:
|
||||||
|
return warning_stream;
|
||||||
|
case sColour::ERROR:
|
||||||
|
return error_stream;
|
||||||
|
default:
|
||||||
|
return info_stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetColour(sColour colour, std::ostream &os)
|
||||||
|
{
|
||||||
|
switch (colour)
|
||||||
|
{
|
||||||
|
case sColour::RESET:
|
||||||
|
os << RESET;
|
||||||
|
break;
|
||||||
|
case sColour::DEBUG:
|
||||||
|
os << DEBUG_COLOUR;
|
||||||
|
break;
|
||||||
|
case sColour::INFO:
|
||||||
|
os << INFO_COLOUR;
|
||||||
|
break;
|
||||||
|
case sColour::WARNING:
|
||||||
|
os << WARNING_COLOUR;
|
||||||
|
break;
|
||||||
|
case sColour::ERROR:
|
||||||
|
os << ERROR_COLOUR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchColour::SwitchColour(sColour colour, std::ostream &os) : os_(os), colour_(colour)
|
||||||
|
{
|
||||||
|
SetColour(colour_, os_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchColour::~SwitchColour()
|
||||||
|
{
|
||||||
|
SetColour(sColour::RESET, os_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dropshell
|
87
getpkg/src/output.hpp
Normal file
87
getpkg/src/output.hpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#ifndef OUTPUT_HPP
|
||||||
|
#define OUTPUT_HPP
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
output.hpp and output.cpp - simple output helpers.
|
||||||
|
|
||||||
|
Defines ostreams:
|
||||||
|
|
||||||
|
debug, info, warning, error.
|
||||||
|
|
||||||
|
These ostreams can be used with C++23 print and println, e.g.
|
||||||
|
std::println(debug, "funny variable: {}={}","my_var",my_var);
|
||||||
|
|
||||||
|
Also defines a few helper functions:
|
||||||
|
|
||||||
|
PrintDebug(const std::string& msg); // equivalent to std::println(debug, msg);
|
||||||
|
PrintInfo(const std::string& msg); // equivalent to std::println(info, msg);
|
||||||
|
PrintWarning(const std::string& msg); // equivalent to std::println(warning, msg);
|
||||||
|
PrintError(const std::string& msg); // equivalent to std::println(error, msg);
|
||||||
|
|
||||||
|
Output for these streams for each line is formatted as:
|
||||||
|
[DBG] <message>
|
||||||
|
[INF] <message>
|
||||||
|
[WRN] <message>
|
||||||
|
[ERR] <message>
|
||||||
|
|
||||||
|
The output is coloured, and the tag is printed in grey.
|
||||||
|
|
||||||
|
In addition, when not using any of the above, helper routines for coloring the output of cout and cerr are provided.
|
||||||
|
|
||||||
|
SetColour(std::ostream& os, sColour colour);
|
||||||
|
|
||||||
|
Where sColour is an enum:
|
||||||
|
enum class sColour {
|
||||||
|
RESET,
|
||||||
|
DEBUG,
|
||||||
|
INFO,
|
||||||
|
WARNING,
|
||||||
|
ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Output streams for different log levels
|
||||||
|
extern std::ostream& debug;
|
||||||
|
extern std::ostream& info;
|
||||||
|
extern std::ostream& warning;
|
||||||
|
extern std::ostream& error;
|
||||||
|
|
||||||
|
extern std::ostream& rawout;
|
||||||
|
extern std::ostream& rawerr;
|
||||||
|
|
||||||
|
// Enum for colours
|
||||||
|
enum class sColour {
|
||||||
|
RESET,
|
||||||
|
DEBUG,
|
||||||
|
INFO,
|
||||||
|
WARNING,
|
||||||
|
ERROR
|
||||||
|
};
|
||||||
|
std::ostream& colourstream(sColour colour);
|
||||||
|
|
||||||
|
// Set colour for a stream
|
||||||
|
void SetColour(sColour colour, std::ostream& os = std::cerr);
|
||||||
|
|
||||||
|
class SwitchColour
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SwitchColour(sColour colour, std::ostream& os = std::cerr);
|
||||||
|
~SwitchColour();
|
||||||
|
private:
|
||||||
|
std::ostream& os_;
|
||||||
|
sColour colour_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dropshell
|
||||||
|
|
||||||
|
#endif // OUTPUT_HPP
|
7343
getpkg/src/xxhash.hpp
Normal file
7343
getpkg/src/xxhash.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1
getpkg/temp_test.txt
Normal file
1
getpkg/temp_test.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
test
|
143
getpkg/test.sh
143
getpkg/test.sh
@ -120,8 +120,95 @@ else
|
|||||||
print_test_result "Autocomplete command output" 1
|
print_test_result "Autocomplete command output" 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 4: Create command
|
# Test 4: Hash command
|
||||||
echo -e "\nTest 4: Create command"
|
echo -e "\nTest 4: Hash command"
|
||||||
|
|
||||||
|
# Test 4a: Hash command with missing argument
|
||||||
|
echo "Test 4a: Hash command error handling"
|
||||||
|
HASH_ERROR_OUTPUT=$("$GETPKG" hash 2>&1) || true
|
||||||
|
if [[ "$HASH_ERROR_OUTPUT" =~ "Usage: getpkg hash" ]]; then
|
||||||
|
print_test_result "Hash command error handling (missing argument)" 0
|
||||||
|
else
|
||||||
|
print_test_result "Hash command error handling (missing argument)" 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4b: Hash command with nonexistent file
|
||||||
|
echo "Test 4b: Hash command with nonexistent file"
|
||||||
|
HASH_NONEXISTENT_OUTPUT=$("$GETPKG" hash nonexistent_file.txt 2>&1) || true
|
||||||
|
if [[ "$HASH_NONEXISTENT_OUTPUT" =~ "Error: Path does not exist" ]]; then
|
||||||
|
print_test_result "Hash command error handling (nonexistent file)" 0
|
||||||
|
else
|
||||||
|
print_test_result "Hash command error handling (nonexistent file)" 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4c: Hash command with test file
|
||||||
|
echo "Test 4c: Hash command with test file"
|
||||||
|
TEST_FILE="${TEST_DIR}/test_hash_file.txt"
|
||||||
|
echo "test content for hashing" > "$TEST_FILE"
|
||||||
|
HASH_FILE_OUTPUT=$("$GETPKG" hash "$TEST_FILE" 2>&1) || HASH_FILE_OUTPUT=""
|
||||||
|
if [[ "$HASH_FILE_OUTPUT" =~ ^[0-9]+$ ]]; then
|
||||||
|
print_test_result "Hash command with file (numeric output)" 0
|
||||||
|
|
||||||
|
# Test that the hash is consistent
|
||||||
|
HASH_FILE_OUTPUT2=$("$GETPKG" hash "$TEST_FILE" 2>&1) || HASH_FILE_OUTPUT2=""
|
||||||
|
if [ "$HASH_FILE_OUTPUT" = "$HASH_FILE_OUTPUT2" ]; then
|
||||||
|
print_test_result "Hash command consistency (same file, same hash)" 0
|
||||||
|
else
|
||||||
|
print_test_result "Hash command consistency (same file, same hash)" 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_test_result "Hash command with file (numeric output)" 1
|
||||||
|
print_test_result "Hash command consistency (same file, same hash)" 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4d: Hash command with directory
|
||||||
|
echo "Test 4d: Hash command with directory"
|
||||||
|
TEST_HASH_DIR="${TEST_DIR}/test_hash_dir"
|
||||||
|
mkdir -p "$TEST_HASH_DIR"
|
||||||
|
echo "dir content 1" > "$TEST_HASH_DIR/file1.txt"
|
||||||
|
echo "dir content 2" > "$TEST_HASH_DIR/file2.txt"
|
||||||
|
HASH_DIR_OUTPUT=$("$GETPKG" hash "$TEST_HASH_DIR" 2>&1) || HASH_DIR_OUTPUT=""
|
||||||
|
if [[ "$HASH_DIR_OUTPUT" =~ ^[0-9]+$ ]]; then
|
||||||
|
print_test_result "Hash command with directory (numeric output)" 0
|
||||||
|
|
||||||
|
# Test that directory hash is different from file hash
|
||||||
|
if [ "$HASH_DIR_OUTPUT" != "$HASH_FILE_OUTPUT" ]; then
|
||||||
|
print_test_result "Hash command different for file vs directory" 0
|
||||||
|
else
|
||||||
|
print_test_result "Hash command different for file vs directory" 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_test_result "Hash command with directory (numeric output)" 1
|
||||||
|
print_test_result "Hash command different for file vs directory" 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4e: Hash command compatibility with tr (sos script format)
|
||||||
|
echo "Test 4e: Hash command compatibility with tr command"
|
||||||
|
LOCALHASH=$("$GETPKG" hash "$TEST_FILE" | tr -d '[:space:]') || LOCALHASH=""
|
||||||
|
if [[ "$LOCALHASH" =~ ^[0-9]+$ ]] && [ ${#LOCALHASH} -gt 0 ]; then
|
||||||
|
print_test_result "Hash command compatible with tr -d '[:space:]'" 0
|
||||||
|
else
|
||||||
|
print_test_result "Hash command compatible with tr -d '[:space:]'" 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4f: Hash command appears in autocomplete
|
||||||
|
echo "Test 4f: Hash command in autocomplete"
|
||||||
|
if [[ "$AUTOCOMPLETE_OUTPUT" =~ hash ]]; then
|
||||||
|
print_test_result "Hash command appears in autocomplete" 0
|
||||||
|
else
|
||||||
|
print_test_result "Hash command appears in autocomplete" 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 4g: Hash command appears in help
|
||||||
|
echo "Test 4g: Hash command in help"
|
||||||
|
if [[ "$HELP_OUTPUT" =~ hash.*file_or_directory ]] && [[ "$HELP_OUTPUT" =~ "Outputs raw hash value to stdout" ]]; then
|
||||||
|
print_test_result "Hash command appears in help" 0
|
||||||
|
else
|
||||||
|
print_test_result "Hash command appears in help" 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 5: Create command
|
||||||
|
echo -e "\nTest 5: Create command"
|
||||||
CREATE_DIR="${TEST_DIR}/${TEST_TOOL_NAME}"
|
CREATE_DIR="${TEST_DIR}/${TEST_TOOL_NAME}"
|
||||||
timeout 3 "$GETPKG" create "$TEST_TOOL_NAME" "$CREATE_DIR" >/dev/null 2>&1 || true
|
timeout 3 "$GETPKG" create "$TEST_TOOL_NAME" "$CREATE_DIR" >/dev/null 2>&1 || true
|
||||||
if [ -d "$CREATE_DIR" ] && [ -f "$CREATE_DIR/setup_script.sh" ]; then
|
if [ -d "$CREATE_DIR" ] && [ -f "$CREATE_DIR/setup_script.sh" ]; then
|
||||||
@ -130,8 +217,8 @@ else
|
|||||||
print_test_result "Create tool directory" 1
|
print_test_result "Create tool directory" 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 5: Create test tool binary
|
# Test 6: Create test tool binary
|
||||||
echo -e "\nTest 5: Create test tool"
|
echo -e "\nTest 6: Create test tool"
|
||||||
cat > "$CREATE_DIR/${TEST_TOOL_NAME}" << 'EOF'
|
cat > "$CREATE_DIR/${TEST_TOOL_NAME}" << 'EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@ -156,15 +243,15 @@ else
|
|||||||
print_test_result "Create test tool binary" 1
|
print_test_result "Create test tool binary" 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 5b: Test basic publish functionality
|
# Test 6b: Test basic publish functionality
|
||||||
echo -e "\nTest 5b: Testing basic publish command"
|
echo -e "\nTest 6b: Testing basic publish command"
|
||||||
# Test with a dummy token to see error message
|
# Test with a dummy token to see error message
|
||||||
DUMMY_OUTPUT=$(timeout 3 "$GETPKG" publish "dummy:x86_64" "$CREATE_DIR" 2>&1 <<< "") || true
|
DUMMY_OUTPUT=$(timeout 3 "$GETPKG" publish "dummy:x86_64" "$CREATE_DIR" 2>&1 <<< "") || true
|
||||||
echo "Dummy publish output: $DUMMY_OUTPUT"
|
echo "Dummy publish output: $DUMMY_OUTPUT"
|
||||||
|
|
||||||
# Test 6: Publish command (requires SOS_WRITE_TOKEN)
|
# Test 7: Publish command (requires SOS_WRITE_TOKEN)
|
||||||
if [ -n "${SOS_WRITE_TOKEN:-}" ]; then
|
if [ -n "${SOS_WRITE_TOKEN:-}" ]; then
|
||||||
echo -e "\nTest 6: Publish command with ARCH"
|
echo -e "\nTest 7: Publish command with ARCH"
|
||||||
echo "SOS_WRITE_TOKEN is set: ${#SOS_WRITE_TOKEN} characters"
|
echo "SOS_WRITE_TOKEN is set: ${#SOS_WRITE_TOKEN} characters"
|
||||||
echo "Publishing ${TEST_TOOL_NAME}:${TEST_ARCH} from $CREATE_DIR"
|
echo "Publishing ${TEST_TOOL_NAME}:${TEST_ARCH} from $CREATE_DIR"
|
||||||
echo "Directory contents:"
|
echo "Directory contents:"
|
||||||
@ -190,8 +277,8 @@ if [ -n "${SOS_WRITE_TOKEN:-}" ]; then
|
|||||||
PUBLISHED_HASH=$(echo "$PUBLISH_OUTPUT" | grep "Hash:" | cut -d' ' -f2)
|
PUBLISHED_HASH=$(echo "$PUBLISH_OUTPUT" | grep "Hash:" | cut -d' ' -f2)
|
||||||
echo "Published hash: $PUBLISHED_HASH"
|
echo "Published hash: $PUBLISHED_HASH"
|
||||||
|
|
||||||
# Test 7: Check if published tool exists
|
# Test 8: Check if published tool exists
|
||||||
echo -e "\nTest 7: Check published tool exists"
|
echo -e "\nTest 8: Check published tool exists"
|
||||||
EXISTS_CHECK=$(curl -s "https://getpkg.xyz/exists/${TEST_TOOL_NAME}:${TEST_ARCH}" 2>/dev/null || echo "error")
|
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
|
if [[ "$EXISTS_CHECK" != "error" ]] && [[ "$EXISTS_CHECK" != "false" ]]; then
|
||||||
print_test_result "Published tool exists on server" 0
|
print_test_result "Published tool exists on server" 0
|
||||||
@ -199,30 +286,30 @@ if [ -n "${SOS_WRITE_TOKEN:-}" ]; then
|
|||||||
print_test_result "Published tool exists on server" 1
|
print_test_result "Published tool exists on server" 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 8: Install command
|
# Test 9: Install command
|
||||||
echo -e "\nTest 8: Install command"
|
echo -e "\nTest 9: Install command"
|
||||||
INSTALL_OUTPUT=$(timeout 3 "$GETPKG" install "$TEST_TOOL_NAME" 2>&1) || INSTALL_OUTPUT=""
|
INSTALL_OUTPUT=$(timeout 3 "$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
|
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
|
print_test_result "Install tool from getpkg.xyz" 0
|
||||||
|
|
||||||
# Test 9: Check installed files
|
# Test 10: Check installed files
|
||||||
echo -e "\nTest 9: Check installed files"
|
echo -e "\nTest 10: Check installed files"
|
||||||
if [ -f ~/.config/getpkg/"${TEST_TOOL_NAME}.json" ] && [ -d ~/.local/bin/getpkg/"${TEST_TOOL_NAME}" ]; then
|
if [ -f ~/.config/getpkg/"${TEST_TOOL_NAME}.json" ] && [ -d ~/.local/bin/getpkg/"${TEST_TOOL_NAME}" ]; then
|
||||||
print_test_result "Tool files installed correctly" 0
|
print_test_result "Tool files installed correctly" 0
|
||||||
else
|
else
|
||||||
print_test_result "Tool files installed correctly" 1
|
print_test_result "Tool files installed correctly" 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 10: Check bashrc_getpkg modifications
|
# Test 11: Check bashrc_getpkg modifications
|
||||||
echo -e "\nTest 10: Check bashrc modifications"
|
echo -e "\nTest 11: Check bashrc modifications"
|
||||||
if [ -f ~/.bashrc_getpkg ] && grep -q "${TEST_TOOL_NAME}" ~/.bashrc_getpkg; then
|
if [ -f ~/.bashrc_getpkg ] && grep -q "${TEST_TOOL_NAME}" ~/.bashrc_getpkg; then
|
||||||
print_test_result "Bashrc modifications for PATH and autocomplete" 0
|
print_test_result "Bashrc modifications for PATH and autocomplete" 0
|
||||||
else
|
else
|
||||||
print_test_result "Bashrc modifications for PATH and autocomplete" 1
|
print_test_result "Bashrc modifications for PATH and autocomplete" 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 11: Direct tool name install (shortcut syntax)
|
# Test 12: Direct tool name install (shortcut syntax)
|
||||||
echo -e "\nTest 11: Direct tool install syntax"
|
echo -e "\nTest 12: Direct tool install syntax"
|
||||||
# First remove the tool
|
# First remove the tool
|
||||||
rm -rf ~/.local/bin/getpkg/"${TEST_TOOL_NAME}"
|
rm -rf ~/.local/bin/getpkg/"${TEST_TOOL_NAME}"
|
||||||
rm -f ~/.config/getpkg/"${TEST_TOOL_NAME}.json"
|
rm -f ~/.config/getpkg/"${TEST_TOOL_NAME}.json"
|
||||||
@ -234,8 +321,8 @@ if [ -n "${SOS_WRITE_TOKEN:-}" ]; then
|
|||||||
print_test_result "Direct tool name install syntax" 1
|
print_test_result "Direct tool name install syntax" 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 12: Update already installed tool (should say up to date)
|
# Test 13: Update already installed tool (should say up to date)
|
||||||
echo -e "\nTest 12: Update check for installed tool"
|
echo -e "\nTest 13: Update check for installed tool"
|
||||||
UPDATE_OUTPUT=$(timeout 3 "$GETPKG" install "$TEST_TOOL_NAME" 2>&1) || UPDATE_OUTPUT=""
|
UPDATE_OUTPUT=$(timeout 3 "$GETPKG" install "$TEST_TOOL_NAME" 2>&1) || UPDATE_OUTPUT=""
|
||||||
if [[ "$UPDATE_OUTPUT" =~ ${TEST_TOOL_NAME}\ is\ already\ up\ to\ date ]]; then
|
if [[ "$UPDATE_OUTPUT" =~ ${TEST_TOOL_NAME}\ is\ already\ up\ to\ date ]]; then
|
||||||
print_test_result "Update check recognizes up-to-date tool" 0
|
print_test_result "Update check recognizes up-to-date tool" 0
|
||||||
@ -263,8 +350,8 @@ if [ -n "${SOS_WRITE_TOKEN:-}" ]; then
|
|||||||
print_test_result "Update check recognizes up-to-date tool" 1
|
print_test_result "Update check recognizes up-to-date tool" 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 12b: Publish without ARCH (for cross-platform tools)
|
# Test 13b: Publish without ARCH (for cross-platform tools)
|
||||||
echo -e "\nTest 12b: Publish without ARCH"
|
echo -e "\nTest 13b: Publish without ARCH"
|
||||||
TEST_TOOL_NOARCH="${TEST_TOOL_NAME}-noarch"
|
TEST_TOOL_NOARCH="${TEST_TOOL_NAME}-noarch"
|
||||||
mkdir -p "${TEST_DIR}/${TEST_TOOL_NOARCH}"
|
mkdir -p "${TEST_DIR}/${TEST_TOOL_NOARCH}"
|
||||||
cat > "${TEST_DIR}/${TEST_TOOL_NOARCH}/${TEST_TOOL_NOARCH}" << 'EOF'
|
cat > "${TEST_DIR}/${TEST_TOOL_NOARCH}/${TEST_TOOL_NOARCH}" << 'EOF'
|
||||||
@ -293,8 +380,8 @@ EOF
|
|||||||
print_test_result "Publish tool without ARCH" 1
|
print_test_result "Publish tool without ARCH" 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 12c: Install universal tool (arch fallback)
|
# Test 13c: Install universal tool (arch fallback)
|
||||||
echo -e "\nTest 12c: Install universal tool (arch fallback)"
|
echo -e "\nTest 13c: Install universal tool (arch fallback)"
|
||||||
rm -rf ~/.config/getpkg/"${TEST_TOOL_NOARCH}.json" ~/.local/bin/getpkg/"${TEST_TOOL_NOARCH}" 2>/dev/null || true
|
rm -rf ~/.config/getpkg/"${TEST_TOOL_NOARCH}.json" ~/.local/bin/getpkg/"${TEST_TOOL_NOARCH}" 2>/dev/null || true
|
||||||
FALLBACK_INSTALL_OUTPUT=$(timeout 3 "$GETPKG" install "${TEST_TOOL_NOARCH}" 2>&1) || FALLBACK_INSTALL_OUTPUT=""
|
FALLBACK_INSTALL_OUTPUT=$(timeout 3 "$GETPKG" install "${TEST_TOOL_NOARCH}" 2>&1) || FALLBACK_INSTALL_OUTPUT=""
|
||||||
if [[ "$FALLBACK_INSTALL_OUTPUT" =~ Arch-specific\ version\ not\ found,\ trying\ universal\ version ]] && [[ "$FALLBACK_INSTALL_OUTPUT" =~ Installed\ ${TEST_TOOL_NOARCH}\ successfully ]]; then
|
if [[ "$FALLBACK_INSTALL_OUTPUT" =~ Arch-specific\ version\ not\ found,\ trying\ universal\ version ]] && [[ "$FALLBACK_INSTALL_OUTPUT" =~ Installed\ ${TEST_TOOL_NOARCH}\ successfully ]]; then
|
||||||
@ -316,8 +403,8 @@ else
|
|||||||
echo "To run full tests, set SOS_WRITE_TOKEN environment variable"
|
echo "To run full tests, set SOS_WRITE_TOKEN environment variable"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 13: Invalid tool name validation
|
# Test 14: Invalid tool name validation
|
||||||
echo -e "\nTest 13: Invalid tool name validation"
|
echo -e "\nTest 14: Invalid tool name validation"
|
||||||
INVALID_OUTPUT=$(timeout 3 "$GETPKG" install "../evil-tool" 2>&1)
|
INVALID_OUTPUT=$(timeout 3 "$GETPKG" install "../evil-tool" 2>&1)
|
||||||
INVALID_EXIT_CODE=$?
|
INVALID_EXIT_CODE=$?
|
||||||
echo "Invalid tool output: $INVALID_OUTPUT"
|
echo "Invalid tool output: $INVALID_OUTPUT"
|
||||||
@ -328,9 +415,9 @@ else
|
|||||||
print_test_result "Invalid tool name rejection" 1
|
print_test_result "Invalid tool name rejection" 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 14: Update command (if we have tools installed)
|
# Test 15: Update command (if we have tools installed)
|
||||||
if [ -d ~/.config/getpkg ] && [ "$(find ~/.config/getpkg -name "*.json" -type f 2>/dev/null | wc -l)" -gt 0 ]; then
|
if [ -d ~/.config/getpkg ] && [ "$(find ~/.config/getpkg -name "*.json" -type f 2>/dev/null | wc -l)" -gt 0 ]; then
|
||||||
echo -e "\nTest 14: Update command"
|
echo -e "\nTest 15: Update command"
|
||||||
UPDATE_ALL_OUTPUT=$(timeout 30 "$GETPKG" update 2>&1) || UPDATE_ALL_OUTPUT=""
|
UPDATE_ALL_OUTPUT=$(timeout 30 "$GETPKG" update 2>&1) || UPDATE_ALL_OUTPUT=""
|
||||||
if [[ "$UPDATE_ALL_OUTPUT" =~ Update\ complete ]]; then
|
if [[ "$UPDATE_ALL_OUTPUT" =~ Update\ complete ]]; then
|
||||||
print_test_result "Update all tools command" 0
|
print_test_result "Update all tools command" 0
|
||||||
|
11
sos/sos
11
sos/sos
@ -2,13 +2,13 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
|
||||||
# get dropshell
|
# get getpkg
|
||||||
TEMP_DIR=$(mktemp -d)
|
TEMP_DIR=$(mktemp -d)
|
||||||
ARCH=$(uname -m)
|
ARCH=$(uname -m)
|
||||||
curl -L -s -o "${TEMP_DIR}/dropshell" "https://getbin.xyz/dropshell.${ARCH}" || die "Failed to download dropshell"
|
curl -L -s -o "${TEMP_DIR}/getpkg" "https://getbin.xyz/getpkg:latest-${ARCH}" || die "Failed to download getpkg"
|
||||||
chmod +x "${TEMP_DIR}/dropshell"
|
chmod +x "${TEMP_DIR}/getpkg"
|
||||||
trap 'rm -rf "${TEMP_DIR}"' EXIT
|
trap 'rm -rf "${TEMP_DIR}"' EXIT
|
||||||
DROPSHELL="${TEMP_DIR}/dropshell"
|
GETPKG="${TEMP_DIR}/getpkg"
|
||||||
|
|
||||||
|
|
||||||
function show_help() {
|
function show_help() {
|
||||||
@ -42,7 +42,6 @@ function datetime() {
|
|||||||
date -u +"%Y.%m%d.%H%M"
|
date -u +"%Y.%m%d.%H%M"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function upload() {
|
function upload() {
|
||||||
if [ "$#" -lt 3 ]; then
|
if [ "$#" -lt 3 ]; then
|
||||||
echo "Usage: sos upload <server> <file> <label:tag> [label:tag ...]"
|
echo "Usage: sos upload <server> <file> <label:tag> [label:tag ...]"
|
||||||
@ -78,7 +77,7 @@ function upload() {
|
|||||||
LABELTAGS_JSON="[${LABELTAGS_JSON%,}]"
|
LABELTAGS_JSON="[${LABELTAGS_JSON%,}]"
|
||||||
|
|
||||||
# trip whitespace from the file path
|
# trip whitespace from the file path
|
||||||
LOCALHASH=$("${DROPSHELL}" hash "${file}" | tr -d '[:space:]')
|
LOCALHASH=$("${GETPKG}" hash "${file}" | tr -d '[:space:]')
|
||||||
echo "Local hash: $LOCALHASH"
|
echo "Local hash: $LOCALHASH"
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user