Files
nupst/readme.plan.md
Juergen Kunz 071ded9c41
All checks were successful
CI / Build All Platforms (Tag/Main only) (push) Has been skipped
CI / Type Check & Lint (push) Successful in 6s
CI / Build Test (Current Platform) (push) Successful in 6s
style: configure deno fmt to use single quotes
- Add singleQuote: true to deno.json fmt configuration
- Reformat all files with single quotes using deno fmt
2025-10-19 13:14:18 +00:00

19 KiB

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

  • Research Deno compilation targets and npm: specifier support
  • Analyze current codebase structure and dependencies
  • Define CLI command structure simplification
  • 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
    {
      "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:
    #!/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 <id>            → nupst ups remove <id>
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 <id>        → nupst group edit <id>
nupst group delete <id>      → nupst group remove <id>

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: rmremove, lslist
  • 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:
    #!/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:
    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:
    {
      "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