packaging

Module: packaging Cohesion: 0.80 Members: 0

packaging

The packaging module is responsible for preparing and distributing the code-buddy application across various operating systems and package managers. It contains platform-specific configuration files, build scripts, and wrappers to ensure code-buddy can be easily installed and run by end-users, abstracting away the underlying Node.js runtime and build artifacts.

This module currently supports:

Common Principles

Regardless of the target platform, the packaging process generally follows these steps:

  1. Build the Core Application: The code-buddy application itself is a Node.js project. All packaging processes assume that the main project's npm ci (install dependencies) and npm run build (compile to dist/) commands have been executed, producing the necessary JavaScript bundles and assets.
  2. Bundle Node.js Runtime (Optional but common): To provide a consistent and isolated environment, most packages bundle a specific version of the Node.js runtime, rather than relying on a system-wide installation.
  3. Create a Wrapper Script: A small script (e.g., buddy, code-buddy.sh, codebuddy.cmd) is created as the primary executable. This wrapper sets up the necessary environment variables and then invokes the bundled Node.js runtime to run the main dist/index.js entry point.
  4. Install Supporting Files: Licenses, documentation (README, CHANGELOG), and configuration files are included.
  5. Platform-Specific Integration: This includes creating shortcuts, adding to system PATH, defining uninstall procedures, and managing permissions.

Arch Linux (AUR) Packaging

The packaging/aur/PKGBUILD file defines how code-buddy is built and packaged for Arch Linux and its derivatives, typically for submission to the Arch User Repository (AUR).

PKGBUILD Breakdown

The PKGBUILD is a shell script that makepkg executes to build a package.

    build() {
        cd "$srcdir/$pkgname-$pkgver"
        npm ci --ignore-scripts # Install project dependencies
        npm run build         # Build the project
    }

This function is responsible for compiling the source code. It navigates into the extracted source directory, installs Node.js dependencies using npm ci, and then executes the npm run build script defined in the project's package.json to generate the dist directory.

    package() {
        cd "$srcdir/$pkgname-$pkgver"

        # Create installation directories
        install -dm755 "$pkgdir/usr/lib/$pkgname"
        install -dm755 "$pkgdir/usr/bin"
        install -dm755 "$pkgdir/usr/share/licenses/$pkgname"
        install -dm755 "$pkgdir/usr/share/doc/$pkgname"

        # Copy built files
        cp -r dist "$pkgdir/usr/lib/$pkgname/"
        cp -r node_modules "$pkgdir/usr/lib/$pkgname/"
        cp package.json "$pkgdir/usr/lib/$pkgname/"

        # Create executable wrapper
        cat > "$pkgdir/usr/bin/buddy" << 'EOF'
#!/bin/bash
exec node /usr/lib/code-buddy/dist/index.js "$@"
EOF
        chmod 755 "$pkgdir/usr/bin/buddy"

        # Create symlink for alternative name
        ln -s buddy "$pkgdir/usr/bin/code-buddy"

        # Install license and documentation
        install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
        install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md"
        install -Dm644 CONTRIBUTING.md "$pkgdir/usr/share/doc/$pkgname/CONTRIBUTING.md" 2>/dev/null || true
        install -Dm644 CHANGELOG.md "$pkgdir/usr/share/doc/$pkgname/CHANGELOG.md" 2>/dev/null || true
    }

This function stages the files into the $pkgdir (the root of the future package).

  1. It creates standard FHS (Filesystem Hierarchy Standard) directories.
  2. It copies the dist directory (containing the built application), node_modules (production dependencies), and package.json into /usr/lib/code-buddy.
  3. It creates a buddy shell script in /usr/bin. This script acts as the entry point, executing the main index.js using the system's node interpreter.
  4. A symbolic link code-buddy is created, pointing to buddy, allowing users to invoke the application using either name.
  5. License and documentation files are installed into their respective FHS locations.
    post_install() {
        echo "==> Code Buddy has been installed!"
        echo "==> Run 'buddy' or 'code-buddy' to start"
        echo "==> Set GROK_API_KEY environment variable before use"
        echo "==> See /usr/share/doc/code-buddy/README.md for more information"
    }

    post_upgrade() {
        post_install
    }

These functions provide informative messages to the user after the package has been installed or upgraded, guiding them on how to use the application and important setup steps (like setting GROK_API_KEY).

Snap Packaging

Snap packages provide a universal, containerized way to distribute applications across various Linux distributions. The packaging/snap/ directory contains the snapcraft.yaml configuration and a wrapper script.

snapcraft.yaml Breakdown

The snapcraft.yaml file describes how to build and package the application as a Snap.

    apps:
      code-buddy:
        command: bin/code-buddy
        plugs:
          - home
          - network
          - network-bind
          - removable-media
        environment:
          NODE_ENV: production

      buddy:
        command: bin/code-buddy
        plugs:
          - home
          - network
          - network-bind
          - removable-media
        environment:
          NODE_ENV: production

This defines the commands that users can run. Both code-buddy and buddy point to the same bin/code-buddy wrapper script.

        parts:
          code-buddy:
            plugin: npm
            source: .
            npm-include-node: true
            npm-node-version: "20.10.0"
            build-packages:
              - build-essential
              - python3
            stage-packages:
              - git
              - ripgrep
            override-build: |
              craftctl default
              npm run build
            organize:
              lib/node_modules/code-buddy: /

This part uses the npm plugin to build the Node.js application.

          wrapper:
            plugin: dump
            source: packaging/snap/
            organize:
              code-buddy.sh: bin/code-buddy
            after: [code-buddy]

This part copies the code-buddy.sh wrapper script from packaging/snap/ and places it in bin/code-buddy within the Snap. It runs after the code-buddy part to ensure the application files are already staged.

    layout:
      /usr/bin/git:
        bind-file: $SNAP/usr/bin/git
      /usr/bin/rg:
        bind-file: $SNAP/usr/bin/rg

This section creates "bind mounts" within the Snap's filesystem. It makes the git and rg (ripgrep) executables that are staged inside the Snap (from stage-packages) available at the conventional /usr/bin/git and /usr/bin/rg paths within the Snap's sandbox. This allows code-buddy to find and execute these tools as if they were system-wide.

    hooks:
      configure:
        plugs: [network]

Defines hooks that run at specific points in the Snap's lifecycle. The configure hook is given network access.

code-buddy.sh Wrapper

#!/bin/bash
# Code Buddy Snap Wrapper

# Set up environment
export HOME="${SNAP_USER_DATA}"
export NODE_ENV="${NODE_ENV:-production}"

# Run the application
exec "${SNAP}/bin/node" "${SNAP}/dist/index.js" "$@"

This simple shell script is the actual entry point for the code-buddy and buddy commands within the Snap.

Windows Installer Packaging

The packaging/windows/ directory contains scripts and configuration files for building native Windows installers (both NSIS .exe and WiX .msi).

Build Process (build-installer.ps1)

The build-installer.ps1 PowerShell script orchestrates the entire Windows installer creation process.

    Push-Location $ProjectRoot
    try {
        npm ci
        npm run build
    } finally {
        Pop-Location
    }

It navigates to the project root, installs dependencies, and runs the npm run build command to generate the dist directory.

    Push-Location $BuildDir
    try {
        & "$BuildDir\node\npm.cmd" install --production --ignore-scripts
    } finally {
        Pop-Location
    }

It then uses the bundled Node.js's npm.cmd to install only production dependencies within the $BuildDir. This ensures the final package is lean.

Windows Build Flow

The build-installer.ps1 script orchestrates a multi-stage build process:

graph TD
    A[Project Root] --> B(build-installer.ps1)
    B --> C{Download Node.js}
    B --> D{npm ci & npm run build}
    B --> E{Copy App Files}
    B --> F{npm install --production}
    B --> G{Build NSIS Installer (EXE)}
    B --> H{Build WiX Installer (MSI)}

    C --> I[Bundled Node.js]
    D --> J[Built `dist` & `package.json`]
    E --> K[Staging Dir with App & Node.js]
    F --> L[Staging Dir with Prod `node_modules`]

    G --> M[CodeBuddy-X.Y.Z-setup.exe]
    H --> N[CodeBuddy-X.Y.Z.msi]

    K -- uses --> G
    K -- uses --> H
    L -- uses --> G
    L -- uses --> H

codebuddy.cmd Wrapper

@echo off
:: Code Buddy CLI Wrapper for Windows

setlocal EnableDelayedExpansion

:: Get the directory where this script is located
set "SCRIPT_DIR=%~dp0"

:: Set up Node.js path
set "NODE_EXE=%SCRIPT_DIR%node\node.exe"

:: Check if bundled Node.js exists, fall back to system Node.js
if not exist "%NODE_EXE%" (
    where node >nul 2>&1
    if errorlevel 1 (
        echo Error: Node.js is not installed. Please install Node.js or reinstall Code Buddy.
        exit /b 1
    )
    set "NODE_EXE=node"
)

:: Set environment variables
set "NODE_ENV=production"
set "CODEBUDDY_HOME=%APPDATA%\codebuddy"

:: Create config directory if it doesn't exist
if not exist "%CODEBUDDY_HOME%" mkdir "%CODEBUDDY_HOME%"

:: Run Code Buddy
"%NODE_EXE%" "%SCRIPT_DIR%dist\index.js" %*

This batch script serves as the primary executable for code-buddy on Windows.

installer.nsi (NSIS Script)

The installer.nsi script defines the behavior of the Nullsoft Scriptable Install System (NSIS) installer, which produces the CodeBuddy-X.Y.Z-setup.exe executable.

WiX MSI Installer (Dynamic product.wxs)

The WiX Toolset is used to create .msi (Microsoft Installer) packages. The build-installer.ps1 script dynamically generates the product.wxs file, which is the primary WiX source.

  1. heat.exe dir . -cg ProductComponents ... -out files.wxs: heat.exe is used to "harvest" all files from the $BuildDir and generate files.wxs, which contains definitions for each file, grouped under ProductComponents.
  2. candle.exe product.wxs files.wxs: candle.exe compiles the .wxs source files into .wixobj object files.
  3. light.exe -ext WixUIExtension product.wixobj files.wixobj -o "$OutputDir\CodeBuddy-$Version.msi": light.exe links the .wixobj files, along with the WixUIExtension (for standard UI dialogs), into the final CodeBuddy-X.Y.Z.msi installer.

Contributing to Packaging

When contributing to the packaging module, consider the following: