Files
ansiblings/packages/nopy
Benjamin Diedrichsen 587ff2cf47
Publish snapshot / snapshot (push) Failing after 1m58s
Add release pipeline and upgrade toolchain to TypeScript 7
Publishing infrastructure
- Three Gitea workflows: ci.yml (PRs, non-main pushes), publish-snapshot.yml
  (main -> Gitea under dist-tag @main) and release.yml (tags -> Gitea + npmjs)
- Tag-driven releases as <package-dir>-v<version>; the manifest stays the
  source of truth and release.yml refuses to run if tag and manifest disagree
- Every publish is idempotent: each step checks the registry first, so a run
  that fails on the second registry can simply be re-run
- Hard coverage gate (85% branches) shared by CI, the pre-push hook and local
  runs, since the thresholds live in vitest.config.ts rather than a CI flag
- README.PUBLISH.md documents the whole mechanism

Toolchain
- TypeScript 7 native compiler; drop tsgo and ts-node, use tsx for dev runs
- Biome 1.9 -> 2.x, Vitest 1 -> 4, zod 3 -> 4, inquirer 8 -> 14, pnpm 11.17.0
- Replace inquirer-checkbox-plus-prompt, which is peer-capped at inquirer <9,
  with enquirer's AutoComplete; the CubeSelection contract is unchanged
- Stand in for zod 4's removed z.AnyZodObject with a local AnyObjectSchema

Repo hygiene
- Stop tracking dist/; ignore coverage/, *.tsbuildinfo, .npmrc* and release.json
- Drop package-lock.json in favour of pnpm-lock.yaml

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 15:17:14 +02:00
..
2026-07-27 13:09:00 +02:00
2026-07-27 13:09:00 +02:00
2026-07-27 13:09:00 +02:00
2026-07-27 13:09:00 +02:00

Nopy

A CLI tool that simplifies pyinfra script management and execution, providing an interactive workflow for deploying infrastructure configurations ("cubes") to remote hosts.

Overview

Nopy wraps pyinfra with structure, validation, and an interactive experience for managing complex infrastructure deployments. It organizes deployments into self-contained "cubes" with dependency management, schema validation, and lifecycle hooks.

Core Concepts

Cubes

Self-contained deployment units consisting of:

  • Python deployment script: <cube-name>.deploy.py
  • JavaScript manifest: <cube-name>.manifest.mjs defining schema, dependencies, defaults, and hooks
  • Configuration variables: Validated with Zod schemas

Cube Manifest

import { z } from 'zod'
import { cubes } from '@bitstack/nopy'

export default cubes.Manifest({
    id: 'apt:install',
    name: 'Install packages with apt',
    dependencies: () => [],
    schema: z.object({
        UPDATE: z.boolean().describe('Update package cache').default(false),
        PACKAGES: z.string().describe('Space-separated list of packages').default('vim htop'),
    })
})

Variable Defaults

Variable defaults are defined directly in the Zod schema using .default(). This ensures that every cube has a predictable starting state and provides type-safe default values.

Priority order (lowest to highest):

  1. Zod schema .default() values
  2. Global env from .nopyrc.json
  3. Accumulated variables from dependencies
  4. User prompts / session replay

This allows cubes to ship with reasonable defaults while still allowing users to override them globally via .nopyrc.json or interactively during deployment.

Configuration

Uses .nopyrc.json files (project-level or home directory) containing:

{
  "hosts": ["host1.example.com", "host2.example.com"],
  "cubeDirs": ["./cubes", "../shared-cubes"],
  "env": {
    "SHARED_VAR": "value"
  },
  "log": {
    "verbosity": "info",
    "debug": false
  }
}

Logging Configuration

Control pyinfra output verbosity and debug information using the log configuration object:

log.verbosity - Controls the level of information printed during execution:

Verbosity PyInfra Flag Description Use Case
"silent" (none) Minimal output (default) Production deployments, clean output
"info" -v Print meta information See what operations are running
"verbose" -vv Include input data Debug parameters and configuration
"trace" -vvv Full command output See all command outputs and details

log.debug - Enables pyinfra's internal debug logging:

Value PyInfra Flag Description Use Case
false (none) No debug logs (default) Normal operation
true --debug Enable pyinfra debug logs Deep debugging of pyinfra internals

Examples:

Basic troubleshooting:

{
  "log": {
    "verbosity": "info"
  }
}

Debug command failures:

{
  "log": {
    "verbosity": "trace"
  }
}

Deep debugging with pyinfra internals:

{
  "log": {
    "verbosity": "trace",
    "debug": true
  }
}

Recommendation: Start with "info" for typical troubleshooting, use "trace" when investigating command failures, and enable debug: true only when debugging pyinfra itself.

Session Recording and Replay

Nopy supports recording deployment sessions to JSON files for later replay. This is useful for:

  • Repeatable deployments
  • CI/CD pipelines
  • Documentation and auditing
  • Sharing configurations across teams

Session File Format

Sessions are stored in .nopysession.json files with the following structure:

{
  "version": "1.0.0",
  "name": "My Deployment Session",
  "timestamp": "2025-10-13T10:30:00Z",
  "cubes": [
    {
      "key": "apt:essentials",
      "variables": {
        "UPDATE": true
      }
    },
    {
      "key": "apt-more",
      "variables": {
        "SOME_VAR": "value"
      }
    }
  ],
  "hosts": [
    "@docker/nopy-test-container"
  ],
  "env": {
    "KEY_DIR": "../../vault/tmp"
  },
  "auth": {
    "method": "ssh-key",
    "username": "root"
  }
}

Structure Details:

  • cubes: Array of cubes with only cube-specific variables (not global env vars)
  • env: Global environment variables shared across cubes (like in .nopyrc.json)
  • hosts: Array of target hosts
  • auth: Authentication configuration (passwords are never stored)

Security Note: Passwords are never stored in session files. If a session uses password authentication, you'll be prompted for the password during replay.

Recording a Session

# Run deployment interactively and save the session
nopy install --save-session my-deployment.nopysession.json

# With defaults (no prompts for variables)
nopy install -D --save-session automated-deployment.nopysession.json

Replaying a Session

# Load and execute a saved session
nopy install --load-session my-deployment.nopysession.json

# Session replay uses the exact cubes, variables, and hosts from the file
# Only password authentication will prompt for credentials

Cube Discovery

Nopy searches for cubes in:

  1. Directories specified in .nopyrc.json cubeDirs
  2. Directories containing a .npcubes marker file (searching upwards from current directory)

Command Line Usage

Installation

This package is part of a yarn workspace monorepo. Install from the repository root:

# From repository root (/ansiblings)
yarn install
yarn workspace @bitstack/nopy build

To use the nopy command globally, you can:

  1. Use yarn workspace command:

    yarn workspace @bitstack/nopy nopy
    
  2. Link the package globally:

    cd packages/nopy
    npm link
    # Now you can use 'nopy' from anywhere
    nopy install
    
  3. Use via npm scripts (from packages/nopy directory):

    yarn nopy
    

Basic Commands

Install cubes (default command):

nopy install
# or simply
nopy

Install with defaults (no prompts for customization):

nopy install --use-defaults
# or
nopy install -D

Use SSH key authentication:

nopy install --auth-method-key
# or
nopy install -K

Repeat last run:

nopy install --repeat-last-run
# or
nopy install -R

Save session for replay:

nopy install --save-session my-deployment.nopysession.json
# or
nopy install -s my-deployment.nopysession.json

Load and replay session:

nopy install --load-session my-deployment.nopysession.json
# or
nopy install -l my-deployment.nopysession.json

Combined options:

nopy install -D -K  # Use defaults + SSH key auth
nopy install -D -s session.nopysession.json  # Use defaults and save session

Advanced Options

Dry run (preview without executing):

nopy install --dry-run

Shows the execution plan including commands, environment variables, and targets without running anything. Sensitive data is masked in output.

Parallel execution:

nopy install --parallel

Executes independent cubes in parallel using a dependency graph. Cubes are grouped into execution stages, with a default concurrency limit of 4.

JSON output (for CI/CD):

nopy install --json
nopy history --json

Machine-readable JSON output for scripting and CI/CD integration.

Continue on error:

nopy install --continue-on-error

Continue deploying remaining cubes even if one fails.

View deployment history:

nopy history              # List recent deployments
nopy install -H <id>      # Replay a specific deployment by ID

Development

Run without building:

npm run nopy

Debug:

npm run debug

Workflow

  1. Load cubes - Discovers and validates cubes from configured directories
  2. Interactive prompts - Select cubes, target host, and authentication method
  3. Dependency resolution - Topologically sorts cubes based on dependencies
  4. Variable assignment - Validates and collects configuration with schema validation
  5. Execute hooks - Runs before/after hooks for orchestration
  6. Deploy - Sequentially executes pyinfra commands

Features

  • Dependency resolution with topological sorting
  • Parallel execution of independent cubes in stages
  • Before/after hooks for multi-cube orchestration
  • SSH key or password authentication
  • Default values with optional customization via manifest env
  • Schema validation using Zod
  • Recursive cube directory discovery
  • Dry-run mode for previewing deployments
  • JSON output for CI/CD integration
  • Session history with replay capability

Documentation

Resources