governance: keep repository root clean; remove marker file; implement repo detection via structure heuristic; update tests and system prompts/templates
This commit is contained in:
99
CodexHelper
Executable file
99
CodexHelper
Executable file
@@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
die() { echo "error: $*" >&2; exit 1; }
|
||||||
|
note() { echo "$*" >&2; }
|
||||||
|
|
||||||
|
find_helper_repo_root() {
|
||||||
|
# Walk upwards to find a dir that looks like the helper repo
|
||||||
|
local d="$(pwd)"
|
||||||
|
while [ "$d" != "/" ]; do
|
||||||
|
if [ -d "$d/collab" ] && [ -f "$d/prompts/global/system.md" ]; then
|
||||||
|
printf '%s' "$d"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
d="$(dirname "$d")"
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
print_help() {
|
||||||
|
cat <<EOF
|
||||||
|
CodexHelper — wrapper for codex-cli with modes and scaffolding
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
CodexHelper new-mode --name <ModeName>
|
||||||
|
CodexHelper new-project --mode <ModeName> --name <project-name> --path <dir> [--force]
|
||||||
|
CodexHelper run [--mode <ModeName>] [--prompt-file <file>] [--config <file>] [--sandbox <mode>] [--full-auto]
|
||||||
|
CodexHelper --help
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- Inside the CodexHelper repo, only 'new-mode' is allowed.
|
||||||
|
- Outside the repo, 'new-project' and 'run' are allowed.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
require_outside_repo_for() {
|
||||||
|
local subcmd="$1"
|
||||||
|
if find_helper_repo_root >/dev/null 2>&1; then
|
||||||
|
if [ "$subcmd" != "new-mode" ]; then
|
||||||
|
die "Only 'new-mode' is allowed when running inside the CodexHelper repository"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_codex() {
|
||||||
|
if [ -n "${CODEX_BIN:-}" ]; then echo "$CODEX_BIN"; return 0; fi
|
||||||
|
if command -v codex >/dev/null 2>&1; then echo codex; return 0; fi
|
||||||
|
if command -v codex-cli >/dev/null 2>&1; then echo codex-cli; return 0; fi
|
||||||
|
die "No codex binary found. Set CODEX_BIN or install 'codex'/'codex-cli' in PATH."
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_new_mode() {
|
||||||
|
local name=""
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--name) name="$2"; shift 2;;
|
||||||
|
--force) FORCE=1; shift;;
|
||||||
|
--help|-h) print_help; exit 0;;
|
||||||
|
*) die "Unknown option for new-mode: $1";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
[ -n "$name" ] || die "--name is required"
|
||||||
|
local dir="modes/$name"
|
||||||
|
if [ -e "$dir" ] && [ -z "${FORCE:-}" ]; then
|
||||||
|
die "Mode '$name' already exists. Use --force to overwrite."
|
||||||
|
fi
|
||||||
|
mkdir -p "$dir"
|
||||||
|
: >"$dir/mode.md"
|
||||||
|
: >"$dir/defaults.yaml"
|
||||||
|
note "Created $dir/mode.md and $dir/defaults.yaml"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_new_project() {
|
||||||
|
require_outside_repo_for new-project
|
||||||
|
# Implementation follows in later milestones
|
||||||
|
die "Not yet implemented: new-project (per plan)."
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_run() {
|
||||||
|
require_outside_repo_for run
|
||||||
|
# Implementation follows in later milestones
|
||||||
|
die "Not yet implemented: run (per plan)."
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
local subcmd="${1:-}"; if [ -z "$subcmd" ] || [ "$subcmd" = "--help" ] || [ "$subcmd" = "-h" ]; then
|
||||||
|
print_help; exit 0
|
||||||
|
fi
|
||||||
|
case "$subcmd" in
|
||||||
|
new-mode) shift; cmd_new_mode "$@";;
|
||||||
|
new-project) shift; cmd_new_project "$@";;
|
||||||
|
run) shift; cmd_run "$@";;
|
||||||
|
*) die "Unknown subcommand: $subcmd";;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
19
README.md
19
README.md
@@ -1,3 +1,18 @@
|
|||||||
# ChatGPTScaffolding
|
# ChatGPTScaffolding / CodexHelper
|
||||||
|
|
||||||
Scaffolding (prompts/personas/modes/rules etc) around codex-cli. Tuned for @ReachableCEO needs as a solo entrepreneur. Developed from scratch in an afternoon after three weeks of intense AI usage across multiple providers/systems.
|
Scaffolding (prompts/personas/modes/rules etc) around codex-cli. Tuned for @ReachableCEO needs as a solo entrepreneur.
|
||||||
|
|
||||||
|
CodexHelper adds:
|
||||||
|
- Modes (global/mode/project prompts)
|
||||||
|
- Project scaffolding
|
||||||
|
- Prompt composition and safe defaults
|
||||||
|
- Governance: TDD, zero technical debt, plan-first via Questions → Proposal → Plan
|
||||||
|
|
||||||
|
Status: Phase 1 in progress — `new-mode` implemented; `new-project` and `run` coming next.
|
||||||
|
|
||||||
|
Quickstart (dev)
|
||||||
|
- Run tests: `scripts/test.sh` (uses bats if available)
|
||||||
|
- Show help: `./CodexHelper --help`
|
||||||
|
- Create a demo mode (in this repo): `./CodexHelper new-mode --name Demo`
|
||||||
|
|
||||||
|
See `docs/wrapper.md` and `prompts/global/` for details and governance rules.
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
- TDD Governance: adopt test-driven development with full unit/integration tests for all features in this repo and generated projects; tests written first and required for acceptance.
|
- TDD Governance: adopt test-driven development with full unit/integration tests for all features in this repo and generated projects; tests written first and required for acceptance.
|
||||||
- Zero Technical Debt: safety first; no technical debt; production-ready at all times; no deferring tests/docs/refactors; use sub-agents as needed.
|
- Zero Technical Debt: safety first; no technical debt; production-ready at all times; no deferring tests/docs/refactors; use sub-agents as needed.
|
||||||
- Planning/Architecture Governance: plan ahead via Questions→Proposal→Plan; maintain a global architecture/module map; implement module-by-module; avoid refactors except when assumptions change and plans/docs are updated.
|
- Planning/Architecture Governance: plan ahead via Questions→Proposal→Plan; maintain a global architecture/module map; implement module-by-module; avoid refactors except when assumptions change and plans/docs are updated.
|
||||||
|
- Clean Root Governance: keep repo root minimal; organize assets under `docs/`, `templates/`, `collab/`, `prompts/`, `modes/`, `scripts/`, `meta/`.
|
||||||
- Phase 1 acceptance:
|
- Phase 1 acceptance:
|
||||||
- new-mode creates mode skeleton
|
- new-mode creates mode skeleton
|
||||||
- new-project scaffolds without overwrites
|
- new-project scaffolds without overwrites
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
- tests: unit/integration tests (bats) cover CLI flows and guardrails; TDD observed
|
- tests: unit/integration tests (bats) cover CLI flows and guardrails; TDD observed
|
||||||
- zero debt: docs/tests included with every feature; no pending TODOs/deferrals; production-ready criteria met
|
- zero debt: docs/tests included with every feature; no pending TODOs/deferrals; production-ready criteria met
|
||||||
- planning: architecture/module map documented; module implementations follow approved plan with no unplanned refactors
|
- planning: architecture/module map documented; module implementations follow approved plan with no unplanned refactors
|
||||||
|
- clean root: root remains minimal; scaffolding organizes assets under subdirectories
|
||||||
\n+## Approval — Tick All That Apply
|
\n+## Approval — Tick All That Apply
|
||||||
|
|
||||||
- Subcommands approved: `new-project`, `run`, `new-mode` [ ]
|
- Subcommands approved: `new-project`, `run`, `new-mode` [ ]
|
||||||
|
@@ -42,6 +42,7 @@ Purpose: Implement a bash wrapper (CodexHelper) around codex-cli with “modes
|
|||||||
- TDD Governance: enforce test-driven development; require unit/integration tests for all features here and in generated projects.
|
- TDD Governance: enforce test-driven development; require unit/integration tests for all features here and in generated projects.
|
||||||
- Zero Technical Debt: safety first; always production-ready; no deferring tests/docs/refactors; leverage sub-agents when needed.
|
- Zero Technical Debt: safety first; always production-ready; no deferring tests/docs/refactors; leverage sub-agents when needed.
|
||||||
- Planning/Architecture Governance: plan ahead via Questions→Proposal→Plan; keep a global architecture/module map; implement module-by-module; avoid refactors except when assumptions change and plans/docs are updated.
|
- Planning/Architecture Governance: plan ahead via Questions→Proposal→Plan; keep a global architecture/module map; implement module-by-module; avoid refactors except when assumptions change and plans/docs are updated.
|
||||||
|
- Clean Root Governance: keep repo root minimal; organize assets under `docs/`, `templates/`, `collab/`, `prompts/`, `modes/`, `scripts/`, `meta/`.
|
||||||
|
|
||||||
## Project Layout (generated)
|
## Project Layout (generated)
|
||||||
- `AGENTS.md` (from `templates/project/_shared/AGENTS.md`)
|
- `AGENTS.md` (from `templates/project/_shared/AGENTS.md`)
|
||||||
@@ -104,6 +105,7 @@ Purpose: Implement a bash wrapper (CodexHelper) around codex-cli with “modes
|
|||||||
- Governance/Propagation honored: when norms change, update `prompts/global/` and AGENTS templates; log in DevLog.
|
- Governance/Propagation honored: when norms change, update `prompts/global/` and AGENTS templates; log in DevLog.
|
||||||
- TDD honored: a test suite (bats) covers CLI flows and guardrails; tests pass.
|
- TDD honored: a test suite (bats) covers CLI flows and guardrails; tests pass.
|
||||||
- Zero Debt honored: code, tests, and docs complete; no debt items remain.
|
- Zero Debt honored: code, tests, and docs complete; no debt items remain.
|
||||||
|
- Clean Root honored: only essential files at root; scaffolding places assets under subdirectories.
|
||||||
|
|
||||||
## Open Items for Confirmation
|
## Open Items for Confirmation
|
||||||
- Template coverage: include `prompts/style.md` by default? (we’ll include as optional, empty file)
|
- Template coverage: include `prompts/style.md` by default? (we’ll include as optional, empty file)
|
||||||
|
@@ -162,3 +162,12 @@ This log is concise and structured for quick machine parsing and summarization.
|
|||||||
- Updated proposal/plan to add architecture/module map deliverable and acceptance
|
- Updated proposal/plan to add architecture/module map deliverable and acceptance
|
||||||
- next:
|
- next:
|
||||||
- Add `docs/architecture.md` early in implementation per plan
|
- Add `docs/architecture.md` early in implementation per plan
|
||||||
|
|
||||||
|
## 2025-09-17T16:38Z
|
||||||
|
- context: Root cleanliness governance and marker removal
|
||||||
|
- actions:
|
||||||
|
- Added clean-root rule to system prompts and AGENTS templates
|
||||||
|
- Removed `.codexhelper-repo`; replaced guard detection with repo-structure heuristic
|
||||||
|
- Updated CLI guardrail tests accordingly
|
||||||
|
- next:
|
||||||
|
- Keep root minimal going forward; store assets under subdirectories
|
||||||
|
21
docs/wrapper.md
Normal file
21
docs/wrapper.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# CodexHelper Wrapper — Usage and Design (Phase 1)
|
||||||
|
|
||||||
|
Overview
|
||||||
|
- CodexHelper wraps `codex`/`codex-cli` to provide modes, project scaffolding, and prompt composition.
|
||||||
|
- Governance: TDD, zero technical debt, plan-first via Questions → Proposal → Plan, production-ready always.
|
||||||
|
|
||||||
|
Status (Phase 1 in progress)
|
||||||
|
- Implemented: `new-mode` scaffolder (repo-only), CLI skeleton, guardrails.
|
||||||
|
- Pending (per plan): `new-project`, `run`, config precedence (YAML+yq).
|
||||||
|
|
||||||
|
CLI
|
||||||
|
- Help: `./CodexHelper --help`
|
||||||
|
- Repo-only: `./CodexHelper new-mode --name <ModeName> [--force]`
|
||||||
|
- Outside-repo (pending): `CodexHelper new-project --mode <ModeName> --name <project-name> --path <dir> [--force]`
|
||||||
|
- Outside-repo (pending): `CodexHelper run [--mode <ModeName>] [--prompt-file <file>] [--config <file>] [--sandbox <mode>] [--full-auto]`
|
||||||
|
|
||||||
|
Development
|
||||||
|
- Tests: run `scripts/test.sh` (uses bats if available, falls back to internal runner).
|
||||||
|
- Follow TDD: write failing tests first, make them pass, refactor.
|
||||||
|
- Keep `docs/architecture.md` and README up to date as features land.
|
||||||
|
|
@@ -13,3 +13,5 @@
|
|||||||
- Zero Technical Debt: Safety first; no technical debt; always production-ready; no deferring tests/docs/refactors; TDD by default; keep docs current.
|
- Zero Technical Debt: Safety first; no technical debt; always production-ready; no deferring tests/docs/refactors; TDD by default; keep docs current.
|
||||||
|
|
||||||
- Planning/Architecture: Plan via Questions→Proposal→Plan; maintain global architecture/module map; implement module-by-module; avoid refactors unless assumptions change and plans/docs are updated.
|
- Planning/Architecture: Plan via Questions→Proposal→Plan; maintain global architecture/module map; implement module-by-module; avoid refactors unless assumptions change and plans/docs are updated.
|
||||||
|
|
||||||
|
- Clean Roots: Keep project root minimal; use `docs/`, `templates/`, `prompts/`, `scripts/`, etc.; avoid ad-hoc root files.
|
||||||
|
@@ -71,3 +71,7 @@ Customize this AGENTS.md to fit your project specifics while preserving the one-
|
|||||||
- Plan ahead: use Questions → Proposal → Plan to align before coding.
|
- Plan ahead: use Questions → Proposal → Plan to align before coding.
|
||||||
- Maintain a project architecture/module map and document boundaries.
|
- Maintain a project architecture/module map and document boundaries.
|
||||||
- Implement module-by-module per plan; avoid refactors, except when new info requires plan/doc updates.
|
- Implement module-by-module per plan; avoid refactors, except when new info requires plan/doc updates.
|
||||||
|
|
||||||
|
## Clean Repository Roots
|
||||||
|
- Keep the project root minimal and tidy. Prefer directories over many files at root.
|
||||||
|
- Place docs, templates, prompts, and scripts under dedicated subdirectories.
|
||||||
|
0
modes/DemoMode/defaults.yaml
Normal file
0
modes/DemoMode/defaults.yaml
Normal file
0
modes/DemoMode/mode.md
Normal file
0
modes/DemoMode/mode.md
Normal file
1
nm.err
Normal file
1
nm.err
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Created modes/DemoMode/mode.md and modes/DemoMode/defaults.yaml
|
1
nm2.err
Normal file
1
nm2.err
Normal file
@@ -0,0 +1 @@
|
|||||||
|
error: Mode 'DemoMode' already exists. Use --force to overwrite.
|
1
nm3.err
Normal file
1
nm3.err
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Created modes/DemoMode/mode.md and modes/DemoMode/defaults.yaml
|
1
np.err
Normal file
1
np.err
Normal file
@@ -0,0 +1 @@
|
|||||||
|
error: Only 'new-mode' is allowed when running inside the CodexHelper repository
|
@@ -26,3 +26,6 @@
|
|||||||
- Plan before coding via Questions → Proposal → Plan.
|
- Plan before coding via Questions → Proposal → Plan.
|
||||||
- Maintain a global architecture/module map; document boundaries.
|
- Maintain a global architecture/module map; document boundaries.
|
||||||
- Implement module-by-module; avoid refactors except when assumptions change and plans/docs are updated.
|
- Implement module-by-module; avoid refactors except when assumptions change and plans/docs are updated.
|
||||||
|
|
||||||
|
-- Clean Roots --
|
||||||
|
- Keep repo root minimal; store assets under `docs/`, `templates/`, `collab/`, `prompts/`, `modes/`, `scripts/`, `meta/`. Avoid ad-hoc root files.
|
||||||
|
@@ -90,6 +90,11 @@ You are a coding agent running in the Codex CLI (terminal-based). Be precise, sa
|
|||||||
- Code must be clean, maintainable, and consistent with project style.
|
- Code must be clean, maintainable, and consistent with project style.
|
||||||
- Use multiple/sub-agents or parallelization if needed to maintain quality and speed.
|
- Use multiple/sub-agents or parallelization if needed to maintain quality and speed.
|
||||||
|
|
||||||
|
## Clean Repository Roots
|
||||||
|
- Keep the repository root minimal and tidy; avoid clutter.
|
||||||
|
- Place helper/templates/docs under dedicated directories (`docs/`, `templates/`, `collab/`, `prompts/`, `modes/`, `scripts/`, `meta/`).
|
||||||
|
- Avoid ad-hoc files at root; prefer directories or hidden dotfiles only when necessary and justified.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
- Only bypass the questions→proposal→plan cycle when the user explicitly directs you to do so (and log that exception in the dev log).
|
- Only bypass the questions→proposal→plan cycle when the user explicitly directs you to do so (and log that exception in the dev log).
|
||||||
|
|
||||||
|
1
run.err
Normal file
1
run.err
Normal file
@@ -0,0 +1 @@
|
|||||||
|
error: Only 'new-mode' is allowed when running inside the CodexHelper repository
|
33
scripts/test.sh
Executable file
33
scripts/test.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
|
||||||
|
if command -v bats >/dev/null 2>&1; then
|
||||||
|
exec bats "$ROOT_DIR/tests"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Minimal internal test runner (fallback when bats is not installed)
|
||||||
|
echo "[info] bats not found; using internal test runner" >&2
|
||||||
|
|
||||||
|
failures=0
|
||||||
|
total=0
|
||||||
|
|
||||||
|
for t in "$ROOT_DIR"/tests/*.sh; do
|
||||||
|
[ -f "$t" ] || continue
|
||||||
|
total=$((total+1))
|
||||||
|
echo "[run] $t"
|
||||||
|
if bash "$t"; then
|
||||||
|
echo "[ok] $t"
|
||||||
|
else
|
||||||
|
echo "[fail] $t" >&2
|
||||||
|
failures=$((failures+1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "[summary] total=$total failures=$failures"
|
||||||
|
if [ "$failures" -ne 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
|
@@ -46,3 +46,6 @@ This file is copied by scaffolding into new projects. Edit to suit the project w
|
|||||||
- Plan ahead via Questions → Proposal → Plan to align before coding.
|
- Plan ahead via Questions → Proposal → Plan to align before coding.
|
||||||
- Maintain an architecture/module map and clear module boundaries.
|
- Maintain an architecture/module map and clear module boundaries.
|
||||||
- Implement module-by-module; refactor only when new information requires plan/doc updates.
|
- Implement module-by-module; refactor only when new information requires plan/doc updates.
|
||||||
|
|
||||||
|
## Clean Repository Roots
|
||||||
|
- Keep the project root minimal and tidy; prefer organizing assets under subdirectories (docs, templates, prompts, scripts, etc.).
|
||||||
|
23
tests/00_cli_guardrails.sh
Normal file
23
tests/00_cli_guardrails.sh
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
. "$ROOT_DIR/tests/helpers/assert.sh"
|
||||||
|
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
|
# 1) Help prints usage and exits 0
|
||||||
|
out="$(bash ./CodexHelper --help 2>&1 || true)"
|
||||||
|
echo "$out" | grep -q "CodexHelper" || { echo "help text missing" >&2; exit 1; }
|
||||||
|
|
||||||
|
# 2) Guardrails: inside helper repo, run/new-project should be blocked
|
||||||
|
if bash ./CodexHelper run 2>run.err 1>run.out; then
|
||||||
|
echo "run should fail in helper repo" >&2; exit 1
|
||||||
|
fi
|
||||||
|
grep -q "Only 'new-mode'" run.err || { echo "missing guardrail message for run" >&2; exit 1; }
|
||||||
|
|
||||||
|
if bash ./CodexHelper new-project --mode Demo --name demo --path /tmp 2>np.err 1>np.out; then
|
||||||
|
echo "new-project should fail in helper repo" >&2; exit 1
|
||||||
|
fi
|
||||||
|
grep -q "Only 'new-mode'" np.err || { echo "missing guardrail message for new-project" >&2; exit 1; }
|
||||||
|
|
||||||
|
exit 0
|
33
tests/01_new_mode.sh
Normal file
33
tests/01_new_mode.sh
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
|
mode="DemoMode"
|
||||||
|
dir="modes/$mode"
|
||||||
|
|
||||||
|
# Clean up from prior runs
|
||||||
|
rm -rf "$dir"
|
||||||
|
|
||||||
|
# Create new mode
|
||||||
|
if ! bash ./CodexHelper new-mode --name "$mode" >nm.out 2>nm.err; then
|
||||||
|
echo "new-mode failed unexpectedly" >&2; exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -f "$dir/mode.md" ] || { echo "missing $dir/mode.md" >&2; exit 1; }
|
||||||
|
[ -f "$dir/defaults.yaml" ] || { echo "missing $dir/defaults.yaml" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Running again without --force should fail
|
||||||
|
if bash ./CodexHelper new-mode --name "$mode" >nm2.out 2>nm2.err; then
|
||||||
|
echo "new-mode should have failed on overwrite without --force" >&2; exit 1
|
||||||
|
fi
|
||||||
|
grep -qi "already exists" nm2.err || { echo "missing overwrite message" >&2; exit 1; }
|
||||||
|
|
||||||
|
# With --force should succeed
|
||||||
|
if ! bash ./CodexHelper new-mode --name "$mode" --force >nm3.out 2>nm3.err; then
|
||||||
|
echo "new-mode --force failed unexpectedly" >&2; exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
24
tests/helpers/assert.sh
Normal file
24
tests/helpers/assert.sh
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
fail() { echo "ASSERTION FAILED: $*" >&2; return 1; }
|
||||||
|
|
||||||
|
assert_eq() {
|
||||||
|
local expected="$1" actual="$2"; shift 2
|
||||||
|
if [ "$expected" != "$actual" ]; then
|
||||||
|
fail "expected='$expected' actual='$actual' $*"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_contains() {
|
||||||
|
local haystack="$1" needle="$2"; shift 2
|
||||||
|
if ! grep -Fq -- "$needle" <<<"$haystack"; then
|
||||||
|
fail "did not find '$needle' in output"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_cmd() {
|
||||||
|
local out err rc
|
||||||
|
out="$({ err=$( { "$@"; } 2>&1 1>&3 ); rc=$?; echo "$err" >&2; echo $rc >&4; } 3>&1 4>&1)"
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user