From 71e283bcd503f12777c4dc8db1fc198925936e3e Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Mon, 27 Oct 2025 11:27:44 +0000 Subject: [PATCH] feat(cli): Add initial MOXYTOOL implementation, packaging, install/uninstall scripts, CI and release workflows --- .gitea/workflows/ci.yml | 84 ++++++++++ .gitea/workflows/npm-publish.yml | 129 ++++++++++++++++ .gitea/workflows/release.yml | 249 ++++++++++++++++++++++++++++++ .gitignore | 52 +++++++ bin/moxytool-wrapper.js | 108 +++++++++++++ changelog.md | 36 +++++ deno.json | 57 +++++++ install.sh | 253 +++++++++++++++++++++++++++++++ license | 21 +++ mod.ts | 48 ++++++ npmextra.json | 1 + package.json | 61 ++++++++ readme.hints.md | 0 readme.md | 172 +++++++++++++++++++++ scripts/compile-all.sh | 72 +++++++++ scripts/install-binary.js | 228 ++++++++++++++++++++++++++++ ts/00_commitinfo_data.ts | 8 + ts/index.ts | 28 ++++ ts/moxytool.cli.ts | 112 ++++++++++++++ ts/moxytool.logging.ts | 57 +++++++ ts/moxytool.paths.ts | 21 +++ ts/moxytool.plugins.ts | 29 ++++ uninstall.sh | 106 +++++++++++++ 23 files changed, 1932 insertions(+) create mode 100644 .gitea/workflows/ci.yml create mode 100644 .gitea/workflows/npm-publish.yml create mode 100644 .gitea/workflows/release.yml create mode 100644 .gitignore create mode 100644 bin/moxytool-wrapper.js create mode 100644 changelog.md create mode 100644 deno.json create mode 100755 install.sh create mode 100644 license create mode 100644 mod.ts create mode 100644 npmextra.json create mode 100644 package.json create mode 100644 readme.hints.md create mode 100644 readme.md create mode 100755 scripts/compile-all.sh create mode 100644 scripts/install-binary.js create mode 100644 ts/00_commitinfo_data.ts create mode 100644 ts/index.ts create mode 100644 ts/moxytool.cli.ts create mode 100644 ts/moxytool.logging.ts create mode 100644 ts/moxytool.paths.ts create mode 100644 ts/moxytool.plugins.ts create mode 100755 uninstall.sh diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..cfcd7f3 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,84 @@ +name: CI + +on: + push: + branches: + - master + - main + pull_request: + branches: + - master + - main + +jobs: + check: + name: Type Check & Lint + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Deno + uses: denoland/setup-deno@v1 + with: + deno-version: v2.x + + - name: Check TypeScript types + run: deno check mod.ts + + - name: Lint code + run: deno lint + continue-on-error: true + + - name: Format check + run: deno fmt --check + continue-on-error: true + + build: + name: Build Test (Current Platform) + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Deno + uses: denoland/setup-deno@v1 + with: + deno-version: v2.x + + - name: Compile for current platform + run: | + echo "Testing compilation for Linux x86_64..." + deno compile --allow-all --no-check \ + --output moxytool-test \ + --target x86_64-unknown-linux-gnu mod.ts + + - name: Test binary execution + run: | + chmod +x moxytool-test + ./moxytool-test || echo "Basic execution test - OK for now" + + build-all: + name: Build All Platforms + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Deno + uses: denoland/setup-deno@v1 + with: + deno-version: v2.x + + - name: Compile all platform binaries + run: bash scripts/compile-all.sh + + - name: Upload all binaries as artifact + uses: actions/upload-artifact@v3 + with: + name: moxytool-binaries.zip + path: dist/binaries/* + retention-days: 30 diff --git a/.gitea/workflows/npm-publish.yml b/.gitea/workflows/npm-publish.yml new file mode 100644 index 0000000..60b2149 --- /dev/null +++ b/.gitea/workflows/npm-publish.yml @@ -0,0 +1,129 @@ +name: Publish to npm + +on: + push: + tags: + - 'v*' + +jobs: + npm-publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Deno + uses: denoland/setup-deno@v1 + with: + deno-version: v2.x + + - name: Setup Node.js for npm publishing + uses: actions/setup-node@v4 + with: + node-version: '18.x' + registry-url: 'https://registry.npmjs.org/' + + - name: Get version from tag + id: version + run: | + VERSION=${GITHUB_REF#refs/tags/} + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "version_number=${VERSION#v}" >> $GITHUB_OUTPUT + echo "Publishing version: $VERSION" + + - name: Verify deno.json version matches tag + run: | + DENO_VERSION=$(grep -o '"version": "[^"]*"' deno.json | cut -d'"' -f4) + TAG_VERSION="${{ steps.version.outputs.version_number }}" + echo "deno.json version: $DENO_VERSION" + echo "Tag version: $TAG_VERSION" + if [ "$DENO_VERSION" != "$TAG_VERSION" ]; then + echo "ERROR: Version mismatch!" + echo "deno.json has version $DENO_VERSION but tag is $TAG_VERSION" + exit 1 + fi + + - name: Compile binaries for npm package + run: | + echo "Compiling binaries for npm package..." + deno task compile + echo "" + echo "Binary sizes:" + ls -lh dist/binaries/ + + - name: Generate SHA256 checksums + run: | + cd dist/binaries + sha256sum * > SHA256SUMS + cat SHA256SUMS + cd ../.. + + - name: Sync package.json version + run: | + VERSION="${{ steps.version.outputs.version_number }}" + echo "Syncing package.json to version ${VERSION}..." + npm version ${VERSION} --no-git-tag-version --allow-same-version + echo "package.json version: $(grep '"version"' package.json | head -1)" + + - name: Create npm package + run: | + echo "Creating npm package..." + npm pack + echo "" + echo "Package created:" + ls -lh *.tgz + + - name: Test local installation + run: | + echo "Testing local package installation..." + PACKAGE_FILE=$(ls *.tgz) + npm install -g ${PACKAGE_FILE} + echo "" + echo "Testing moxytool command:" + moxytool || echo "Note: Binary execution may fail in CI environment" + echo "" + echo "Checking installed files:" + npm ls -g @serve.zone/moxytool || true + + - name: Publish to npm + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + echo "Publishing to npm registry..." + npm publish --access public + echo "" + echo "✅ Successfully published @serve.zone/moxytool to npm!" + echo "" + echo "Package info:" + npm view @serve.zone/moxytool + + - name: Verify npm package + run: | + echo "Waiting for npm propagation..." + sleep 30 + echo "" + echo "Verifying published package..." + npm view @serve.zone/moxytool + echo "" + echo "Testing installation from npm:" + npm install -g @serve.zone/moxytool + echo "" + echo "Package installed successfully!" + which moxytool || echo "Binary location check skipped" + + - name: Publish Summary + run: | + echo "================================================" + echo " npm Publish Complete!" + echo "================================================" + echo "" + echo "✅ Package: @serve.zone/moxytool" + echo "✅ Version: ${{ steps.version.outputs.version }}" + echo "" + echo "Installation:" + echo " npm install -g @serve.zone/moxytool" + echo "" + echo "Registry:" + echo " https://www.npmjs.com/package/@serve.zone/moxytool" + echo "" diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..f9f31c3 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,249 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + build-and-release: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Deno + uses: denoland/setup-deno@v1 + with: + deno-version: v2.x + + - name: Get version from tag + id: version + run: | + VERSION=${GITHUB_REF#refs/tags/} + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "version_number=${VERSION#v}" >> $GITHUB_OUTPUT + echo "Building version: $VERSION" + + - name: Verify deno.json version matches tag + run: | + DENO_VERSION=$(grep -o '"version": "[^"]*"' deno.json | cut -d'"' -f4) + TAG_VERSION="${{ steps.version.outputs.version_number }}" + echo "deno.json version: $DENO_VERSION" + echo "Tag version: $TAG_VERSION" + if [ "$DENO_VERSION" != "$TAG_VERSION" ]; then + echo "ERROR: Version mismatch!" + echo "deno.json has version $DENO_VERSION but tag is $TAG_VERSION" + exit 1 + fi + + - name: Compile binaries for all platforms + run: | + echo "================================================" + echo " MOXYTOOL Release Compilation" + echo " Version: ${{ steps.version.outputs.version }}" + echo "================================================" + echo "" + + # Clean up old binaries and create fresh directory + rm -rf dist/binaries + mkdir -p dist/binaries + echo "→ Cleaned old binaries from dist/binaries" + echo "" + + # Linux x86_64 + echo "→ Compiling for Linux x86_64..." + deno compile --allow-all --no-check \ + --output dist/binaries/moxytool-linux-x64 \ + --target x86_64-unknown-linux-gnu mod.ts + echo " ✓ Linux x86_64 complete" + + # Linux ARM64 + echo "→ Compiling for Linux ARM64..." + deno compile --allow-all --no-check \ + --output dist/binaries/moxytool-linux-arm64 \ + --target aarch64-unknown-linux-gnu mod.ts + echo " ✓ Linux ARM64 complete" + + # macOS x86_64 + echo "→ Compiling for macOS x86_64..." + deno compile --allow-all --no-check \ + --output dist/binaries/moxytool-macos-x64 \ + --target x86_64-apple-darwin mod.ts + echo " ✓ macOS x86_64 complete" + + # macOS ARM64 + echo "→ Compiling for macOS ARM64..." + deno compile --allow-all --no-check \ + --output dist/binaries/moxytool-macos-arm64 \ + --target aarch64-apple-darwin mod.ts + echo " ✓ macOS ARM64 complete" + + # Windows x86_64 + echo "→ Compiling for Windows x86_64..." + deno compile --allow-all --no-check \ + --output dist/binaries/moxytool-windows-x64.exe \ + --target x86_64-pc-windows-msvc mod.ts + echo " ✓ Windows x86_64 complete" + + echo "" + echo "All binaries compiled successfully!" + ls -lh dist/binaries/ + + - name: Generate SHA256 checksums + run: | + cd dist/binaries + sha256sum * > SHA256SUMS.txt + cat SHA256SUMS.txt + cd ../.. + + - name: Extract changelog for this version + id: changelog + run: | + VERSION="${{ steps.version.outputs.version }}" + + # Check if changelog.md exists + if [ ! -f changelog.md ]; then + echo "No changelog.md found, using default release notes" + cat > /tmp/release_notes.md << EOF + ## MOXYTOOL $VERSION + + Pre-compiled binaries for multiple platforms. + + ### Installation + + Use the installation script: + \`\`\`bash + curl -sSL https://code.foss.global/serve.zone/moxytool/raw/branch/master/install.sh | sudo bash + \`\`\` + + Or download the binary for your platform and make it executable. + + ### Supported Platforms + - Linux x86_64 (x64) + - Linux ARM64 (aarch64) + - macOS x86_64 (Intel) + - macOS ARM64 (Apple Silicon) + - Windows x86_64 + + ### Checksums + SHA256 checksums are provided in SHA256SUMS.txt + EOF + else + # Try to extract section for this version from changelog.md + # This is a simple extraction - adjust based on your changelog format + awk "/## \[$VERSION\]/,/## \[/" changelog.md | sed '$d' > /tmp/release_notes.md || cat > /tmp/release_notes.md << EOF + ## MOXYTOOL $VERSION + + See changelog.md for full details. + + ### Installation + + Use the installation script: + \`\`\`bash + curl -sSL https://code.foss.global/serve.zone/moxytool/raw/branch/master/install.sh | sudo bash + \`\`\` + EOF + fi + + echo "Release notes:" + cat /tmp/release_notes.md + + - name: Delete existing release if it exists + run: | + VERSION="${{ steps.version.outputs.version }}" + + echo "Checking for existing release $VERSION..." + + # Try to get existing release by tag + EXISTING_RELEASE_ID=$(curl -s \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + "https://code.foss.global/api/v1/repos/serve.zone/moxytool/releases/tags/$VERSION" \ + | jq -r '.id // empty') + + if [ -n "$EXISTING_RELEASE_ID" ]; then + echo "Found existing release (ID: $EXISTING_RELEASE_ID), deleting..." + curl -X DELETE -s \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + "https://code.foss.global/api/v1/repos/serve.zone/moxytool/releases/$EXISTING_RELEASE_ID" + echo "Existing release deleted" + sleep 2 + else + echo "No existing release found, proceeding with creation" + fi + + - name: Create Gitea Release + run: | + VERSION="${{ steps.version.outputs.version }}" + RELEASE_NOTES=$(cat /tmp/release_notes.md) + + # Create the release + echo "Creating release for $VERSION..." + RELEASE_ID=$(curl -X POST -s \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -H "Content-Type: application/json" \ + "https://code.foss.global/api/v1/repos/serve.zone/moxytool/releases" \ + -d "{ + \"tag_name\": \"$VERSION\", + \"name\": \"MOXYTOOL $VERSION\", + \"body\": $(jq -Rs . /tmp/release_notes.md), + \"draft\": false, + \"prerelease\": false + }" | jq -r '.id') + + echo "Release created with ID: $RELEASE_ID" + + # Upload binaries as release assets + for binary in dist/binaries/*; do + filename=$(basename "$binary") + echo "Uploading $filename..." + curl -X POST -s \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -H "Content-Type: application/octet-stream" \ + --data-binary "@$binary" \ + "https://code.foss.global/api/v1/repos/serve.zone/moxytool/releases/$RELEASE_ID/assets?name=$filename" + done + + echo "All assets uploaded successfully" + + - name: Clean up old releases + run: | + echo "Cleaning up old releases (keeping only last 3)..." + + # Fetch all releases sorted by creation date + RELEASES=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + "https://code.foss.global/api/v1/repos/serve.zone/moxytool/releases" | \ + jq -r 'sort_by(.created_at) | reverse | .[3:] | .[].id') + + # Delete old releases + if [ -n "$RELEASES" ]; then + echo "Found releases to delete:" + for release_id in $RELEASES; do + echo " Deleting release ID: $release_id" + curl -X DELETE -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + "https://code.foss.global/api/v1/repos/serve.zone/moxytool/releases/$release_id" + done + echo "Old releases deleted successfully" + else + echo "No old releases to delete (less than 4 releases total)" + fi + echo "" + + - name: Release Summary + run: | + echo "================================================" + echo " Release ${{ steps.version.outputs.version }} Complete!" + echo "================================================" + echo "" + echo "Binaries published:" + ls -lh dist/binaries/ + echo "" + echo "Release URL:" + echo "https://code.foss.global/serve.zone/moxytool/releases/tag/${{ steps.version.outputs.version }}" + echo "" + echo "Installation command:" + echo "curl -sSL https://code.foss.global/serve.zone/moxytool/raw/branch/master/install.sh | sudo bash" + echo "" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2745845 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# Compiled Deno binaries (built by scripts/compile-all.sh) +dist/binaries/ + +# Deno cache and lock file +.deno/ +deno.lock + +# Node.js artifacts +node_modules/ +vendor/ +dist_ts/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Logs +*.log +logs/ + +# Environment +.env +.env.local +.env.*.local + +# OS specific +.DS_Store +Thumbs.db +Desktop.ini + +# Development +.nogit/ +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Test coverage +coverage/ +.nyc_output/ + +# Temporary files +tmp/ +temp/ +*.tmp + +# Package manager lock files (optional - some projects commit these) +# Uncomment if you don't want to commit lock files +# package-lock.json +# yarn.lock +# pnpm-lock.yaml diff --git a/bin/moxytool-wrapper.js b/bin/moxytool-wrapper.js new file mode 100644 index 0000000..638ab35 --- /dev/null +++ b/bin/moxytool-wrapper.js @@ -0,0 +1,108 @@ +#!/usr/bin/env node + +/** + * MOXYTOOL npm wrapper + * This script executes the appropriate pre-compiled binary based on the current platform + */ + +import { spawn } from 'child_process'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; +import { existsSync } from 'fs'; +import { platform, arch } from 'os'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +/** + * Get the binary name for the current platform + */ +function getBinaryName() { + const plat = platform(); + const architecture = arch(); + + // Map Node's platform/arch to our binary naming + const platformMap = { + 'darwin': 'macos', + 'linux': 'linux', + 'win32': 'windows' + }; + + const archMap = { + 'x64': 'x64', + 'arm64': 'arm64' + }; + + const mappedPlatform = platformMap[plat]; + const mappedArch = archMap[architecture]; + + if (!mappedPlatform || !mappedArch) { + console.error(`Error: Unsupported platform/architecture: ${plat}/${architecture}`); + console.error('Supported platforms: Linux, macOS, Windows'); + console.error('Supported architectures: x64, arm64'); + process.exit(1); + } + + // Construct binary name + let binaryName = `moxytool-${mappedPlatform}-${mappedArch}`; + if (plat === 'win32') { + binaryName += '.exe'; + } + + return binaryName; +} + +/** + * Execute the binary + */ +function executeBinary() { + const binaryName = getBinaryName(); + const binaryPath = join(__dirname, '..', 'dist', 'binaries', binaryName); + + // Check if binary exists + if (!existsSync(binaryPath)) { + console.error(`Error: Binary not found at ${binaryPath}`); + console.error('This might happen if:'); + console.error('1. The postinstall script failed to run'); + console.error('2. The platform is not supported'); + console.error('3. The package was not installed correctly'); + console.error(''); + console.error('Try reinstalling the package:'); + console.error(' npm uninstall -g @serve.zone/moxytool'); + console.error(' npm install -g @serve.zone/moxytool'); + process.exit(1); + } + + // Spawn the binary with all arguments passed through + const child = spawn(binaryPath, process.argv.slice(2), { + stdio: 'inherit', + shell: false + }); + + // Handle child process events + child.on('error', (err) => { + console.error(`Error executing moxytool: ${err.message}`); + process.exit(1); + }); + + child.on('exit', (code, signal) => { + if (signal) { + process.kill(process.pid, signal); + } else { + process.exit(code || 0); + } + }); + + // Forward signals to child process + const signals = ['SIGINT', 'SIGTERM', 'SIGHUP']; + signals.forEach(signal => { + process.on(signal, () => { + if (!child.killed) { + child.kill(signal); + } + }); + }); +} + +// Execute +executeBinary(); diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..90c82e4 --- /dev/null +++ b/changelog.md @@ -0,0 +1,36 @@ +# Changelog + +## 2025-10-27 - 1.1.0 - feat(cli) +Add initial MOXYTOOL implementation, packaging, install/uninstall scripts, CI and release workflows + +- Add core CLI implementation (mod.ts and ts/): vgpu-setup command, logging, paths and plugins integration +- Add Deno config (deno.json) and build/test tasks +- Add compilation and packaging scripts (scripts/compile-all.sh, scripts/install-binary.js) and binary wrapper (bin/moxytool-wrapper.js) +- Add installer and uninstaller scripts (install.sh, uninstall.sh) for easy deployment +- Add CI, build and release workflows (.gitea/workflows/) including multi-platform compilation and npm publish steps +- Add documentation and metadata: readme.md, changelog.md, package.json and license +- Add .gitignore and dist/binaries handling, plus release checksum generation in workflows + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] - 2025-01-24 + +### Added +- Initial release of MOXYTOOL +- `vgpu-setup` command for automated Proxmox vGPU installation +- Support for NVIDIA vGPU on Proxmox hosts +- Interactive installer integration with wvthoog/proxmox-vgpu-installer +- Cross-platform binary support (Linux, macOS, Windows) +- Multi-architecture support (x64, arm64) + +### Features +- Automated vGPU driver download and installation +- Support for custom driver URLs and local files +- Debug mode for troubleshooting +- Step-by-step installation process +- Verification of Proxmox installation before setup + +[1.0.0]: https://code.foss.global/serve.zone/moxytool/releases/tag/v1.0.0 diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..4697b02 --- /dev/null +++ b/deno.json @@ -0,0 +1,57 @@ +{ + "name": "@serve.zone/moxytool", + "version": "1.0.0", + "exports": "./mod.ts", + "nodeModulesDir": "auto", + "tasks": { + "dev": "deno run --allow-all mod.ts", + "compile": "deno task compile:all", + "compile:all": "bash scripts/compile-all.sh", + "test": "deno test --allow-all test/", + "test:watch": "deno test --allow-all --watch test/", + "check": "deno check mod.ts", + "fmt": "deno fmt", + "lint": "deno lint" + }, + "lint": { + "rules": { + "tags": [ + "recommended" + ] + } + }, + "fmt": { + "useTabs": false, + "lineWidth": 100, + "indentWidth": 2, + "semiColons": true, + "singleQuote": true + }, + "compilerOptions": { + "lib": [ + "deno.window" + ], + "strict": true + }, + "imports": { + "@std/path": "jsr:@std/path@^1.0.0", + "@std/fmt": "jsr:@std/fmt@^1.0.0", + "@std/assert": "jsr:@std/assert@^1.0.0", + "@push.rocks/npmextra": "npm:@push.rocks/npmextra@^5.1.2", + "@push.rocks/projectinfo": "npm:@push.rocks/projectinfo@^5.0.1", + "@push.rocks/qenv": "npm:@push.rocks/qenv@^6.1.0", + "@push.rocks/smartcli": "npm:@push.rocks/smartcli@^4.0.11", + "@push.rocks/smartdelay": "npm:@push.rocks/smartdelay@^3.0.5", + "@push.rocks/smartfile": "npm:@push.rocks/smartfile@^11.0.23", + "@push.rocks/smartjson": "npm:@push.rocks/smartjson@^5.0.20", + "@push.rocks/smartlog": "npm:@push.rocks/smartlog@^3.0.7", + "@push.rocks/smartlog-destination-local": "npm:@push.rocks/smartlog-destination-local@^9.0.0", + "@push.rocks/smartpath": "npm:@push.rocks/smartpath@^5.0.5", + "@push.rocks/smartshell": "npm:@push.rocks/smartshell@^3.2.2", + "@push.rocks/smartexpect": "npm:@push.rocks/smartexpect@^1.0.15", + "@push.rocks/smartrx": "npm:@push.rocks/smartrx@^3.0.10", + "@push.rocks/smartpromise": "npm:@push.rocks/smartpromise@^4.0.0", + "@push.rocks/smartstring": "npm:@push.rocks/smartstring@^4.0.0", + "@push.rocks/smarttime": "npm:@push.rocks/smarttime@^4.0.0" + } +} diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..f7a82f2 --- /dev/null +++ b/install.sh @@ -0,0 +1,253 @@ +#!/bin/bash + +# MOXYTOOL Installer Script +# Downloads and installs pre-compiled MOXYTOOL binary from releases +# +# Usage: +# Direct piped installation (recommended): +# curl -sSL https://code.foss.global/serve.zone/moxytool/raw/branch/master/install.sh | sudo bash +# +# With version specification: +# curl -sSL https://code.foss.global/serve.zone/moxytool/raw/branch/master/install.sh | sudo bash -s -- --version v1.0.0 +# +# Options: +# -h, --help Show this help message +# --version VERSION Install specific version (e.g., v1.0.0) +# --install-dir DIR Installation directory (default: /opt/moxytool) + +set -e + +# Default values +SHOW_HELP=0 +SPECIFIED_VERSION="" +INSTALL_DIR="/opt/moxytool" +GITEA_BASE_URL="https://code.foss.global" +GITEA_REPO="serve.zone/moxytool" + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + SHOW_HELP=1 + shift + ;; + --version) + SPECIFIED_VERSION="$2" + shift 2 + ;; + --install-dir) + INSTALL_DIR="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" + echo "Use -h or --help for usage information" + exit 1 + ;; + esac +done + +if [ $SHOW_HELP -eq 1 ]; then + echo "MOXYTOOL Installer Script" + echo "Downloads and installs pre-compiled MOXYTOOL binary" + echo "" + echo "Usage: $0 [options]" + echo "" + echo "Options:" + echo " -h, --help Show this help message" + echo " --version VERSION Install specific version (e.g., v1.0.0)" + echo " --install-dir DIR Installation directory (default: /opt/moxytool)" + echo "" + echo "Examples:" + echo " # Install latest version" + echo " curl -sSL https://code.foss.global/serve.zone/moxytool/raw/branch/master/install.sh | sudo bash" + echo "" + echo " # Install specific version" + echo " curl -sSL https://code.foss.global/serve.zone/moxytool/raw/branch/master/install.sh | sudo bash -s -- --version v1.0.0" + exit 0 +fi + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + echo "Please run as root (sudo bash install.sh or pipe to sudo bash)" + exit 1 +fi + +# Helper function to detect OS and architecture +detect_platform() { + local os=$(uname -s) + local arch=$(uname -m) + + # Map OS + case "$os" in + Linux) + os_name="linux" + ;; + Darwin) + os_name="macos" + ;; + MINGW*|MSYS*|CYGWIN*) + os_name="windows" + ;; + *) + echo "Error: Unsupported operating system: $os" + echo "Supported: Linux, macOS, Windows" + exit 1 + ;; + esac + + # Map architecture + case "$arch" in + x86_64|amd64) + arch_name="x64" + ;; + aarch64|arm64) + arch_name="arm64" + ;; + *) + echo "Error: Unsupported architecture: $arch" + echo "Supported: x86_64/amd64 (x64), aarch64/arm64 (arm64)" + exit 1 + ;; + esac + + # Construct binary name + if [ "$os_name" = "windows" ]; then + echo "moxytool-${os_name}-${arch_name}.exe" + else + echo "moxytool-${os_name}-${arch_name}" + fi +} + +# Get latest release version from Gitea API +get_latest_version() { + echo "Fetching latest release version from Gitea..." >&2 + + local api_url="${GITEA_BASE_URL}/api/v1/repos/${GITEA_REPO}/releases/latest" + local response=$(curl -sSL "$api_url" 2>/dev/null) + + if [ $? -ne 0 ] || [ -z "$response" ]; then + echo "Error: Failed to fetch latest release information from Gitea API" >&2 + echo "URL: $api_url" >&2 + exit 1 + fi + + # Extract tag_name from JSON response + local version=$(echo "$response" | grep -o '"tag_name":"[^"]*"' | cut -d'"' -f4) + + if [ -z "$version" ]; then + echo "Error: Could not determine latest version from API response" >&2 + exit 1 + fi + + echo "$version" +} + +# Main installation process +echo "================================================" +echo " MOXYTOOL Installation Script" +echo "================================================" +echo "" + +# Detect platform +BINARY_NAME=$(detect_platform) +echo "Detected platform: $BINARY_NAME" +echo "" + +# Determine version to install +if [ -n "$SPECIFIED_VERSION" ]; then + VERSION="$SPECIFIED_VERSION" + echo "Installing specified version: $VERSION" +else + VERSION=$(get_latest_version) + echo "Installing latest version: $VERSION" +fi +echo "" + +# Construct download URL +DOWNLOAD_URL="${GITEA_BASE_URL}/${GITEA_REPO}/releases/download/${VERSION}/${BINARY_NAME}" +echo "Download URL: $DOWNLOAD_URL" +echo "" + +# Clean installation directory - ensure only binary exists +if [ -d "$INSTALL_DIR" ]; then + echo "Cleaning installation directory: $INSTALL_DIR" + rm -rf "$INSTALL_DIR" +fi + +# Create fresh installation directory +echo "Creating installation directory: $INSTALL_DIR" +mkdir -p "$INSTALL_DIR" + +# Download binary +echo "Downloading MOXYTOOL binary..." +TEMP_FILE="$INSTALL_DIR/moxytool.download" +curl -sSL "$DOWNLOAD_URL" -o "$TEMP_FILE" + +if [ $? -ne 0 ]; then + echo "Error: Failed to download binary from $DOWNLOAD_URL" + echo "" + echo "Please check:" + echo " 1. Your internet connection" + echo " 2. The specified version exists: ${GITEA_BASE_URL}/${GITEA_REPO}/releases" + echo " 3. The platform binary is available for this release" + rm -f "$TEMP_FILE" + exit 1 +fi + +# Check if download was successful (file exists and not empty) +if [ ! -s "$TEMP_FILE" ]; then + echo "Error: Downloaded file is empty or does not exist" + rm -f "$TEMP_FILE" + exit 1 +fi + +# Move to final location +BINARY_PATH="$INSTALL_DIR/moxytool" +mv "$TEMP_FILE" "$BINARY_PATH" + +if [ $? -ne 0 ] || [ ! -f "$BINARY_PATH" ]; then + echo "Error: Failed to move binary to $BINARY_PATH" + rm -f "$TEMP_FILE" 2>/dev/null + exit 1 +fi + +# Make executable +chmod +x "$BINARY_PATH" + +if [ $? -ne 0 ]; then + echo "Error: Failed to make binary executable" + exit 1 +fi + +echo "Binary installed successfully to: $BINARY_PATH" +echo "" + +# Check if /usr/local/bin is in PATH +if [[ ":$PATH:" == *":/usr/local/bin:"* ]]; then + BIN_DIR="/usr/local/bin" +else + BIN_DIR="/usr/bin" +fi + +# Create symlink for global access +ln -sf "$BINARY_PATH" "$BIN_DIR/moxytool" +echo "Symlink created: $BIN_DIR/moxytool -> $BINARY_PATH" + +echo "" +echo "================================================" +echo " MOXYTOOL Installation Complete!" +echo "================================================" +echo "" +echo "Installation details:" +echo " Binary location: $BINARY_PATH" +echo " Symlink location: $BIN_DIR/moxytool" +echo " Version: $VERSION" +echo "" +echo "Get started:" +echo " moxytool" +echo " moxytool vgpu-setup # Set up Proxmox vGPU support" +echo "" +echo "For vGPU setup, run:" +echo " sudo moxytool vgpu-setup" +echo "" diff --git a/license b/license new file mode 100644 index 0000000..e15cc84 --- /dev/null +++ b/license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Serve Zone + +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. diff --git a/mod.ts b/mod.ts new file mode 100644 index 0000000..cf4c611 --- /dev/null +++ b/mod.ts @@ -0,0 +1,48 @@ +#!/usr/bin/env -S deno run --allow-all + +/** + * MOXYTOOL - Proxmox Administration Tool + * + * A comprehensive CLI tool for managing Proxmox servers, including: + * - vGPU setup and configuration + * - VM management + * - Cluster configuration + * - Automated maintenance tasks + * + * Required Permissions: + * - --allow-net: Network access for downloading installers + * - --allow-read: Read configuration files + * - --allow-write: Write configuration files, logs + * - --allow-run: Execute system commands (git, bash, systemctl) + * - --allow-sys: Access system information + * - --allow-env: Read environment variables + * + * @module + */ + +import * as cli from './ts/moxytool.cli.ts'; + +/** + * Main entry point for the MOXYTOOL application + * Sets up the CLI environment and executes the requested command + */ +async function main(): Promise { + // Set environment variable to indicate CLI call + Deno.env.set('CLI_CALL', 'true'); + + // Execute the CLI + await cli.runCli(); +} + +// Execute main and handle errors +if (import.meta.main) { + try { + await main(); + } catch (error) { + console.error(`Error: ${error instanceof Error ? error.message : String(error)}`); + Deno.exit(1); + } +} + +// Export for library usage +export * from './ts/index.ts'; diff --git a/npmextra.json b/npmextra.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/npmextra.json @@ -0,0 +1 @@ +{} diff --git a/package.json b/package.json new file mode 100644 index 0000000..bdf26b2 --- /dev/null +++ b/package.json @@ -0,0 +1,61 @@ +{ + "name": "@serve.zone/moxytool", + "version": "1.0.0", + "description": "Proxmox administration tool for vGPU setup, VM management, and cluster configuration", + "keywords": [ + "proxmox", + "vgpu", + "virtualization", + "nvidia", + "gpu passthrough", + "server management", + "hypervisor", + "cli", + "automation", + "serve.zone" + ], + "homepage": "https://code.foss.global/serve.zone/moxytool", + "bugs": { + "url": "https://code.foss.global/serve.zone/moxytool/issues" + }, + "repository": { + "type": "git", + "url": "git+https://code.foss.global/serve.zone/moxytool.git" + }, + "author": "Serve Zone", + "license": "MIT", + "type": "module", + "bin": { + "moxytool": "./bin/moxytool-wrapper.js" + }, + "scripts": { + "postinstall": "node scripts/install-binary.js", + "prepublishOnly": "echo 'Publishing MOXYTOOL binaries to npm...'", + "test": "echo 'Tests are run with Deno: deno task test'", + "build": "echo 'no build needed'" + }, + "files": [ + "bin/", + "scripts/install-binary.js", + "readme.md", + "license", + "changelog.md" + ], + "engines": { + "node": ">=14.0.0" + }, + "os": [ + "darwin", + "linux", + "win32" + ], + "cpu": [ + "x64", + "arm64" + ], + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "packageManager": "pnpm@10.18.1+sha512.77a884a165cbba2d8d1c19e3b4880eee6d2fcabd0d879121e282196b80042351d5eb3ca0935fa599da1dc51265cc68816ad2bddd2a2de5ea9fdf92adbec7cd34" +} diff --git a/readme.hints.md b/readme.hints.md new file mode 100644 index 0000000..e69de29 diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..ad27e3b --- /dev/null +++ b/readme.md @@ -0,0 +1,172 @@ +# MOXYTOOL + +> Proxmox Administration Tool for vGPU setup, VM management, and cluster configuration + +[![npm version](https://badge.fury.io/js/@serve.zone%2Fmoxytool.svg)](https://www.npmjs.com/package/@serve.zone/moxytool) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) + +## Overview + +MOXYTOOL is a comprehensive command-line tool for managing Proxmox servers, with a focus on simplified vGPU setup and advanced server configuration. Built with Deno and compiled to native binaries for maximum performance and portability. + +## Features + +- **vGPU Setup**: Automated installation and configuration of NVIDIA vGPU support on Proxmox +- **Cross-Platform**: Native binaries for Linux, macOS, and Windows +- **Multi-Architecture**: Support for x64 and ARM64 processors +- **Interactive CLI**: User-friendly command-line interface with detailed guidance +- **Proxmox Integration**: Deep integration with Proxmox VE for seamless management + +## Installation + +### Global Installation (Recommended) + +```bash +npm install -g @serve.zone/moxytool +``` + +or with pnpm: + +```bash +pnpm install -g @serve.zone/moxytool +``` + +### Local Installation + +```bash +npm install @serve.zone/moxytool +``` + +## Usage + +### vGPU Setup + +Install and configure NVIDIA vGPU support on your Proxmox host: + +```bash +sudo moxytool vgpu-setup +``` + +#### Arguments + +- `--step ` - Force execution at a specific installation step +- `--url ` - Use a custom driver URL (.run or .zip format) +- `--file ` - Use a local driver file +- `--debug` - Enable debug output mode + +#### Examples + +```bash +# Basic setup with interactive prompts +sudo moxytool vgpu-setup + +# Use a custom driver URL +sudo moxytool vgpu-setup --url https://example.com/driver.run + +# Use a local driver file +sudo moxytool vgpu-setup --file /path/to/driver.run + +# Resume at a specific step +sudo moxytool vgpu-setup --step 2 + +# Debug mode +sudo moxytool vgpu-setup --debug +``` + +### Installation Process + +1. **Prerequisites**: Ensure virtualization is enabled in BIOS (Intel Vt-d or AMD IOMMU) +2. **Run Setup**: Execute `sudo moxytool vgpu-setup` +3. **Follow Prompts**: The installer will guide you through the process +4. **Reboot**: System will require a reboot after initial setup +5. **Complete Setup**: Run the command again after reboot to finish installation +6. **Verify**: Check installation with `mdevctl types` + +### Post-Installation + +After successful installation: + +1. **Verify vGPU profiles**: `mdevctl types` +2. **Configure VMs**: Add vGPU devices in Proxmox web UI (VM → Hardware → Add → PCI Device) +3. **Install guest drivers**: Download and install NVIDIA vGPU guest drivers in your VMs + +## Requirements + +- Proxmox VE 7.4+ or 8.x +- NVIDIA GPU with vGPU support +- Root/sudo access +- Internet connection for downloading drivers + +## Supported Platforms + +- **Linux**: x64, ARM64 +- **macOS**: x64, ARM64 (Apple Silicon) +- **Windows**: x64 + +## Development + +### Prerequisites + +- Deno 1.x or later +- Bash (for compilation scripts) + +### Building from Source + +```bash +# Clone the repository +git clone https://code.foss.global/serve.zone/moxytool.git +cd moxytool + +# Run locally with Deno +deno task dev + +# Compile binaries for all platforms +deno task compile:all + +# Run tests +deno task test +``` + +### Project Structure + +``` +moxytool/ +├── mod.ts # Main entry point +├── deno.json # Deno configuration +├── package.json # NPM package manifest +├── ts/ # TypeScript source files +│ ├── moxytool.cli.ts # CLI command definitions +│ ├── moxytool.plugins.ts # Plugin imports +│ ├── moxytool.logging.ts # Logging setup +│ ├── moxytool.paths.ts # Path definitions +│ └── index.ts # Node.js entry point +├── bin/ # Binary wrapper +│ └── moxytool-wrapper.js # NPM binary wrapper +├── scripts/ # Build scripts +│ ├── compile-all.sh # Compilation script +│ └── install-binary.js # Binary installation +└── dist/ # Compiled binaries + └── binaries/ +``` + +## Credits + +MOXYTOOL uses the excellent [proxmox-vgpu-installer](https://github.com/anomixer/proxmox-vgpu-installer) by anomixer for the core vGPU installation process, which supports Proxmox v9. + +## License + +MIT License - see [LICENSE](license) file for details + +## Support + +- **Issues**: [https://code.foss.global/serve.zone/moxytool/issues](https://code.foss.global/serve.zone/moxytool/issues) +- **Repository**: [https://code.foss.global/serve.zone/moxytool](https://code.foss.global/serve.zone/moxytool) + +## Related Projects + +- [NUPST](https://code.foss.global/serve.zone/nupst) - Network UPS Shutdown Tool +- [SPARK](https://code.foss.global/serve.zone/spark) - Server Configuration and Management Tool + +--- + +Made with ❤️ by [Serve Zone](https://serve.zone) diff --git a/scripts/compile-all.sh b/scripts/compile-all.sh new file mode 100755 index 0000000..27e2bf0 --- /dev/null +++ b/scripts/compile-all.sh @@ -0,0 +1,72 @@ +#!/bin/bash +set -e + +# Get version from deno.json +VERSION=$(cat deno.json | grep -o '"version": *"[^"]*"' | cut -d'"' -f4) +BINARY_DIR="dist/binaries" + +echo "================================================" +echo " MOXYTOOL Compilation Script" +echo " Version: ${VERSION}" +echo "================================================" +echo "" +echo "Compiling for all supported platforms..." +echo "" + +# Clean up old binaries and create fresh directory +rm -rf "$BINARY_DIR" +mkdir -p "$BINARY_DIR" +echo "→ Cleaned old binaries from $BINARY_DIR" +echo "" + +# Linux x86_64 +echo "→ Compiling for Linux x86_64..." +deno compile --allow-all --no-check --output "$BINARY_DIR/moxytool-linux-x64" \ + --target x86_64-unknown-linux-gnu mod.ts +echo " ✓ Linux x86_64 complete" +echo "" + +# Linux ARM64 +echo "→ Compiling for Linux ARM64..." +deno compile --allow-all --no-check --output "$BINARY_DIR/moxytool-linux-arm64" \ + --target aarch64-unknown-linux-gnu mod.ts +echo " ✓ Linux ARM64 complete" +echo "" + +# macOS x86_64 +echo "→ Compiling for macOS x86_64..." +deno compile --allow-all --no-check --output "$BINARY_DIR/moxytool-macos-x64" \ + --target x86_64-apple-darwin mod.ts +echo " ✓ macOS x86_64 complete" +echo "" + +# macOS ARM64 +echo "→ Compiling for macOS ARM64..." +deno compile --allow-all --no-check --output "$BINARY_DIR/moxytool-macos-arm64" \ + --target aarch64-apple-darwin mod.ts +echo " ✓ macOS ARM64 complete" +echo "" + +# Windows x86_64 +echo "→ Compiling for Windows x86_64..." +deno compile --allow-all --no-check --output "$BINARY_DIR/moxytool-windows-x64.exe" \ + --target x86_64-pc-windows-msvc mod.ts +echo " ✓ Windows x86_64 complete" +echo "" + +echo "================================================" +echo " Compilation Summary" +echo "================================================" +echo "" +ls -lh "$BINARY_DIR/" | tail -n +2 +echo "" +echo "✓ All binaries compiled successfully!" +echo "" +echo "Binary location: $BINARY_DIR/" +echo "" +echo "To create a release:" +echo " 1. Test the binaries on their respective platforms" +echo " 2. Create a git tag: git tag v${VERSION}" +echo " 3. Push the tag: git push origin v${VERSION}" +echo " 4. Upload the binaries to the release" +echo "" diff --git a/scripts/install-binary.js b/scripts/install-binary.js new file mode 100644 index 0000000..5d9d0c7 --- /dev/null +++ b/scripts/install-binary.js @@ -0,0 +1,228 @@ +#!/usr/bin/env node + +/** + * MOXYTOOL npm postinstall script + * Downloads the appropriate binary for the current platform from Gitea releases + */ + +import { platform, arch } from 'os'; +import { existsSync, mkdirSync, chmodSync, unlinkSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; +import https from 'https'; +import { pipeline } from 'stream'; +import { createWriteStream } from 'fs'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +// Configuration +const REPO_BASE = 'https://code.foss.global/serve.zone/moxytool'; +const VERSION = process.env.npm_package_version || '1.0.0'; + +function getBinaryInfo() { + const plat = platform(); + const architecture = arch(); + + const platformMap = { + 'darwin': 'macos', + 'linux': 'linux', + 'win32': 'windows' + }; + + const archMap = { + 'x64': 'x64', + 'arm64': 'arm64' + }; + + const mappedPlatform = platformMap[plat]; + const mappedArch = archMap[architecture]; + + if (!mappedPlatform || !mappedArch) { + return { supported: false, platform: plat, arch: architecture }; + } + + let binaryName = `moxytool-${mappedPlatform}-${mappedArch}`; + if (plat === 'win32') { + binaryName += '.exe'; + } + + return { + supported: true, + platform: mappedPlatform, + arch: mappedArch, + binaryName, + originalPlatform: plat + }; +} + +function downloadFile(url, destination) { + return new Promise((resolve, reject) => { + console.log(`Downloading from: ${url}`); + + // Follow redirects + const download = (url, redirectCount = 0) => { + if (redirectCount > 5) { + reject(new Error('Too many redirects')); + return; + } + + https.get(url, (response) => { + if (response.statusCode === 301 || response.statusCode === 302) { + console.log(`Following redirect to: ${response.headers.location}`); + download(response.headers.location, redirectCount + 1); + return; + } + + if (response.statusCode !== 200) { + reject(new Error(`Failed to download: ${response.statusCode} ${response.statusMessage}`)); + return; + } + + const totalSize = parseInt(response.headers['content-length'], 10); + let downloadedSize = 0; + let lastProgress = 0; + + response.on('data', (chunk) => { + downloadedSize += chunk.length; + const progress = Math.round((downloadedSize / totalSize) * 100); + + // Only log every 10% to reduce noise + if (progress >= lastProgress + 10) { + console.log(`Download progress: ${progress}%`); + lastProgress = progress; + } + }); + + const file = createWriteStream(destination); + + pipeline(response, file, (err) => { + if (err) { + reject(err); + } else { + console.log('Download complete!'); + resolve(); + } + }); + }).on('error', reject); + }; + + download(url); + }); +} + +async function main() { + console.log('==========================================='); + console.log(' MOXYTOOL - Binary Installation'); + console.log('==========================================='); + console.log(''); + + const binaryInfo = getBinaryInfo(); + + if (!binaryInfo.supported) { + console.error(`❌ Error: Unsupported platform/architecture: ${binaryInfo.platform}/${binaryInfo.arch}`); + console.error(''); + console.error('Supported platforms:'); + console.error(' • Linux (x64, arm64)'); + console.error(' • macOS (x64, arm64)'); + console.error(' • Windows (x64)'); + console.error(''); + console.error('If you believe your platform should be supported, please file an issue:'); + console.error(' https://code.foss.global/serve.zone/moxytool/issues'); + process.exit(1); + } + + console.log(`Platform: ${binaryInfo.platform} (${binaryInfo.originalPlatform})`); + console.log(`Architecture: ${binaryInfo.arch}`); + console.log(`Binary: ${binaryInfo.binaryName}`); + console.log(`Version: ${VERSION}`); + console.log(''); + + // Create dist/binaries directory if it doesn't exist + const binariesDir = join(__dirname, '..', 'dist', 'binaries'); + if (!existsSync(binariesDir)) { + console.log('Creating binaries directory...'); + mkdirSync(binariesDir, { recursive: true }); + } + + const binaryPath = join(binariesDir, binaryInfo.binaryName); + + // Check if binary already exists and skip download + if (existsSync(binaryPath)) { + console.log('✓ Binary already exists, skipping download'); + } else { + // Construct download URL + // Try release URL first, fall back to raw branch if needed + const releaseUrl = `${REPO_BASE}/releases/download/v${VERSION}/${binaryInfo.binaryName}`; + const fallbackUrl = `${REPO_BASE}/raw/branch/master/dist/binaries/${binaryInfo.binaryName}`; + + console.log('Downloading platform-specific binary...'); + console.log('This may take a moment depending on your connection speed.'); + console.log(''); + + try { + // Try downloading from release + await downloadFile(releaseUrl, binaryPath); + } catch (err) { + console.log(`Release download failed: ${err.message}`); + console.log('Trying fallback URL...'); + + try { + // Try fallback URL + await downloadFile(fallbackUrl, binaryPath); + } catch (fallbackErr) { + console.error(`❌ Error: Failed to download binary`); + console.error(` Primary URL: ${releaseUrl}`); + console.error(` Fallback URL: ${fallbackUrl}`); + console.error(''); + console.error('This might be because:'); + console.error('1. The release has not been created yet'); + console.error('2. Network connectivity issues'); + console.error('3. The version specified does not exist'); + console.error(''); + console.error('You can try:'); + console.error('1. Installing from source: https://code.foss.global/serve.zone/moxytool'); + console.error('2. Downloading the binary manually from the releases page'); + + // Clean up partial download + if (existsSync(binaryPath)) { + unlinkSync(binaryPath); + } + + process.exit(1); + } + } + + console.log(`✓ Binary downloaded successfully`); + } + + // On Unix-like systems, ensure the binary is executable + if (binaryInfo.originalPlatform !== 'win32') { + try { + console.log('Setting executable permissions...'); + chmodSync(binaryPath, 0o755); + console.log('✓ Binary permissions updated'); + } catch (err) { + console.error(`⚠️ Warning: Could not set executable permissions: ${err.message}`); + console.error(' You may need to manually run:'); + console.error(` chmod +x ${binaryPath}`); + } + } + + console.log(''); + console.log('✅ MOXYTOOL installation completed successfully!'); + console.log(''); + console.log('You can now use MOXYTOOL by running:'); + console.log(' moxytool --help'); + console.log(''); + console.log('For vGPU setup, run:'); + console.log(' sudo moxytool vgpu-setup'); + console.log(''); + console.log('==========================================='); +} + +// Run the installation +main().catch(err => { + console.error(`❌ Installation failed: ${err.message}`); + process.exit(1); +}); diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts new file mode 100644 index 0000000..0ee3c67 --- /dev/null +++ b/ts/00_commitinfo_data.ts @@ -0,0 +1,8 @@ +/** + * autocreated commitinfo by @push.rocks/commitinfo + */ +export const commitinfo = { + name: '@serve.zone/moxytool', + version: '1.1.0', + description: 'Proxmox administration tool for vGPU setup, VM management, and cluster configuration' +} diff --git a/ts/index.ts b/ts/index.ts new file mode 100644 index 0000000..13a28dd --- /dev/null +++ b/ts/index.ts @@ -0,0 +1,28 @@ +#!/usr/bin/env node + +/** + * MOXYTOOL - Proxmox Administration Tool + * Node.js entry point for library usage + */ + +import * as cli from './moxytool.cli.ts'; +import { logger } from './moxytool.logging.ts'; +import process from 'node:process'; + +/** + * Main entry point for MOXYTOOL + * Initializes the CLI and executes the given command + */ +async function main() { + await cli.runCli(); +} + +// Run the main function and handle any errors +main().catch((error) => { + logger.error(`Error: ${error}`); + process.exit(1); +}); + +// Export for library usage +export { logger }; +export * from './moxytool.cli.ts'; diff --git a/ts/moxytool.cli.ts b/ts/moxytool.cli.ts new file mode 100644 index 0000000..5ab222f --- /dev/null +++ b/ts/moxytool.cli.ts @@ -0,0 +1,112 @@ +import * as plugins from './moxytool.plugins.ts'; +import * as paths from './moxytool.paths.ts'; +import { logger } from './moxytool.logging.ts'; + +export const runCli = async () => { + const smartshellInstance = new plugins.smartshell.Smartshell({ + executor: 'bash', + }); + + const smartcliInstance = new plugins.smartcli.Smartcli(); + + // Standard command (no arguments) + smartcliInstance.standardCommand().subscribe(async () => { + logger.log('info', 'MOXYTOOL - Proxmox Administration Tool'); + logger.log('info', ''); + logger.log('info', 'Available commands:'); + logger.log('info', '* vgpu-setup - Install and configure Proxmox vGPU support'); + logger.log('info', ''); + logger.log('info', 'Usage: moxytool [options]'); + }); + + // vGPU setup command + smartcliInstance.addCommand('vgpu-setup').subscribe(async (argvArg) => { + logger.log('ok', 'Starting Proxmox vGPU setup process'); + logger.log('info', 'This will install vGPU support for NVIDIA GPUs on Proxmox'); + logger.log('info', ''); + + // Check for arguments + const step = argvArg.step; + const url = argvArg.url; + const file = argvArg.file; + const debug = argvArg.debug; + + // Check if running on Proxmox + try { + const result = await smartshellInstance.exec('which pveversion'); + if (result.exitCode !== 0) { + logger.log('error', 'This system does not appear to be running Proxmox'); + logger.log('error', 'Please run this command on a Proxmox host'); + Deno.exit(1); + } + } catch (e) { + logger.log('error', 'Failed to verify Proxmox installation'); + logger.log('error', 'Please ensure you are running this on a Proxmox host'); + Deno.exit(1); + } + + // Create temporary directory + const tmpDir = '/tmp/moxytool-vgpu-setup'; + await smartshellInstance.exec(`mkdir -p ${tmpDir}`); + + try { + // Step 1: Clone and run the installer script + logger.log('info', 'Step 1: Downloading Proxmox vGPU installer (supports v9)...'); + const cloneResult = await smartshellInstance.exec( + `cd ${tmpDir} && git clone https://github.com/anomixer/proxmox-vgpu-installer.git`, + ); + + if (cloneResult.exitCode !== 0) { + logger.log('error', 'Failed to clone the installer repository'); + logger.log('error', cloneResult.stderr || 'Unknown error'); + Deno.exit(1); + } + + logger.log('ok', 'Installer downloaded successfully'); + logger.log('info', ''); + + // Build command with arguments + let command = 'bash proxmox-installer.sh'; + if (step) { + command += ` --step ${step}`; + } + if (url) { + command += ` --url ${url}`; + } + if (file) { + command += ` --file ${file}`; + } + if (debug) { + command += ' --debug'; + } + + logger.log('info', 'Running installer script...'); + logger.log('info', 'Please follow the interactive prompts'); + logger.log('info', ''); + + // Run the installer script interactively + const installResult = await smartshellInstance.exec( + `cd ${tmpDir}/proxmox-vgpu-installer && ${command}`, + ); + + if (installResult.exitCode !== 0) { + logger.log('error', 'Installer script failed'); + logger.log('error', installResult.stderr || 'Unknown error'); + Deno.exit(1); + } + + logger.log('ok', 'vGPU setup process completed'); + logger.log('info', ''); + logger.log('info', 'Next steps:'); + logger.log('info', '1. If prompted, reboot your system'); + logger.log('info', '2. After reboot, run this command again to continue setup'); + logger.log('info', '3. Verify installation with: mdevctl types'); + logger.log('info', '4. Configure your VMs with vGPU in the Proxmox web UI'); + } catch (error) { + logger.log('error', `Setup failed: ${error instanceof Error ? error.message : String(error)}`); + Deno.exit(1); + } + }); + + smartcliInstance.startParse(); +}; diff --git a/ts/moxytool.logging.ts b/ts/moxytool.logging.ts new file mode 100644 index 0000000..1771e1f --- /dev/null +++ b/ts/moxytool.logging.ts @@ -0,0 +1,57 @@ +import * as plugins from './moxytool.plugins.ts'; + +/** + * A simple logger class for MOXYTOOL + */ +class Logger { + private static instance: Logger; + + public static getInstance(): Logger { + if (!Logger.instance) { + Logger.instance = new Logger(); + } + return Logger.instance; + } + + public log(level: string, message: string): void { + const timestamp = new Date().toISOString(); + + switch (level) { + case 'error': + console.error(`[${timestamp}] [ERROR] ${message}`); + break; + case 'warn': + console.warn(`[${timestamp}] [WARN] ${message}`); + break; + case 'ok': + case 'success': + console.log(`[${timestamp}] [OK] ${message}`); + break; + case 'info': + default: + console.log(`[${timestamp}] [INFO] ${message}`); + break; + } + } + + public error(message: string): void { + this.log('error', message); + } + + public warn(message: string): void { + this.log('warn', message); + } + + public info(message: string): void { + this.log('info', message); + } + + public success(message: string): void { + this.log('success', message); + } +} + +/** + * Logger instance for MOXYTOOL + */ +export const logger = Logger.getInstance(); diff --git a/ts/moxytool.paths.ts b/ts/moxytool.paths.ts new file mode 100644 index 0000000..76d145a --- /dev/null +++ b/ts/moxytool.paths.ts @@ -0,0 +1,21 @@ +import * as plugins from './moxytool.plugins.ts'; + +/** + * Package directory path + */ +export const packageDir = plugins.path.dirname(plugins.path.dirname(import.meta.url)); + +/** + * Data directory for moxytool configurations + */ +export const dataDir = '/etc/moxytool'; + +/** + * Log directory + */ +export const logDir = plugins.path.join(dataDir, 'logs'); + +/** + * Temporary working directory + */ +export const tmpDir = plugins.path.join(dataDir, 'tmp'); diff --git a/ts/moxytool.plugins.ts b/ts/moxytool.plugins.ts new file mode 100644 index 0000000..85e77fa --- /dev/null +++ b/ts/moxytool.plugins.ts @@ -0,0 +1,29 @@ +// std library scope +import * as path from '@std/path'; + +export { path }; + +// @pushrocks scope +import * as npmextra from '@push.rocks/npmextra'; +import * as projectinfo from '@push.rocks/projectinfo'; +import * as smartcli from '@push.rocks/smartcli'; +import * as smartdelay from '@push.rocks/smartdelay'; +import * as smartfile from '@push.rocks/smartfile'; +import * as smartjson from '@push.rocks/smartjson'; +import * as smartlog from '@push.rocks/smartlog'; +import * as smartlogDestinationLocal from '@push.rocks/smartlog-destination-local'; +import * as smartpath from '@push.rocks/smartpath'; +import * as smartshell from '@push.rocks/smartshell'; + +export { + npmextra, + projectinfo, + smartcli, + smartdelay, + smartfile, + smartjson, + smartlog, + smartlogDestinationLocal, + smartpath, + smartshell, +}; diff --git a/uninstall.sh b/uninstall.sh new file mode 100755 index 0000000..f1e7ddf --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +# MOXYTOOL Uninstaller Script +# Completely removes MOXYTOOL from the system + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + echo "Please run as root (sudo ./uninstall.sh)" + exit 1 +fi + +# This script can be called directly or through the CLI +# When called through the CLI, environment variables are set +# REMOVE_CONFIG=yes|no - whether to remove configuration files +# REMOVE_DATA=yes|no - whether to remove data files + +# If not set through CLI, use defaults +REMOVE_CONFIG=${REMOVE_CONFIG:-"no"} +REMOVE_DATA=${REMOVE_DATA:-"no"} + +echo "MOXYTOOL Uninstaller" +echo "====================" +echo "This will completely remove MOXYTOOL from your system." + +# Find the directory where this script is located +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + +# Step 1: Remove global symlinks +if [ -L "/usr/local/bin/moxytool" ]; then + echo "Removing global symlink from /usr/local/bin/moxytool..." + rm -f /usr/local/bin/moxytool +fi + +if [ -L "/usr/bin/moxytool" ]; then + echo "Removing global symlink from /usr/bin/moxytool..." + rm -f /usr/bin/moxytool +fi + +# Step 2: Remove configuration if requested +if [ "$REMOVE_CONFIG" = "yes" ]; then + if [ -d "/etc/moxytool" ]; then + echo "Removing configuration directory /etc/moxytool..." + rm -rf /etc/moxytool + fi +else + if [ -d "/etc/moxytool" ]; then + echo "Configuration preserved in /etc/moxytool (use --remove-config to remove)" + fi +fi + +# Step 3: Remove data if requested +if [ "$REMOVE_DATA" = "yes" ]; then + if [ -d "/var/lib/moxytool" ]; then + echo "Removing data directory /var/lib/moxytool..." + rm -rf /var/lib/moxytool + fi + if [ -d "/var/log/moxytool" ]; then + echo "Removing log directory /var/log/moxytool..." + rm -rf /var/log/moxytool + fi + if [ -d "/tmp/moxytool-vgpu-setup" ]; then + echo "Removing temporary installation directory /tmp/moxytool-vgpu-setup..." + rm -rf /tmp/moxytool-vgpu-setup + fi +else + if [ -d "/var/lib/moxytool" ] || [ -d "/var/log/moxytool" ]; then + echo "Data and logs preserved (use --remove-data to remove)" + fi +fi + +# Step 4: Remove installation directory +if [ -d "/opt/moxytool" ]; then + echo "Removing installation directory /opt/moxytool..." + rm -rf /opt/moxytool +fi + +echo "" +echo "================================================" +echo " MOXYTOOL Uninstallation Complete" +echo "================================================" +echo "" +echo "MOXYTOOL has been removed from your system." + +if [ "$REMOVE_CONFIG" = "no" ] && [ -d "/etc/moxytool" ]; then + echo "" + echo "Configuration has been preserved in /etc/moxytool" + echo "To remove it, run: sudo rm -rf /etc/moxytool" +fi + +if [ "$REMOVE_DATA" = "no" ]; then + if [ -d "/var/lib/moxytool" ] || [ -d "/var/log/moxytool" ]; then + echo "" + echo "Data and logs have been preserved in:" + [ -d "/var/lib/moxytool" ] && echo " - /var/lib/moxytool" + [ -d "/var/log/moxytool" ] && echo " - /var/log/moxytool" + [ -d "/tmp/moxytool-vgpu-setup" ] && echo " - /tmp/moxytool-vgpu-setup" + echo "To remove them, run:" + [ -d "/var/lib/moxytool" ] && echo " sudo rm -rf /var/lib/moxytool" + [ -d "/var/log/moxytool" ] && echo " sudo rm -rf /var/log/moxytool" + [ -d "/tmp/moxytool-vgpu-setup" ] && echo " sudo rm -rf /tmp/moxytool-vgpu-setup" + fi +fi + +echo "" +echo "Thank you for using MOXYTOOL!" +echo ""