Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions AI_CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
8 changes: 6 additions & 2 deletions cli/bash/commands/basectl/subcommands/check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Usage:
basectl check [options]
Options:
--dev Include developer/test dependency checks such as BATS.
--format <text|json> Select output format. Defaults to text.
-v Enable DEBUG logging for this subcommand.
-h, --help Show this help text.
Expand All @@ -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
}

Expand Down Expand Up @@ -57,6 +58,9 @@ base_check_subcommand_main() {
;;
esac
;;
--dev)
setup_enable_dev_dependencies
;;
-v)
setup_enable_debug_logging
;;
Expand Down
6 changes: 5 additions & 1 deletion cli/bash/commands/basectl/subcommands/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 <path> Use a specific base_manifest.yaml path.
--recreate-venv Back up and recreate the project virtual environment.
Expand All @@ -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.
Expand All @@ -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
Expand Down
46 changes: 31 additions & 15 deletions cli/bash/commands/basectl/subcommands/setup_common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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 ]]
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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'
Expand All @@ -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
Expand Down
84 changes: 78 additions & 6 deletions cli/bash/commands/basectl/tests/setup.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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."* ]]
}
Expand All @@ -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."* ]]
}

Expand Down Expand Up @@ -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."* ]]
Expand Down Expand Up @@ -405,21 +407,37 @@ 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."* ]]
[[ "$output" == *"Running Python project setup layer."* ]]
[[ "$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
Expand Down Expand Up @@ -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."* ]]
Expand All @@ -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"
Expand Down Expand Up @@ -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"

Expand All @@ -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

Expand Down Expand Up @@ -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."* ]]
}
Expand Down
Loading