-
Notifications
You must be signed in to change notification settings - Fork 1
Pipeline Design inline
Shipwright is a CLI orchestration tool with 100+ subcommands, each implemented as a standalone bash script (scripts/sw-<name>.sh) dispatched by a central router (scripts/sw). The ping command is a liveness probe — a minimal diagnostic that confirms the CLI is on $PATH, executable, and returns a known exit code. This pattern is well-established in networked systems and developer tooling (Docker, kubectl, etc.) as the simplest possible health check.
Constraints from the codebase:
- All scripts must be Bash 3.2 compatible (no associative arrays, no
${var,,}, noreadarray) -
set -euo pipefailand an ERR trap are mandatory on every script -
VERSION="3.2.4"must appear at the top of every script and stay in sync withpackage.json - The router (
scripts/sw) dispatches via acasestatement usingexec— new commands get onecaseentry immediately before the*)wildcard at line ~608 - Test files mirror implementation files 1:1 (
sw-ping-test.sh↔sw-ping.sh) - Test suites are registered in
package.json's"test"script as a&&-chained bash invocation sequence
┌──────────────────────────────────────────────────────────────┐
│ User Shell │
│ $ shipwright ping │
└─────────────────────┬────────────────────────────────────────┘
│ exec via PATH
▼
┌──────────────────────────────────────────────────────────────┐
│ Router scripts/sw │
│ main() → case "$cmd" in │
│ ping) exec "$SCRIPT_DIR/sw-ping.sh" "$@" ←── NEW │
│ hello) exec "$SCRIPT_DIR/sw-hello.sh" "$@" │
│ *) error + exit 1 │
│ esac │
└─────────────────────┬────────────────────────────────────────┘
│ exec (replaces router process)
▼
┌──────────────────────────────────────────────────────────────┐
│ Command scripts/sw-ping.sh │
│ main() → case "${1:-}" in │
│ "") echo "pong" && exit 0 ← happy path │
│ --help|-h) show_help && exit 0 │
│ --version) echo "$VERSION" && exit 0 │
│ *) error + show_help && exit 1 │
│ esac │
└──────────────────────────────────────────────────────────────┘
│ independent test execution
▼
┌──────────────────────────────────────────────────────────────┐
│ Test Suite scripts/sw-ping-test.sh │
│ Calls sw-ping.sh directly (not via router) │
│ 6 assertions: output, exit code, --help, -h, --version, │
│ invalid option │
│ Reports PASS/FAIL counts; exits 1 if any FAIL > 0 │
└──────────────────────────────────────────────────────────────┘
Implement ping as a standalone script (scripts/sw-ping.sh) registered in the central router, following the identical pattern as sw-hello.sh. The command has a single responsibility: print pong\n to stdout and exit 0 when called with no arguments.
Specific patterns applied:
-
main()uses acase "${1:-}"switch — the:-default preventsset -ufrom aborting when no argument is passed -
execin the router replaces the router process with the command process — no subprocess overhead, no double-ERR-trap noise - Helper fallbacks (
info(),success(), etc.) are inlined as one-liners, sourced conditionally fromlib/helpers.sh, so the script works both standalone and within the full CLI -
((PASS++))arithmetic in the test file is safe underpipefailon Bash 3.2 — confirmed by the existingsw-hello-test.shwhich uses this exact pattern at lines 15 and 29 - Test file uses inline
assert_equals/assert_exit_codehelpers (notlib/test-helpers.sh) — matching thesw-hello-test.shself-contained pattern
# sw-ping.sh — Public interface # Invocation (no args): happy path sw-ping.sh # stdout: "pong\n" # stderr: (empty) # exit: 0 # Invocation (--help or -h): help text sw-ping.sh --help | -h # stdout: help text containing "USAGE" # stderr: (empty) # exit: 0 # Invocation (--version): version string sw-ping.sh --version # stdout: semver string matching /^[0-9]+\.[0-9]+\.[0-9]+/ # stderr: (empty) # exit: 0 # Invocation (unknown flag): error path sw-ping.sh --anything-else # stdout: help text # stderr: error message "Unknown option: ..." # exit: 1 # Router contract # scripts/sw dispatches: exec "$SCRIPT_DIR/sw-ping.sh" "$@" # The router passes ALL remaining args verbatim — sw-ping.sh owns its own arg parsing
# sw-ping-test.sh — Test harness interface # Outputs to stdout: colored PASS/FAIL lines + "PASS: N" + "FAIL: N" # Exit code: 0 if FAIL==0, else 1 # No external dependencies — self-contained (no lib/test-helpers.sh) # Direct invocation: bash scripts/sw-ping-test.sh
Request path (no args):
User → shell → scripts/sw [cmd=ping] → exec sw-ping.sh → main("") → echo "pong" → stdout → exit 0
Request path (--help):
User → shell → scripts/sw [cmd=ping, args=--help] → exec sw-ping.sh --help
→ main("--help") → show_help() → stdout → exit 0
Error path (unknown flag):
User → shell → scripts/sw [cmd=ping, args=--bogus] → exec sw-ping.sh --bogus
→ main("--bogus") → error() [stderr] + show_help() [stdout] → exit 1
Test execution path (isolated):
npm test → bash sw-ping-test.sh
→ sw-ping.sh (direct, no router)
→ capture stdout + exit code
→ assert_equals / assert_exit_code → PASS/FAIL counters → exit 0|1
| Component | Errors It Handles | Errors It Propagates |
|---|---|---|
scripts/sw (router) |
Unknown command name → error + exit 1
|
Passes all args verbatim to sw-ping.sh; does not catch ping errors |
sw-ping.sh |
Unknown flag → error + exit 1; missing helpers (test env) → fallback helpers |
ERR trap catches unexpected failures; exits with bash error code |
sw-ping-test.sh |
Assertion failures → FAIL++ + diagnostic message; never aborts mid-suite |
set -euo pipefail still active — unexpected script errors exit immediately with line/status |
Key boundary: The router uses exec (not a subshell), so the ping script's exit code becomes the router's exit code directly. There is no error translation layer between them.
-
Inline in router (
scripts/sw) — Pros: No new file, zero dispatch overhead. Cons: Violates the single responsibility of the router (it routes, not executes); breaks testability (can't callsw-ping.shdirectly); deviates from every other command; makes the router grow without bound. Rejected. -
Shared
pingfunction inlib/helpers.sh— Pros: Reusable if other scripts wanted a liveness check. Cons: Premature abstraction — nothing else needsping;lib/helpers.shis for output/event utilities, not commands; adds coupling to a shared library that has no other command logic. Rejected. -
shipwright pingas a router alias forshipwright hello— Pros: Near-zero implementation. Cons: Wrong output (hello world≠pong), wrong semantics, confusing to users, not independently testable. Rejected.
Files to create:
| File | Purpose |
|---|---|
scripts/sw-ping.sh |
The ping command implementation (~67 lines, mirrors sw-hello.sh) |
scripts/sw-ping-test.sh |
6-test suite (~108 lines, mirrors sw-hello-test.sh) |
Files to modify:
| File | Change | Location |
|---|---|---|
scripts/sw |
Add ping) case before *) wildcard |
Lines 605–607 (insert before line 608) |
package.json |
Append && bash scripts/sw-ping-test.sh to "test" script |
Line 39 |
Dependencies: None. No new packages, no new libraries.
Risk areas:
-
Router insertion point —
scripts/swis 617 lines. The*)wildcard is at line 608. A mis-placed insertion (e.g., inside a nestedcaseblock like theversionsub-case at lines 591–601) would silently mis-routeping. Mitigation: insert immediately after thehello)block at line 607, matching the existing pattern exactly. -
set -euo pipefail+ arithmetic —((PASS++))returns exit code 1 when the result is 0 (i.e., on the very first increment from 0). This is not an issue here becausePASSstarts at 0 and((PASS++))only runs on the success branch of anifblock — the outerifalready exited the block. Confirmed safe insw-hello-test.sh. However, a bare((PASS++))as a standalone statement at the top level would abort underpipefailifPASSwere 0 — never do this outside a conditional. -
package.jsontest chain ordering —npm testis a single&&chain. Ifsw-ping-test.shis appended after the last existing test, a failure in any prior test will skip it; conversely, a failure insw-ping-test.shstops subsequent tests. This is acceptable and consistent with how all other tests are registered. -
Output exactness — The test asserts
output == "pong"withassert_equals. Iflib/helpers.shis on the path andinfo()or other helpers emit prefix characters beforeecho "pong", the test will fail. Mitigation:echo "pong"must be a bareecho, not wrapped in any helper function.
-
bash scripts/sw-ping-test.shexits 0 with outputPASS: 6 / FAIL: 0 -
scripts/sw-ping.sh(direct) prints exactlypongto stdout (no ANSI codes, no prefix characters) -
scripts/sw-ping.shexits 0 on no-arg invocation; exits 1 on unknown flag -
shipwright ping(via router) printspongand exits 0 — confirms router dispatch is wired correctly -
shipwright ping --helpprints text containingUSAGEand exits 0 -
shipwright ping --versionprints a semver string matching/^[0-9]+\.[0-9]+\.[0-9]+/and exits 0 -
shipwright ping --bogusexits 1 and emits an error to stderr -
npm testpasses in full (no regressions in the 100+ existing test suites) -
shipwright version checkexits 0 — confirmsVERSION="3.2.4"insw-ping.shmatchespackage.json -
shellcheck scripts/sw-ping.shproduces no errors (disable SC2034 forVERSIONif needed, matchingsw-hello.shline 7)