# NUPST Migration Plan: Node.js → Deno v4.0.0 **Migration Goal**: Convert NUPST from Node.js to Deno with single-executable distribution **Version**: 3.1.2 → 4.0.0 (breaking changes) **Platforms**: Linux x64/ARM64, macOS x64/ARM64, Windows x64 --- ## Phase 0: Planning & Preparation - [x] Research Deno compilation targets and npm: specifier support - [x] Analyze current codebase structure and dependencies - [x] Define CLI command structure simplification - [x] Create detailed migration task list - [ ] Create feature branch: `migration/deno-v4` - [ ] Backup current working state with git tag: `v3.1.2-pre-deno-migration` --- ## Phase 1: Dependency Migration (4-6 hours) ### 1.1 Analyze Current Dependencies - [ ] List all production dependencies from `package.json` - Current: `net-snmp@3.20.0` - [ ] List all dev dependencies to be removed - `@git.zone/tsbuild`, `@git.zone/tsrun`, `@git.zone/tstest`, `@push.rocks/qenv`, `@push.rocks/tapbundle`, `@types/node` - [ ] Identify Node.js built-in module usage - `child_process` (execSync) - `https` (for version checking) - `fs` (readFileSync, writeFileSync, existsSync, mkdirSync) - `path` (join, dirname, resolve) ### 1.2 Create Deno Configuration - [ ] Create `deno.json` with project configuration ```json { "name": "@serve.zone/nupst", "version": "4.0.0", "exports": "./mod.ts", "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 tests/", "check": "deno check mod.ts" }, "lint": { "rules": { "tags": ["recommended"] } }, "fmt": { "useTabs": false, "lineWidth": 100, "indentWidth": 2, "semiColons": true }, "compilerOptions": { "lib": ["deno.window"], "strict": true }, "imports": { "@std/cli": "jsr:@std/cli@^1.0.0", "@std/fmt": "jsr:@std/fmt@^1.0.0", "@std/path": "jsr:@std/path@^1.0.0" } } ``` ### 1.3 Update Import Statements - [ ] `ts/snmp/manager.ts`: Change `import * as snmp from 'net-snmp'` to `import * as snmp from "npm:net-snmp@3.20.0"` - [ ] `ts/cli.ts`: Change `import { execSync } from 'child_process'` to `import { execSync } from "node:child_process"` - [ ] `ts/nupst.ts`: Change `import * as https from 'https'` to `import * as https from "node:https"` - [ ] Search for all `fs` imports and update to `node:fs` - [ ] Search for all `path` imports and update to `node:path` - [ ] Update all relative imports to use `.ts` extension instead of `.js` - Example: `'./nupst.js'` → `'./nupst.ts'` ### 1.4 Test npm: Specifier Compatibility - [ ] Create test file: `tests/snmp_compatibility_test.ts` - [ ] Test SNMP v1 connection with npm:net-snmp - [ ] Test SNMP v2c connection with npm:net-snmp - [ ] Test SNMP v3 connection with npm:net-snmp - [ ] Verify native addon loading works in compiled binary --- ## Phase 2: Code Structure Refactoring (3-4 hours) ### 2.1 Create Main Entry Point - [ ] Create `mod.ts` as main Deno entry point: ```typescript #!/usr/bin/env -S deno run --allow-all /** * NUPST - UPS Shutdown Tool for Deno * * Required Permissions: * --allow-net: SNMP communication with UPS devices * --allow-read: Configuration file access (/etc/nupst/config.json) * --allow-write: Configuration file updates * --allow-run: System commands (systemctl, shutdown) * --allow-sys: System information (hostname, OS info) * --allow-env: Environment variables */ import { NupstCli } from './ts/cli.ts'; const cli = new NupstCli(); await cli.parseAndExecute(Deno.args); ``` ### 2.2 Update All Import Extensions Files to update (change .js → .ts in imports): - [ ] `ts/index.ts` - [ ] `ts/cli.ts` (imports from ./nupst.js, ./logger.js) - [ ] `ts/nupst.ts` (imports from ./snmp/manager.js, ./daemon.js, etc.) - [ ] `ts/daemon.ts` (imports from ./snmp/manager.js, ./logger.js, ./helpers/) - [ ] `ts/systemd.ts` (imports from ./daemon.js, ./logger.js) - [ ] `ts/cli/service-handler.ts` - [ ] `ts/cli/group-handler.ts` - [ ] `ts/cli/ups-handler.ts` - [ ] `ts/snmp/index.ts` - [ ] `ts/snmp/manager.ts` (imports from ./types.js, ./oid-sets.js) - [ ] `ts/snmp/oid-sets.ts` (imports from ./types.js) - [ ] `ts/helpers/index.ts` - [ ] `ts/logger.ts` ### 2.3 Update process.argv References - [ ] `ts/cli.ts`: Replace `process.argv` with `Deno.args` (adjust indexing: process.argv[2] → Deno.args[0]) - [ ] Update parseAndExecute method to work with Deno.args (0-indexed vs 2-indexed) ### 2.4 Update File System Operations - [ ] Search for `fs.readFileSync()` → Consider using `Deno.readTextFile()` or keep node:fs - [ ] Search for `fs.writeFileSync()` → Consider using `Deno.writeTextFile()` or keep node:fs - [ ] Search for `fs.existsSync()` → Keep node:fs or use Deno.stat - [ ] Search for `fs.mkdirSync()` → Keep node:fs or use Deno.mkdir - [ ] Decision: Keep node:fs for consistency or migrate to Deno APIs? ### 2.5 Update Path Operations - [ ] Verify all `path.join()`, `path.resolve()`, `path.dirname()` work with node:path - [ ] Consider using `@std/path` from JSR for better Deno integration ### 2.6 Handle __dirname and __filename - [ ] Find all `__dirname` usage - [ ] Replace with `import.meta.dirname` (Deno) or `dirname(fromFileUrl(import.meta.url))` - [ ] Find all `__filename` usage - [ ] Replace with `import.meta.filename` or `fromFileUrl(import.meta.url)` --- ## Phase 3: CLI Command Simplification (3-4 hours) ### 3.1 Design New Command Structure Current → New mapping: ``` OLD NEW === === nupst enable → nupst service enable nupst disable → nupst service disable nupst daemon-start → nupst service start-daemon nupst logs → nupst service logs nupst stop → nupst service stop nupst start → nupst service start nupst status → nupst service status nupst add → nupst ups add nupst edit [id] → nupst ups edit [id] nupst delete → nupst ups remove nupst list → nupst ups list nupst setup → nupst ups edit (removed alias) nupst test → nupst ups test nupst group list → nupst group list nupst group add → nupst group add nupst group edit → nupst group edit nupst group delete → nupst group remove nupst config → nupst config show nupst update → nupst update nupst uninstall → nupst uninstall nupst help → nupst help / nupst --help (new) → nupst --version ``` ### 3.2 Update CLI Parser (ts/cli.ts) - [ ] Refactor `parseAndExecute()` to handle new command structure - [ ] Add `service` subcommand handler - [ ] Add `ups` subcommand handler - [ ] Keep `group` subcommand handler (already exists, just update delete→remove) - [ ] Add `config` subcommand handler with `show` default - [ ] Add `--version` flag handler - [ ] Update `help` command to show new structure - [ ] Add command aliases: `rm` → `remove`, `ls` → `list` - [ ] Add `--json` flag for machine-readable output (future enhancement) ### 3.3 Update Command Handlers - [ ] `ts/cli/service-handler.ts`: Update method names if needed - [ ] `ts/cli/ups-handler.ts`: Rename `delete()` → `remove()`, remove `setup` method - [ ] `ts/cli/group-handler.ts`: Rename `delete()` → `remove()` ### 3.4 Improve Help Messages - [ ] Update `showHelp()` in ts/cli.ts with new command structure - [ ] Update `showGroupHelp()` in ts/cli.ts - [ ] Add `showServiceHelp()` method - [ ] Add `showUpsHelp()` method - [ ] Add `showConfigHelp()` method - [ ] Include usage examples in help text ### 3.5 Add Version Command - [ ] Read version from deno.json - [ ] Create `--version` handler in CLI - [ ] Display version with build info --- ## Phase 4: Compilation & Distribution (2-3 hours) ### 4.1 Create Compilation Script - [ ] Create directory: `scripts/` - [ ] Create `scripts/compile-all.sh`: ```bash #!/bin/bash set -e VERSION=$(cat deno.json | jq -r '.version') BINARY_DIR="dist/binaries" echo "Compiling NUPST v${VERSION} for all platforms..." mkdir -p "$BINARY_DIR" # Linux x86_64 echo "→ Linux x86_64..." deno compile --allow-all --output "$BINARY_DIR/nupst-linux-x64" \ --target x86_64-unknown-linux-gnu mod.ts # Linux ARM64 echo "→ Linux ARM64..." deno compile --allow-all --output "$BINARY_DIR/nupst-linux-arm64" \ --target aarch64-unknown-linux-gnu mod.ts # macOS x86_64 echo "→ macOS x86_64..." deno compile --allow-all --output "$BINARY_DIR/nupst-macos-x64" \ --target x86_64-apple-darwin mod.ts # macOS ARM64 echo "→ macOS ARM64..." deno compile --allow-all --output "$BINARY_DIR/nupst-macos-arm64" \ --target aarch64-apple-darwin mod.ts # Windows x86_64 echo "→ Windows x86_64..." deno compile --allow-all --output "$BINARY_DIR/nupst-windows-x64.exe" \ --target x86_64-pc-windows-msvc mod.ts echo "" echo "✓ Compilation complete!" ls -lh "$BINARY_DIR/" ``` - [ ] Make script executable: `chmod +x scripts/compile-all.sh` ### 4.2 Test Local Compilation - [ ] Run `deno task compile` to compile for all platforms - [ ] Verify all 5 binaries are created - [ ] Check binary sizes (should be reasonable, < 100MB each) - [ ] Test local binary on current platform: `./dist/binaries/nupst-linux-x64 --version` ### 4.3 Update Installation Scripts - [ ] Update `install.sh`: - Remove Node.js download logic (lines dealing with vendor/node-*) - Add detection for binary download from GitHub releases - Simplify to download appropriate binary based on OS/arch - Place binary in `/opt/nupst/bin/nupst` - Create symlink: `/usr/local/bin/nupst → /opt/nupst/bin/nupst` - Update to v4.0.0 in script - [ ] Simplify or remove `setup.sh` (no longer needed without Node.js) - [ ] Update `bin/nupst` launcher: - Option A: Keep as simple wrapper - Option B: Remove and symlink directly to binary - [ ] Update `uninstall.sh`: - Remove vendor directory cleanup - Update paths to new binary location ### 4.4 Update Systemd Service - [ ] Update systemd service file path in `ts/systemd.ts` - [ ] Verify ExecStart points to correct binary location: `/opt/nupst/bin/nupst daemon-start` - [ ] Remove Node.js environment variables if any - [ ] Test service installation and startup --- ## Phase 5: Testing & Validation (4-6 hours) ### 5.1 Create Deno Test Suite - [ ] Create `tests/` directory (or migrate from existing `test/`) - [ ] Create `tests/snmp_test.ts`: Test SNMP manager functionality - [ ] Create `tests/config_test.ts`: Test configuration loading/saving - [ ] Create `tests/cli_test.ts`: Test CLI parsing and command routing - [ ] Create `tests/daemon_test.ts`: Test daemon logic - [ ] Remove dependency on @git.zone/tstest and @push.rocks/tapbundle - [ ] Use Deno's built-in test runner (`Deno.test()`) ### 5.2 Unit Tests - [ ] Test SNMP connection with mock responses - [ ] Test configuration validation - [ ] Test UPS status parsing for different models - [ ] Test group logic (redundant/non-redundant modes) - [ ] Test threshold checking - [ ] Test version comparison logic ### 5.3 Integration Tests - [ ] Test CLI command parsing for all commands - [ ] Test config file creation and updates - [ ] Test UPS add/edit/remove operations - [ ] Test group add/edit/remove operations - [ ] Mock systemd operations for testing ### 5.4 Binary Testing - [ ] Test compiled binary on Linux x64 - [ ] Test compiled binary on Linux ARM64 (if available) - [ ] Test compiled binary on macOS x64 (if available) - [ ] Test compiled binary on macOS ARM64 (if available) - [ ] Test compiled binary on Windows x64 (if available) - [ ] Verify SNMP functionality works in compiled binary - [ ] Verify config file operations work in compiled binary - [ ] Test systemd integration with compiled binary ### 5.5 Performance Testing - [ ] Measure binary size for each platform - [ ] Measure startup time: `time ./nupst-linux-x64 --version` - [ ] Measure memory footprint during daemon operation - [ ] Compare with Node.js version performance - [ ] Document performance metrics ### 5.6 Upgrade Path Testing - [ ] Create test with v3.x config - [ ] Verify v4.x can read existing config - [ ] Test migration from old commands to new commands - [ ] Verify systemd service upgrade path --- ## Phase 6: Distribution Strategy (2-3 hours) ### 6.1 GitHub Actions Workflow - [ ] Create `.github/workflows/release.yml`: ```yaml name: Release on: push: tags: - 'v*' jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: denoland/setup-deno@v1 with: deno-version: v1.x - name: Compile binaries run: deno task compile - name: Generate checksums run: | cd dist/binaries sha256sum * > SHA256SUMS - name: Create Release uses: softprops/action-gh-release@v1 with: files: dist/binaries/* generate_release_notes: true ``` ### 6.2 Update package.json for npm - [ ] Update version to 4.0.0 - [ ] Update description to mention Deno - [ ] Add postinstall script to symlink appropriate binary: ```json { "name": "@serve.zone/nupst", "version": "4.0.0", "description": "UPS Shutdown Tool - Deno-based single executable", "bin": { "nupst": "bin/nupst-npm-wrapper.js" }, "type": "module", "scripts": { "postinstall": "node bin/setup-npm-binary.js" }, "files": [ "dist/binaries/*", "bin/*" ] } ``` - [ ] Create `bin/setup-npm-binary.js` to symlink correct binary - [ ] Create `bin/nupst-npm-wrapper.js` as entry point ### 6.3 Verify Distribution Methods - [ ] Test GitHub release download and installation - [ ] Test npm install from tarball - [ ] Test direct install.sh script - [ ] Verify all methods create working installation --- ## Phase 7: Documentation Updates (2-3 hours) ### 7.1 Update README.md - [ ] Remove Node.js requirements section - [ ] Update features list (mention Deno, single executable) - [ ] Update installation methods: - Method 1: Quick install script (updated) - Method 2: GitHub releases (new) - Method 3: npm (updated with notes) - [ ] Update usage section with new command structure - [ ] Add command mapping table (v3 → v4) - [ ] Update platform support matrix (note: no Windows ARM) - [ ] Update "System Changes" section (no vendor directory) - [ ] Update security section (remove Node.js mentions) - [ ] Update uninstallation instructions ### 7.2 Create MIGRATION.md - [ ] Create detailed migration guide from v3.x to v4.x - [ ] List all breaking changes: 1. CLI command structure reorganization 2. No Node.js requirement 3. Windows ARM not supported 4. Installation path changes - [ ] Provide command mapping table - [ ] Explain config compatibility - [ ] Document upgrade procedure - [ ] Add rollback instructions ### 7.3 Update CHANGELOG.md - [ ] Add v4.0.0 section with all breaking changes - [ ] List new features (Deno, single executable) - [ ] List improvements (startup time, binary size) - [ ] List removed features (Windows ARM, setup command alias) - [ ] Migration guide reference ### 7.4 Update Help Text - [ ] Ensure all help commands show new structure - [ ] Add examples for common operations - [ ] Include migration notes in help output --- ## Phase 8: Cleanup & Finalization (1 hour) ### 8.1 Remove Obsolete Files - [ ] Delete `vendor/` directory (Node.js binaries) - [ ] Delete `dist/` directory (old compiled JS) - [ ] Delete `dist_ts/` directory (old compiled TS) - [ ] Delete `node_modules/` directory - [ ] Remove or update `tsconfig.json` (decide if needed for npm compatibility) - [ ] Remove `setup.sh` if no longer needed - [ ] Remove old test files in `test/` if migrated to `tests/` - [ ] Delete `pnpm-lock.yaml` ### 8.2 Update Git Configuration - [ ] Update `.gitignore`: ``` # Deno .deno/ deno.lock # Compiled binaries dist/binaries/ # Old Node.js artifacts (to be removed) node_modules/ vendor/ dist/ dist_ts/ pnpm-lock.yaml ``` - [ ] Add `deno.lock` to version control - [ ] Create `.denoignore` if needed ### 8.3 Final Validation - [ ] Run `deno check mod.ts` - verify no type errors - [ ] Run `deno lint` - verify code quality - [ ] Run `deno fmt --check` - verify formatting - [ ] Run `deno task test` - verify all tests pass - [ ] Run `deno task compile` - verify all binaries compile - [ ] Test each binary manually ### 8.4 Prepare for Release - [ ] Create git tag: `v4.0.0` - [ ] Push to main branch - [ ] Push tags to trigger release workflow - [ ] Verify GitHub Actions workflow succeeds - [ ] Verify binaries are attached to release - [ ] Test installation from GitHub release - [ ] Publish to npm: `npm publish` - [ ] Test npm installation --- ## Rollback Strategy If critical issues are discovered: - [ ] Keep `v3.1.2` tag available for rollback - [ ] Create `v3-stable` branch for continued v3 maintenance - [ ] Update install.sh to offer v3/v4 choice - [ ] Document known issues in GitHub Issues - [ ] Provide downgrade instructions in docs --- ## Success Criteria Checklist - [ ] ✅ All 5 platform binaries compile successfully - [ ] ✅ Binary sizes are reasonable (< 100MB per platform) - [ ] ✅ Startup time < 2 seconds - [ ] ✅ SNMP v1/v2c/v3 functionality verified on real UPS device - [ ] ✅ All CLI commands work with new structure - [ ] ✅ Config file compatibility maintained - [ ] ✅ Systemd integration works on Linux - [ ] ✅ Installation scripts work on fresh systems - [ ] ✅ npm package still installable and functional - [ ] ✅ All tests pass - [ ] ✅ Documentation is complete and accurate - [ ] ✅ GitHub release created with binaries - [ ] ✅ Migration guide tested by following it step-by-step --- ## Timeline - **Phase 0**: 1 hour ✓ (in progress) - **Phase 1**: 4-6 hours - **Phase 2**: 3-4 hours - **Phase 3**: 3-4 hours - **Phase 4**: 2-3 hours - **Phase 5**: 4-6 hours - **Phase 6**: 2-3 hours - **Phase 7**: 2-3 hours - **Phase 8**: 1 hour **Total Estimate**: 22-31 hours --- ## Notes & Decisions ### Key Decisions Made: 1. ✅ Use npm:net-snmp (no pure Deno SNMP library available) 2. ✅ Major version bump to 4.0.0 (breaking changes) 3. ✅ CLI reorganization with subcommands 4. ✅ Keep npm publishing alongside binary distribution 5. ✅ 5 platform targets (Windows ARM not supported by Deno yet) ### Open Questions: - [ ] Should we keep tsconfig.json for npm package compatibility? - [ ] Should we fully migrate to Deno APIs (Deno.readFile) or keep node:fs? - [ ] Should we remove the `bin/nupst` wrapper or keep it? - [ ] Should setup.sh be completely removed or kept for dependencies? ### Risk Areas: - ⚠️ SNMP native addon compatibility in compiled binaries (HIGH PRIORITY TO TEST) - ⚠️ Systemd integration with new binary structure - ⚠️ Config migration from v3 to v4 - ⚠️ npm package installation with embedded binaries