mirror of
https://github.com/spitkov/ynspkg.git
synced 2025-01-18 12:34:39 +01:00
initial
This commit is contained in:
commit
0add4e98be
7 changed files with 766 additions and 0 deletions
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# Build directories
|
||||||
|
build/
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# IDE specific files
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# Package manager specific
|
||||||
|
/var/cache/yns/
|
||||||
|
/var/lib/yns/
|
38
CMakeLists.txt
Normal file
38
CMakeLists.txt
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(yns VERSION 1.0)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# Find required packages
|
||||||
|
find_package(CURL REQUIRED)
|
||||||
|
find_package(nlohmann_json REQUIRED)
|
||||||
|
|
||||||
|
# Add executable
|
||||||
|
add_executable(yns
|
||||||
|
src/main.cpp
|
||||||
|
src/package_manager.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Include directories
|
||||||
|
target_include_directories(yns PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
|
${CURL_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Link libraries
|
||||||
|
target_link_libraries(yns PRIVATE
|
||||||
|
${CURL_LIBRARIES}
|
||||||
|
nlohmann_json::nlohmann_json
|
||||||
|
)
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
install(TARGETS yns DESTINATION bin)
|
||||||
|
|
||||||
|
# Create required directories during installation
|
||||||
|
install(CODE "
|
||||||
|
execute_process(
|
||||||
|
COMMAND mkdir -p /var/cache/yns
|
||||||
|
COMMAND mkdir -p /var/lib/yns
|
||||||
|
)
|
||||||
|
")
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 YNS Package Manager
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
150
README.md
Normal file
150
README.md
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
# YNS Package Manager
|
||||||
|
|
||||||
|
A lightweight package manager for Linux that uses a single repository source.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- CMake (>= 3.10)
|
||||||
|
- C++17 compiler
|
||||||
|
- libcurl
|
||||||
|
- nlohmann-json
|
||||||
|
|
||||||
|
On Ubuntu/Debian, you can install the dependencies with:
|
||||||
|
```bash
|
||||||
|
sudo apt-get install build-essential cmake libcurl4-openssl-dev nlohmann-json3-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
After building, you can install the package manager with:
|
||||||
|
```bash
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
- Install the `yns` binary to `/usr/local/bin`
|
||||||
|
- Create necessary directories:
|
||||||
|
- `/var/cache/yns/` - For package cache
|
||||||
|
- `/var/lib/yns/` - For installed package database
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Update package cache
|
||||||
|
```bash
|
||||||
|
yns update
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install a package
|
||||||
|
```bash
|
||||||
|
yns install <package_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Remove a package
|
||||||
|
```bash
|
||||||
|
yns remove <package_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Upgrade a package
|
||||||
|
```bash
|
||||||
|
yns upgrade <package_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
### List all packages
|
||||||
|
```bash
|
||||||
|
yns list
|
||||||
|
```
|
||||||
|
|
||||||
|
### Interactive Mode
|
||||||
|
```bash
|
||||||
|
yns interactive
|
||||||
|
```
|
||||||
|
|
||||||
|
Interactive mode provides a shell-like interface where you can execute multiple commands without the `yns` prefix. Available commands in interactive mode:
|
||||||
|
|
||||||
|
- `help` - Show available commands
|
||||||
|
- `update` - Update package cache
|
||||||
|
- `install <package>` - Install a package
|
||||||
|
- `remove <package>` - Remove a package
|
||||||
|
- `upgrade <package>` - Upgrade a package
|
||||||
|
- `list` - List all packages
|
||||||
|
- `clear` - Clear the screen
|
||||||
|
- `exit` - Exit interactive mode
|
||||||
|
|
||||||
|
Example interactive session:
|
||||||
|
```
|
||||||
|
$ yns interactive
|
||||||
|
YNS Package Manager Interactive Mode
|
||||||
|
Type 'help' for available commands or 'exit' to quit
|
||||||
|
yns> update
|
||||||
|
[100%] Package cache updated successfully
|
||||||
|
yns> list
|
||||||
|
Available packages:
|
||||||
|
==================
|
||||||
|
calc [installed 1.1, update available 2.0]
|
||||||
|
notepad [available 2.0]
|
||||||
|
yns> install notepad
|
||||||
|
[50%] Installing notepad@2.0
|
||||||
|
notepad@2.0 installed successfully
|
||||||
|
yns> exit
|
||||||
|
Goodbye!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Color Coding
|
||||||
|
|
||||||
|
The package manager uses color-coded output for better visibility:
|
||||||
|
|
||||||
|
🔵 Blue:
|
||||||
|
- Progress indicators (e.g., "[50%] Installing package...")
|
||||||
|
- Available but not installed packages in `yns list`
|
||||||
|
- Interactive mode prompt
|
||||||
|
|
||||||
|
🟢 Green:
|
||||||
|
- Success messages
|
||||||
|
- Installed packages that are up to date in `yns list`
|
||||||
|
- Interactive mode welcome message
|
||||||
|
|
||||||
|
🟡 Yellow:
|
||||||
|
- Installed packages with updates available in `yns list`
|
||||||
|
|
||||||
|
🔴 Red:
|
||||||
|
- Error messages
|
||||||
|
|
||||||
|
Example `yns list` output:
|
||||||
|
```
|
||||||
|
Available packages:
|
||||||
|
==================
|
||||||
|
calc [installed 1.1, update available 2.0] # Yellow
|
||||||
|
notepad [available 2.0] # Blue
|
||||||
|
vim [installed 3.0] # Green
|
||||||
|
```
|
||||||
|
|
||||||
|
## Repository Structure
|
||||||
|
|
||||||
|
The package manager uses a single repository source at:
|
||||||
|
`https://raw.githubusercontent.com/spitkov/ynsrepo/refs/heads/main/repo.json`
|
||||||
|
|
||||||
|
The repository contains package information in JSON format, including:
|
||||||
|
- Package name
|
||||||
|
- Version
|
||||||
|
- Installation script URL
|
||||||
|
- Update script URL
|
||||||
|
- Remove script URL
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Colored terminal output
|
||||||
|
- Progress indicators
|
||||||
|
- Automatic package cache management
|
||||||
|
- Version tracking for installed packages
|
||||||
|
- Secure script execution
|
||||||
|
- Error handling and recovery
|
||||||
|
- Interactive shell mode
|
39
include/package_manager.hpp
Normal file
39
include/package_manager.hpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
class PackageManager {
|
||||||
|
public:
|
||||||
|
PackageManager();
|
||||||
|
|
||||||
|
bool update();
|
||||||
|
bool install(const std::string& package_name);
|
||||||
|
bool remove(const std::string& package_name);
|
||||||
|
bool upgrade(const std::string& package_name);
|
||||||
|
bool list();
|
||||||
|
bool interactive_mode();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr const char* REPO_URL = "https://raw.githubusercontent.com/spitkov/ynsrepo/refs/heads/main/repo.json";
|
||||||
|
static constexpr const char* CACHE_DIR = "/var/cache/yns/";
|
||||||
|
static constexpr const char* CACHE_FILE = "/var/cache/yns/repo.json";
|
||||||
|
static constexpr const char* INSTALLED_DB = "/var/lib/yns/installed.json";
|
||||||
|
|
||||||
|
bool download_file(const std::string& url, const std::string& output_path);
|
||||||
|
bool execute_script(const std::string& script_path);
|
||||||
|
bool cache_repo();
|
||||||
|
json read_cache();
|
||||||
|
json read_installed_db();
|
||||||
|
void save_installed_db(const json& db);
|
||||||
|
void print_progress(const std::string& message, int percentage);
|
||||||
|
void print_error(const std::string& message);
|
||||||
|
void print_success(const std::string& message);
|
||||||
|
void print_interactive_help();
|
||||||
|
|
||||||
|
json repo_cache;
|
||||||
|
json installed_packages;
|
||||||
|
};
|
96
src/main.cpp
Normal file
96
src/main.cpp
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
#include "package_manager.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
void print_usage() {
|
||||||
|
std::cout << "Usage: yns <command> [package_name]\n\n"
|
||||||
|
<< "Commands:\n"
|
||||||
|
<< " update Update package cache\n"
|
||||||
|
<< " install <package> Install a package\n"
|
||||||
|
<< " remove <package> Remove a package\n"
|
||||||
|
<< " upgrade <package> Upgrade a package\n"
|
||||||
|
<< " list List all packages\n"
|
||||||
|
<< " interactive Start interactive mode\n\n"
|
||||||
|
<< "Interactive Mode:\n"
|
||||||
|
<< " Run 'yns interactive' to enter interactive mode where you can\n"
|
||||||
|
<< " execute multiple commands without prefix. Type 'help' in\n"
|
||||||
|
<< " interactive mode for available commands.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool needs_sudo(const std::string& command) {
|
||||||
|
return command == "update" || command == "install" ||
|
||||||
|
command == "remove" || command == "upgrade" ||
|
||||||
|
command == "interactive" || command == "list";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_self_path() {
|
||||||
|
char path[1024];
|
||||||
|
ssize_t count = readlink("/proc/self/exe", path, sizeof(path));
|
||||||
|
return std::string(path, (count > 0) ? count : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc < 2) {
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string command = argv[1];
|
||||||
|
|
||||||
|
if (needs_sudo(command) && geteuid() != 0) {
|
||||||
|
std::string self = get_self_path();
|
||||||
|
std::string sudo_cmd = "sudo " + self;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
sudo_cmd += " ";
|
||||||
|
sudo_cmd += argv[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "This operation requires root privileges.\n";
|
||||||
|
return system(sudo_cmd.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageManager pm;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (command == "update") {
|
||||||
|
return pm.update() ? 0 : 1;
|
||||||
|
}
|
||||||
|
else if (command == "list") {
|
||||||
|
return pm.list() ? 0 : 1;
|
||||||
|
}
|
||||||
|
else if (command == "interactive") {
|
||||||
|
return pm.interactive_mode() ? 0 : 1;
|
||||||
|
}
|
||||||
|
else if (command == "install" || command == "remove" || command == "upgrade") {
|
||||||
|
if (argc < 3) {
|
||||||
|
std::cerr << "Error: Package name required for " << command << " command\n";
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string package_name = argv[2];
|
||||||
|
|
||||||
|
if (command == "install") {
|
||||||
|
return pm.install(package_name) ? 0 : 1;
|
||||||
|
}
|
||||||
|
else if (command == "remove") {
|
||||||
|
return pm.remove(package_name) ? 0 : 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return pm.upgrade(package_name) ? 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "Error: Unknown command '" << command << "'\n";
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
std::cerr << "\033[31mError: " << e.what() << "\033[0m\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
380
src/package_manager.cpp
Normal file
380
src/package_manager.cpp
Normal file
|
@ -0,0 +1,380 @@
|
||||||
|
#include "package_manager.hpp"
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <sstream>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
const std::string RED = "\033[31m";
|
||||||
|
const std::string GREEN = "\033[32m";
|
||||||
|
const std::string BLUE = "\033[34m";
|
||||||
|
const std::string YELLOW = "\033[33m";
|
||||||
|
const std::string RESET = "\033[0m";
|
||||||
|
|
||||||
|
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
|
||||||
|
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageManager::PackageManager() {
|
||||||
|
fs::create_directories(CACHE_DIR);
|
||||||
|
fs::create_directories("/var/lib/yns");
|
||||||
|
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||||
|
installed_packages = read_installed_db();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackageManager::download_file(const std::string& url, const std::string& output_path) {
|
||||||
|
CURL* curl = curl_easy_init();
|
||||||
|
if (!curl) return false;
|
||||||
|
|
||||||
|
std::string response_data;
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_data);
|
||||||
|
|
||||||
|
CURLcode res = curl_easy_perform(curl);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
|
||||||
|
if (res != CURLE_OK) return false;
|
||||||
|
|
||||||
|
std::ofstream output_file(output_path);
|
||||||
|
if (!output_file) return false;
|
||||||
|
|
||||||
|
output_file << response_data;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackageManager::execute_script(const std::string& script_path) {
|
||||||
|
std::string cmd = "chmod +x " + script_path + " && " + script_path;
|
||||||
|
int exit_code = system(cmd.c_str());
|
||||||
|
if (WIFEXITED(exit_code)) {
|
||||||
|
int status = WEXITSTATUS(exit_code);
|
||||||
|
if (status != 0) {
|
||||||
|
print_error("Script failed with exit code: " + std::to_string(status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
print_error("Script terminated abnormally");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackageManager::cache_repo() {
|
||||||
|
print_progress("Updating package cache", 0);
|
||||||
|
if (!download_file(REPO_URL, CACHE_FILE)) {
|
||||||
|
print_error("Failed to download repository data");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
print_progress("Updating package cache", 100);
|
||||||
|
print_success("Package cache updated successfully");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
json PackageManager::read_cache() {
|
||||||
|
try {
|
||||||
|
std::ifstream cache_file(CACHE_FILE);
|
||||||
|
if (!cache_file) return json::object();
|
||||||
|
return json::parse(cache_file);
|
||||||
|
} catch (...) {
|
||||||
|
return json::object();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json PackageManager::read_installed_db() {
|
||||||
|
try {
|
||||||
|
std::ifstream db_file(INSTALLED_DB);
|
||||||
|
if (!db_file) return json::object();
|
||||||
|
return json::parse(db_file);
|
||||||
|
} catch (...) {
|
||||||
|
return json::object();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackageManager::save_installed_db(const json& db) {
|
||||||
|
std::ofstream db_file(INSTALLED_DB);
|
||||||
|
db_file << db.dump(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackageManager::print_progress(const std::string& message, int percentage) {
|
||||||
|
std::cout << BLUE << "[" << percentage << "%] " << message << RESET << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackageManager::print_error(const std::string& message) {
|
||||||
|
std::cerr << RED << "Error: " << message << RESET << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackageManager::print_success(const std::string& message) {
|
||||||
|
std::cout << GREEN << message << RESET << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackageManager::update() {
|
||||||
|
return cache_repo();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackageManager::install(const std::string& package_name) {
|
||||||
|
if (!update()) return false;
|
||||||
|
|
||||||
|
repo_cache = read_cache();
|
||||||
|
|
||||||
|
if (!repo_cache["packages"].contains(package_name)) {
|
||||||
|
print_error("Package '" + package_name + "' not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto package = repo_cache["packages"][package_name];
|
||||||
|
std::string repo_version = package["version"];
|
||||||
|
|
||||||
|
if (installed_packages.contains(package_name)) {
|
||||||
|
std::string installed_version = installed_packages[package_name]["version"];
|
||||||
|
if (installed_version == repo_version) {
|
||||||
|
print_success(package_name + " is already installed (version " + installed_version + ")");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
print_progress("New version available: " + repo_version + " (currently installed: " + installed_version + ")", 0);
|
||||||
|
std::cout << "Would you like to upgrade? [y/N] ";
|
||||||
|
std::string response;
|
||||||
|
std::getline(std::cin, response);
|
||||||
|
if (response == "y" || response == "Y") {
|
||||||
|
return upgrade(package_name);
|
||||||
|
} else {
|
||||||
|
print_success("Keeping current version " + installed_version);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string install_script = package["install"];
|
||||||
|
std::string temp_script = "/tmp/yns_install_" + package_name + ".sh";
|
||||||
|
print_progress("Downloading installation script", 0);
|
||||||
|
|
||||||
|
if (!download_file(install_script, temp_script)) {
|
||||||
|
print_error("Failed to download installation script");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_progress("Installing " + package_name + "@" + repo_version, 50);
|
||||||
|
|
||||||
|
std::string cmd = "chmod +x " + temp_script + " && " + temp_script;
|
||||||
|
int exit_code = system(cmd.c_str());
|
||||||
|
fs::remove(temp_script);
|
||||||
|
|
||||||
|
if (!WIFEXITED(exit_code)) {
|
||||||
|
print_error("Installation script terminated abnormally");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = WEXITSTATUS(exit_code);
|
||||||
|
if (status != 0) {
|
||||||
|
print_error("Installation failed with exit code: " + std::to_string(status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
installed_packages[package_name] = {
|
||||||
|
{"version", repo_version}
|
||||||
|
};
|
||||||
|
save_installed_db(installed_packages);
|
||||||
|
|
||||||
|
print_success(package_name + "@" + repo_version + " installed successfully");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackageManager::remove(const std::string& package_name) {
|
||||||
|
if (!installed_packages.contains(package_name)) {
|
||||||
|
print_error("Package '" + package_name + "' is not installed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
repo_cache = read_cache();
|
||||||
|
if (!repo_cache["packages"].contains(package_name)) {
|
||||||
|
print_error("Package information not found in repository");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string remove_script = repo_cache["packages"][package_name]["remove"];
|
||||||
|
std::string temp_script = "/tmp/yns_remove_" + package_name + ".sh";
|
||||||
|
|
||||||
|
print_progress("Downloading removal script", 0);
|
||||||
|
if (!download_file(remove_script, temp_script)) {
|
||||||
|
print_error("Failed to download removal script");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_progress("Removing " + package_name, 50);
|
||||||
|
|
||||||
|
std::string cmd = "chmod +x " + temp_script + " && " + temp_script;
|
||||||
|
int exit_code = system(cmd.c_str());
|
||||||
|
fs::remove(temp_script);
|
||||||
|
|
||||||
|
if (!WIFEXITED(exit_code)) {
|
||||||
|
print_error("Removal script terminated abnormally");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = WEXITSTATUS(exit_code);
|
||||||
|
if (status != 0) {
|
||||||
|
print_error("Removal failed with exit code: " + std::to_string(status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
installed_packages.erase(package_name);
|
||||||
|
save_installed_db(installed_packages);
|
||||||
|
|
||||||
|
print_success(package_name + " removed successfully");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackageManager::upgrade(const std::string& package_name) {
|
||||||
|
if (!installed_packages.contains(package_name)) {
|
||||||
|
print_error("Package '" + package_name + "' is not installed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!update()) return false;
|
||||||
|
|
||||||
|
repo_cache = read_cache();
|
||||||
|
if (!repo_cache["packages"].contains(package_name)) {
|
||||||
|
print_error("Package information not found in repository");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string installed_version = installed_packages[package_name]["version"];
|
||||||
|
std::string repo_version = repo_cache["packages"][package_name]["version"];
|
||||||
|
|
||||||
|
if (installed_version == repo_version) {
|
||||||
|
print_success(package_name + " is already up to date (" + installed_version + ")");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string update_script = repo_cache["packages"][package_name]["update"];
|
||||||
|
std::string temp_script = "/tmp/yns_update_" + package_name + ".sh";
|
||||||
|
|
||||||
|
print_progress("Downloading update script", 0);
|
||||||
|
if (!download_file(update_script, temp_script)) {
|
||||||
|
print_error("Failed to download update script");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_progress("Updating " + package_name + " from " + installed_version + " to " + repo_version, 50);
|
||||||
|
|
||||||
|
std::string cmd = "chmod +x " + temp_script + " && " + temp_script;
|
||||||
|
int exit_code = system(cmd.c_str());
|
||||||
|
fs::remove(temp_script);
|
||||||
|
|
||||||
|
if (!WIFEXITED(exit_code)) {
|
||||||
|
print_error("Update script terminated abnormally");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = WEXITSTATUS(exit_code);
|
||||||
|
if (status != 0) {
|
||||||
|
print_error("Update failed with exit code: " + std::to_string(status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
installed_packages[package_name]["version"] = repo_version;
|
||||||
|
save_installed_db(installed_packages);
|
||||||
|
|
||||||
|
print_success(package_name + " updated to version " + repo_version);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackageManager::list() {
|
||||||
|
if (!update()) return false;
|
||||||
|
|
||||||
|
repo_cache = read_cache();
|
||||||
|
|
||||||
|
std::cout << "\nAvailable packages:\n";
|
||||||
|
std::cout << "==================\n";
|
||||||
|
|
||||||
|
for (const auto& [name, package] : repo_cache["packages"].items()) {
|
||||||
|
std::string repo_version = package["version"];
|
||||||
|
std::string status;
|
||||||
|
|
||||||
|
if (installed_packages.contains(name)) {
|
||||||
|
std::string installed_version = installed_packages[name]["version"];
|
||||||
|
if (installed_version == repo_version) {
|
||||||
|
status = GREEN + "[installed " + installed_version + "]" + RESET;
|
||||||
|
} else {
|
||||||
|
status = YELLOW + "[installed " + installed_version + ", update available " + repo_version + "]" + RESET;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status = BLUE + "[available " + repo_version + "]" + RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << name << " " << status << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PackageManager::print_interactive_help() {
|
||||||
|
std::cout << "\nAvailable commands:\n"
|
||||||
|
<< " help Show this help message\n"
|
||||||
|
<< " update Update package cache\n"
|
||||||
|
<< " install <package> Install a package\n"
|
||||||
|
<< " remove <package> Remove a package\n"
|
||||||
|
<< " upgrade <package> Upgrade a package\n"
|
||||||
|
<< " list List all packages\n"
|
||||||
|
<< " clear Clear the screen\n"
|
||||||
|
<< " exit Exit interactive mode\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackageManager::interactive_mode() {
|
||||||
|
std::cout << GREEN << "YNS Package Manager Interactive Mode\n" << RESET;
|
||||||
|
std::cout << "Type 'help' for available commands or 'exit' to quit\n";
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (true) {
|
||||||
|
std::cout << BLUE << "yns> " << RESET;
|
||||||
|
if (!std::getline(std::cin, line) || line == "exit") {
|
||||||
|
std::cout << "Goodbye!\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istringstream iss(line);
|
||||||
|
std::string command;
|
||||||
|
iss >> command;
|
||||||
|
|
||||||
|
if (command == "help") {
|
||||||
|
print_interactive_help();
|
||||||
|
}
|
||||||
|
else if (command == "update") {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
else if (command == "list") {
|
||||||
|
list();
|
||||||
|
}
|
||||||
|
else if (command == "clear") {
|
||||||
|
system("clear");
|
||||||
|
}
|
||||||
|
else if (command == "install" || command == "remove" || command == "upgrade") {
|
||||||
|
std::string package_name;
|
||||||
|
if (!(iss >> package_name)) {
|
||||||
|
print_error("Package name required for " + command + " command");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command == "install") {
|
||||||
|
install(package_name);
|
||||||
|
}
|
||||||
|
else if (command == "remove") {
|
||||||
|
remove(package_name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
upgrade(package_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print_error("Unknown command '" + command + "'. Type 'help' for available commands.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
Loading…
Reference in a new issue