From 3777b71e6043ac4ff06cc0a46797409884e37279 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah Date: Sun, 24 May 2026 18:51:10 -0700 Subject: [PATCH] Make BATS an opt-in dev dependency --- AI_CONTEXT.md | 7 +- README.md | 2 + TODO.md | 3 +- .../commands/basectl/subcommands/check.sh | 8 +- .../commands/basectl/subcommands/setup.sh | 6 +- .../basectl/subcommands/setup_common.sh | 46 ++++++---- cli/bash/commands/basectl/tests/setup.bats | 84 +++++++++++++++++-- 7 files changed, 128 insertions(+), 28 deletions(-) diff --git a/AI_CONTEXT.md b/AI_CONTEXT.md index b359698..a50689d 100644 --- a/AI_CONTEXT.md +++ b/AI_CONTEXT.md @@ -26,9 +26,10 @@ This file is the shared working memory for future AI-assisted development in thi - Artifact manifests declare `project.name` and `artifacts` with `type`, `name`, and `version`; users do not specify managers. The Python registry maps known `(type, name)` pairs to managers and errors on unknown artifacts. -- Most recent uncommitted change adds `basectl check --format json` for - automation-friendly stdout output while preserving the default human logging - contract on stderr. +- Most recent uncommitted change makes BATS a developer/test dependency: + default `basectl setup` no longer installs BATS and default `basectl check` + no longer fails when BATS is missing. Use `basectl setup --dev` and + `basectl check --dev` to install/check developer dependencies. - The previous change makes `basectl shell` reject unexpected arguments with a usage error instead of silently ignoring them. - The previous change added the supported `--version` flag to `basectl --help`. - The previous change consolidated `basectl setup` dry-run state on exported `DRY_RUN` while still clearing legacy inherited `dry_run`. diff --git a/README.md b/README.md index c1cc2e0..9949ca4 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,8 @@ with `BASE_SETUP_PYTHON_FORMULA` when a workspace needs a different formula. After this Bash bootstrap layer creates Base's own Python environment, setup installs `PyYAML` into that environment and invokes the Python project setup layer to read `base_manifest.yaml` and reconcile project artifacts. +Developer/test dependencies such as BATS are opt-in; use `basectl setup --dev` +to install them and `basectl check --dev` to verify them. ## Quick Start diff --git a/TODO.md b/TODO.md index 5dc2ade..2c4f969 100644 --- a/TODO.md +++ b/TODO.md @@ -94,11 +94,12 @@ Use this as a commit-by-commit work queue. When an item is fixed, update the che - Cover default enable, preserve, and disable flows. - Done locally; pending commit. -- [ ] Decide whether BATS is a required or dev-only dependency. +- [x] Decide whether BATS is a required or dev-only dependency. - File: `cli/bash/commands/basectl/subcommands/setup_common.sh` - Problem: setup installs BATS unconditionally and check treats missing BATS as a failure. - Expected fix: either document BATS as a first-class dependency or make it optional via `--dev` or project metadata. - Update setup/check tests and README accordingly. + - Done locally with BATS as an opt-in `--dev` dependency; pending commit. - [ ] Make `caff` PID detection more robust. - File: `cli/bash/commands/caff/caff.sh` diff --git a/cli/bash/commands/basectl/subcommands/check.sh b/cli/bash/commands/basectl/subcommands/check.sh index cc8245d..967b2e1 100644 --- a/cli/bash/commands/basectl/subcommands/check.sh +++ b/cli/bash/commands/basectl/subcommands/check.sh @@ -14,6 +14,7 @@ Usage: basectl check [options] Options: + --dev Include developer/test dependency checks such as BATS. --format Select output format. Defaults to text. -v Enable DEBUG logging for this subcommand. -h, --help Show this help text. @@ -25,8 +26,8 @@ Check does: 1. Verify Homebrew is installed. 2. Verify Xcode Command Line Tools are installed. 3. Verify Python 3.13 is installed via Homebrew. - 4. Verify BATS is installed via Homebrew. - 5. Verify ~/.base.d/base/.venv exists. + 4. Verify ~/.base.d/base/.venv exists. + 5. Verify BATS is installed via Homebrew when --dev is passed. EOF } @@ -57,6 +58,9 @@ base_check_subcommand_main() { ;; esac ;; + --dev) + setup_enable_dev_dependencies + ;; -v) setup_enable_debug_logging ;; diff --git a/cli/bash/commands/basectl/subcommands/setup.sh b/cli/bash/commands/basectl/subcommands/setup.sh index a393a6e..34f2bc7 100644 --- a/cli/bash/commands/basectl/subcommands/setup.sh +++ b/cli/bash/commands/basectl/subcommands/setup.sh @@ -14,6 +14,7 @@ Usage: basectl setup [options] [project] Options: + --dev Install developer/test dependencies such as BATS. --dry-run Log what would happen without making changes. --manifest Use a specific base_manifest.yaml path. --recreate-venv Back up and recreate the project virtual environment. @@ -27,7 +28,7 @@ Setup does: 1. Install Homebrew if needed. 2. Install Xcode Command Line Tools if needed. 3. Install Python 3.13 via Homebrew if needed. - 4. Install BATS via Homebrew if needed. + 4. Install BATS via Homebrew if --dev is passed. 5. Create ~/.base.d/base/.venv if it does not already exist. 6. Install Base Python bootstrap packages into the Base virtual environment. 7. Invoke the Python project setup layer for base_manifest.yaml artifacts. @@ -53,6 +54,9 @@ base_setup_subcommand_main() { --dry-run) setup_enable_dry_run ;; + --dev) + setup_enable_dev_dependencies + ;; --manifest) shift if [[ -z "${1:-}" ]]; then diff --git a/cli/bash/commands/basectl/subcommands/setup_common.sh b/cli/bash/commands/basectl/subcommands/setup_common.sh index f5915fe..76fb21b 100644 --- a/cli/bash/commands/basectl/subcommands/setup_common.sh +++ b/cli/bash/commands/basectl/subcommands/setup_common.sh @@ -20,7 +20,7 @@ readonly _base_setup_common_sourced setup_clear_run_state() { # Clear legacy lowercase state too so inherited environments cannot trigger # lib_std.sh dry-run behavior unless this command explicitly enables it. - unset dry_run DRY_RUN BASE_SETUP_PROJECT_NAME BASE_SETUP_MANIFEST BASE_SETUP_PYYAML_READY BASE_SETUP_RECREATE_VENV BASE_PROJECT + unset dry_run DRY_RUN BASE_SETUP_DEV BASE_SETUP_PROJECT_NAME BASE_SETUP_MANIFEST BASE_SETUP_PYYAML_READY BASE_SETUP_RECREATE_VENV BASE_PROJECT } setup_enable_dry_run() { @@ -32,6 +32,14 @@ setup_enable_debug_logging() { export LOG_DEBUG=1 } +setup_enable_dev_dependencies() { + export BASE_SETUP_DEV=true +} + +setup_dev_dependencies_enabled() { + [[ "${BASE_SETUP_DEV:-false}" == true ]] +} + setup_is_dry_run() { [[ "${DRY_RUN-}" == true ]] } @@ -457,11 +465,13 @@ setup_run_check() { missing=1 fi - if setup_bats_installed; then - log_info "BATS formula '$(setup_bats_formula)' is installed via Homebrew." - else - log_warn "BATS formula '$(setup_bats_formula)' is not installed via Homebrew." - missing=1 + if setup_dev_dependencies_enabled; then + if setup_bats_installed; then + log_info "BATS formula '$(setup_bats_formula)' is installed via Homebrew." + else + log_warn "BATS formula '$(setup_bats_formula)' is not installed via Homebrew." + missing=1 + fi fi if setup_virtualenv_exists; then @@ -543,13 +553,15 @@ setup_run_check_json() { missing=1 fi - if setup_bats_installed; then - bats_ok=true - bats_message="BATS formula '$(setup_bats_formula)' is installed via Homebrew." - else - bats_ok=false - bats_message="BATS formula '$(setup_bats_formula)' is not installed via Homebrew." - missing=1 + if setup_dev_dependencies_enabled; then + if setup_bats_installed; then + bats_ok=true + bats_message="BATS formula '$(setup_bats_formula)' is installed via Homebrew." + else + bats_ok=false + bats_message="BATS formula '$(setup_bats_formula)' is not installed via Homebrew." + missing=1 + fi fi if setup_virtualenv_exists; then @@ -566,7 +578,9 @@ setup_run_check_json() { setup_print_check_json_item "," "homebrew" "$homebrew_ok" "$homebrew_message" setup_print_check_json_item "," "xcode_command_line_tools" "$xcode_ok" "$xcode_message" setup_print_check_json_item "," "python" "$python_ok" "$python_message" - setup_print_check_json_item "," "bats" "$bats_ok" "$bats_message" + if setup_dev_dependencies_enabled; then + setup_print_check_json_item "," "bats" "$bats_ok" "$bats_message" + fi setup_print_check_json_item "" "base_virtualenv" "$venv_ok" "$venv_message" printf ' ]\n' printf '}\n' @@ -579,7 +593,9 @@ setup_run_install() { setup_install_homebrew setup_install_xcode_tools setup_install_python - setup_install_bats + if setup_dev_dependencies_enabled; then + setup_install_bats + fi setup_create_virtualenv setup_install_pyyaml setup_install_click diff --git a/cli/bash/commands/basectl/tests/setup.bats b/cli/bash/commands/basectl/tests/setup.bats index b3639b5..3049db5 100644 --- a/cli/bash/commands/basectl/tests/setup.bats +++ b/cli/bash/commands/basectl/tests/setup.bats @@ -304,6 +304,7 @@ run_base_command() { [ "$status" -eq 0 ] [[ "$output" == *"Usage:"* ]] [[ "$output" == *"basectl setup [options]"* ]] + [[ "$output" == *"--dev"* ]] [[ "$output" == *"--recreate-venv"* ]] [[ "$output" == *"Prepare the local Base CLI environment on macOS."* ]] } @@ -314,6 +315,7 @@ run_base_command() { [ "$status" -eq 0 ] [[ "$output" == *"Usage:"* ]] [[ "$output" == *"basectl check [options]"* ]] + [[ "$output" == *"--dev"* ]] [[ "$output" == *"Verify the local Base CLI environment on macOS without making changes."* ]] } @@ -376,7 +378,7 @@ EOF [[ "$output" == *"Homebrew is already installed."* ]] [[ "$output" == *"Xcode Command Line Tools are already installed."* ]] [[ "$output" == *"Python formula 'python@3.13' is already installed via Homebrew."* ]] - [[ "$output" == *"BATS formula 'bats-core' is already installed via Homebrew."* ]] + [[ "$output" != *"BATS formula 'bats-core'"* ]] [[ "$output" == *"Virtual environment already exists at '$venv_dir'."* ]] [[ "$output" == *"Python package 'PyYAML' is already installed in the Base virtual environment."* ]] [[ "$output" == *"Python package 'click' is already installed in the Base virtual environment."* ]] @@ -405,7 +407,7 @@ EOF [[ "$output" == *"Installing Xcode Command Line Tools."* ]] [[ "$output" == *"Xcode Command Line Tools installation detected."* ]] [[ "$output" == *"Installing Python formula 'python@3.13' via Homebrew."* ]] - [[ "$output" == *"Installing BATS formula 'bats-core' via Homebrew."* ]] + [[ "$output" != *"BATS formula 'bats-core'"* ]] [[ "$output" == *"Creating Python virtual environment at '$venv_dir'."* ]] [[ "$output" == *"Installing Python package 'PyYAML' in the Base virtual environment."* ]] [[ "$output" == *"Installing Python package 'click' in the Base virtual environment."* ]] @@ -413,13 +415,29 @@ EOF [[ "$output" == *"Base CLI setup is complete."* ]] [ -f "$TEST_STATE_DIR/homebrew-install-ran" ] [ -f "$TEST_STATE_DIR/python-install-ran" ] - [ -f "$TEST_STATE_DIR/bats-install-ran" ] + [ ! -f "$TEST_STATE_DIR/bats-install-ran" ] [ -f "$TEST_STATE_DIR/pyyaml-install-ran" ] [ -f "$TEST_STATE_DIR/click-install-ran" ] [ -f "$TEST_STATE_DIR/project-setup-ran" ] [ -f "$venv_dir/pyvenv.cfg" ] } +@test "basectl setup --dev installs BATS with developer dependencies" { + local installer + + create_xcode_stubs + installer="$(create_homebrew_installer_stub)" + + run_base_command \ + BASE_SETUP_ALLOW_NONINTERACTIVE_XCODE_INSTALL=true \ + BASE_SETUP_HOMEBREW_INSTALLER_SCRIPT="$installer" \ + setup --dev + + [ "$status" -eq 0 ] + [[ "$output" == *"Installing BATS formula 'bats-core' via Homebrew."* ]] + [ -f "$TEST_STATE_DIR/bats-install-ran" ] +} + @test "basectl setup backs up an existing non-venv path before creating the Base virtual environment" { local backup_path local installer @@ -520,7 +538,7 @@ EOF [[ "$output" == *"[DRY-RUN] Would install Homebrew using the official installer."* ]] [[ "$output" == *"[DRY-RUN] Would install Xcode Command Line Tools and wait for installation to complete."* ]] [[ "$output" == *"[DRY-RUN] Would install Python formula 'python@3.13' via Homebrew."* ]] - [[ "$output" == *"[DRY-RUN] Would install BATS formula 'bats-core' via Homebrew."* ]] + [[ "$output" != *"BATS formula 'bats-core'"* ]] [[ "$output" == *"[DRY-RUN] Would create Python virtual environment at '$TEST_HOME/.base.d/base/.venv'."* ]] [[ "$output" == *"[DRY-RUN] Would install Python package 'PyYAML' in the Base virtual environment."* ]] [[ "$output" == *"[DRY-RUN] Would install Python package 'click' in the Base virtual environment."* ]] @@ -529,6 +547,13 @@ EOF [ ! -e "$TEST_HOME/.base.d/base/.venv" ] } +@test "basectl setup --dev dry-run includes BATS" { + run_base_command setup --dev --dry-run + + [ "$status" -eq 0 ] + [[ "$output" == *"[DRY-RUN] Would install BATS formula 'bats-core' via Homebrew."* ]] +} + @test "basectl setup ignores inherited DRY_RUN without --dry-run" { local installer local venv_dir="$TEST_HOME/.base.d/base/.venv" @@ -569,11 +594,29 @@ EOF [[ "$output" == *"Homebrew is installed."* ]] [[ "$output" == *"Xcode Command Line Tools are installed."* ]] [[ "$output" == *"Python formula 'python@3.13' is installed via Homebrew."* ]] - [[ "$output" == *"BATS formula 'bats-core' is installed via Homebrew."* ]] + [[ "$output" != *"BATS formula 'bats-core'"* ]] [[ "$output" == *"Virtual environment exists at '$venv_dir'."* ]] [[ "$output" == *"Base CLI environment check passed."* ]] } +@test "basectl check --dev includes BATS in developer dependency checks" { + local venv_dir="$TEST_HOME/.base.d/base/.venv" + + create_brew_stub + create_xcode_stubs + touch "$TEST_STATE_DIR/xcode-installed" + mkdir -p "$TEST_TMPDIR/CommandLineTools" + touch "$TEST_STATE_DIR/python-installed" + mkdir -p "$venv_dir/bin" + printf '#!/usr/bin/env bash\n' > "$venv_dir/bin/activate" + + run_base_command check --dev + + [ "$status" -eq 1 ] + [[ "$output" == *"BATS formula 'bats-core' is not installed via Homebrew."* ]] + [[ "$output" == *"Base CLI environment check found missing requirements."* ]] +} + @test "basectl check --format json writes successful check results to stdout" { local venv_dir="$TEST_HOME/.base.d/base/.venv" @@ -600,10 +643,39 @@ EOF [ "$status" -eq 0 ] [[ "$output" == *'"ok": true'* ]] [[ "$output" == *'"name":"homebrew","ok":true'* ]] + [[ "$output" != *'"name":"bats"'* ]] [[ "$output" == *'"name":"base_virtualenv","ok":true'* ]] [ "${stderr:-}" = "" ] } +@test "basectl check --dev --format json includes BATS check results" { + local venv_dir="$TEST_HOME/.base.d/base/.venv" + + create_brew_stub + create_xcode_stubs + touch "$TEST_STATE_DIR/xcode-installed" + mkdir -p "$TEST_TMPDIR/CommandLineTools" + touch "$TEST_STATE_DIR/python-installed" + mkdir -p "$venv_dir/bin" + printf '#!/usr/bin/env bash\n' > "$venv_dir/bin/activate" + + run --separate-stderr env \ + HOME="$TEST_HOME" \ + PATH="$TEST_MOCKBIN:$TEST_BASH_BIN_DIR:/usr/bin:/bin:/usr/sbin:/sbin" \ + OSTYPE="darwin24" \ + BASE_SETUP_BREW_BIN="$TEST_MOCKBIN/brew" \ + BASE_SETUP_TEST_STATE_DIR="$TEST_STATE_DIR" \ + BASE_SETUP_TEST_MOCKBIN="$TEST_MOCKBIN" \ + BASE_SETUP_TEST_PYTHON_PREFIX="$TEST_TMPDIR/python-prefix" \ + BASE_SETUP_XCODE_COMMAND_LINE_TOOLS_DIR="$TEST_TMPDIR/CommandLineTools" \ + "$BASE_REPO_ROOT/bin/basectl" check --dev --format json + + [ "$status" -eq 1 ] + [[ "$output" == *'"ok": false'* ]] + [[ "$output" == *'"name":"bats","ok":false'* ]] + [ "${stderr:-}" = "" ] +} + @test "basectl check --format json writes failed check results to stdout" { create_xcode_stubs @@ -633,7 +705,7 @@ EOF [[ "$output" == *"Homebrew is not installed."* ]] [[ "$output" == *"Xcode Command Line Tools are not installed."* ]] [[ "$output" == *"Python formula 'python@3.13' is not installed via Homebrew."* ]] - [[ "$output" == *"BATS formula 'bats-core' is not installed via Homebrew."* ]] + [[ "$output" != *"BATS formula 'bats-core'"* ]] [[ "$output" == *"Virtual environment is missing at '$TEST_HOME/.base.d/base/.venv'."* ]] [[ "$output" == *"Base CLI environment check found missing requirements."* ]] }