Skip to content

Pipeline Design inline

Seth Ford edited this page Mar 2, 2026 · 8 revisions

Design: Add a hello command that prints 'hello world' to the shipwright CLI

Context

Shipwright is a CLI orchestration tool with 100+ modular commands, each implemented as standalone bash scripts. The hello command is a simple demonstration command that:

  • Outputs exactly "hello world" when invoked
  • Provides --help and -h flags for usage information
  • Integrates with Shipwright's established command router pattern
  • Must follow strict Bash 3.2 compatibility and error-handling conventions

Codebase Constraints:

  • All commands follow the same modular script pattern in scripts/
  • Router dispatch in scripts/sw uses case statements with exec
  • Mandatory conventions: set -euo pipefail, VERSION constant, ERR trap, no Bash 4.x+ features
  • Test files mirror command files (e.g., sw-hello.shsw-hello-test.sh)

Decision

Chosen Approach: Implement hello command as a standalone modular script following Shipwright's established Command Router pattern.

Components & Interfaces

┌─────────────────────────────────────────────────────────────┐
│                        Router (scripts/sw)                   │
│  • Case statement dispatches "hello" to sw-hello.sh          │
│  • Uses exec for subprocess execution                        │
└──────────────────────────┬──────────────────────────────────┘
                           │ exec → $SCRIPT_DIR/sw-hello.sh
                           │
┌──────────────────────────▼──────────────────────────────────┐
│              Hello Command (scripts/sw-hello.sh)             │
│  • Parses args: --help, -h, or error on unknown args        │
│  • Outputs: "hello world" or help text                      │
│  • Exit codes: 0 (success), 1 (error)                       │
└──────────────────────────┬──────────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────────┐
│           Test Suite (scripts/sw-hello-test.sh)              │
│  • 5 unit tests covering all code paths                     │
│  • Uses test-helpers.sh (assert_pass/assert_fail)           │
│  • Verifies output, help, exit codes, invalid args          │
└─────────────────────────────────────────────────────────────┘

Interface Contracts

Command Interface (bash):

# Input
$ shipwright hello [OPTIONS]

# Options
--help, -h     Show usage information
<invalid>      Exit with error code 1

# Output
STDOUT: "hello world"    (on success)
STDOUT: <help text>      (on --help or -h)
STDERR: <error message>  (on invalid args)

# Exit Codes
0               Success
1               Invalid arguments or error

Router Integration:

# In scripts/sw main() function, case statement:
case "$cmd" in
    hello)
        exec "$SCRIPT_DIR/sw-hello.sh" "$@"
        ;;
esac

Data Flow

User Input (CLI)
    ↓
scripts/sw router
    ├─ Parses command name: "hello"
    ├─ Loads SCRIPT_DIR
    └─ exec → sw-hello.sh with all args
         ↓
    sw-hello.sh
    ├─ Parses args (--help, -h, or error)
    ├─ Calls show_help() or main()
    └─ Output to STDOUT
         ↓
    User sees output / help / error

Error Boundaries

Layer Responsibility Error Handling
Router Command dispatch If exec fails: bash returns non-zero (no cleanup needed)
Hello Script Argument parsing, output Invalid args → show help, exit 1
Test Suite Verify behavior Test failures → exit 1, visible in npm test

Error Propagation:

  • Invalid args at CLI → show_help() + exit 1
  • Unknown flags → error message + exit 1
  • Router exec failure → bash error (rare, caught by ERR trap)

Alternatives Considered

1. Inline Command in Router (scripts/sw)

Pros: Single file, no new files to maintain
Cons:

  • Violates Shipwright's established 100+ command pattern
  • Makes 617-line router harder to read and maintain
  • Untestable in isolation
  • Creates precedent for code bloat in router

Rejected. Architectural anti-pattern for this codebase.

2. Shared Library (e.g., lib/hello-logic.sh)

Pros: Reusable logic if multiple commands need greeting functionality
Cons:

  • Over-engineered for a single 67-line command
  • Violates YAGNI (You Aren't Gonna Need It)
  • Adds unnecessary abstraction layer
  • Test setup becomes more complex

Rejected. Premature abstraction; no clear reuse case.

3. TypeScript Command (compiles to Node.js)

Pros: Type safety, modern language
Cons:

  • Breaks from 100% bash codebase
  • Requires build step and runtime (Node.js)
  • Contradicts Shipwright's bash-only philosophy
  • Incompatible with all existing Shipwright patterns

Rejected. Architectural mismatch; Shipwright is pure bash.


Implementation Plan

Files to Create

  1. scripts/sw-hello.sh (67 lines)

    • Main command script
    • Implements main(), show_help()
    • Parses args and outputs "hello world"
    • Follows script template (VERSION, ERR trap, Bash 3.2 compatible)
  2. scripts/sw-hello-test.sh (66 lines)

    • Unit test suite with 5 tests
    • Tests: exact output, help flags, exit codes, invalid args
    • Uses lib/test-helpers.sh helpers
    • Passes ✅ when bash scripts/sw-hello-test.sh runs

Files to Modify

  1. scripts/sw (2 lines added)
    • Router case statement
    • Add between existing commands (alphabetically ordered)
    • Pattern: hello) exec "$SCRIPT_DIR/sw-hello.sh" "$@" ;;

Dependencies

  • Internal: lib/test-helpers.sh (for test harness only, via sourcing)
  • External: None (bash stdlib only)
  • No new npm packages required

Risk Areas

Risk Severity Mitigation
Router registration typo Low Manual verification + test execution
Output format mismatch Low Unit test verifies exact string match
Bash 3.2 compatibility Very Low No bash 4.x+ features (no ${var,,}, no associative arrays)
Exit code semantics Very Low Test suite covers both 0 (success) and 1 (error) paths
Help flag parsing Very Low Tests verify both --help and -h work

Validation Criteria

  • Script Syntax: bash -n scripts/sw-hello.sh succeeds (no syntax errors)
  • Router Registration: Grep confirms hello) case in scripts/sw with correct exec syntax
  • Test Suite Passes: bash scripts/sw-hello-test.sh outputs 5/5 PASS
  • CLI Integration: shipwright hello outputs exactly "hello world\n"
  • CLI Alias Works: sw hello outputs exactly "hello world\n"
  • Help Flags: shipwright hello --help and shipwright hello -h show usage (non-empty output)
  • Error Handling: shipwright hello --invalid exits with code 1
  • Bash Conventions Met:
    • Script has set -euo pipefail
    • Script has VERSION constant matching package.json
    • Script has ERR trap with proper error message
    • No Bash 4.x+ features (case-insensitive expansion, associative arrays, etc.)
    • Proper header comment block
  • Full Test Suite: npm test passes with no regressions (all 102 test suites)
  • Reproducibility: Second run of all validation steps produces identical results

Architectural Properties

Single Responsibility: Hello command is responsible only for:

  • Parsing --help/-h flags
  • Outputting "hello world" or help text
  • Exit code semantics

Modularity: Command is isolated in its own script; changes to hello do not affect:

  • Router logic
  • Other commands
  • Test harness
  • Test helpers

Testability: Command can be tested without:

  • Full Shipwright CLI
  • Network calls
  • External dependencies
  • Mock binaries

Extensibility: If greeting commands multiply later (goodbye, greet <name>), they follow identical pattern — no refactoring of hello command needed.


Historical Context & Lessons

Previous Iteration Failure: "output missing: intake" (14 occurrences, weight: 127834)

  • Root cause: Test expected intake stage output; hello command didn't produce it
  • Resolution: Hello command is a leaf command, not a pipeline stage — no intake output needed
  • Design implication: Keep hello focused; don't conflate with pipeline machinery

Key Learning: Shipwright's distinction between:

  • Commands (leaf): sw-hello.sh, sw-version.sh — standalone
  • Stages (composite): pipeline orchestration with intake/plan/build/test/etc.

This design respects that boundary.

Clone this wiki locally