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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ manifest-driven through `lib/base/dev_manifest.yaml`; use `basectl setup --dev`
to install them and `basectl check --dev` or `basectl doctor --dev` to verify
them.

On macOS, `basectl setup` sends a best-effort notification when setup completes
or fails. Notifications are skipped during `--dry-run` and never change the
setup exit status. Use `basectl setup --no-notify` or
`BASE_SETUP_NOTIFY=false` to disable them.

## Quick Start

Base is currently designed to be checked out once per workspace. Until a
Expand Down
5 changes: 0 additions & 5 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,6 @@ they are merged.
- Goal: provide a guided checklist-style setup experience without making `basectl setup` itself interactive-heavy.
- Expected behavior: explain each prerequisite, confirm before performing major steps, and call existing setup/check primitives internally.

- [ ] Add macOS notification on long setup completion.
- Goal: make long-running setup friendlier.
- Expected behavior: optionally display a macOS notification when `basectl setup` completes or fails.
- Notes: keep this best-effort and non-fatal.

- [ ] Package Base as a Homebrew formula or tap.
- Goal: make Base installation feel native on macOS.
- Expected behavior: support an install path such as `brew install codeforester/base/basectl`.
Expand Down
17 changes: 17 additions & 0 deletions cli/bash/commands/basectl/subcommands/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Options:
--dev Install manifest-declared developer prerequisites.
--dry-run Log what would happen without making changes.
--manifest <path> Use a specific base_manifest.yaml path.
--no-notify Disable the default best-effort macOS completion notification.
--recreate-venv Back up and recreate the project virtual environment.
-v Enable DEBUG logging for this subcommand.
-h, --help Show this help text.
Expand All @@ -41,6 +42,7 @@ EOF
}

base_setup_subcommand_main() {
local exit_code
local project_name=""

setup_clear_run_state
Expand All @@ -67,6 +69,12 @@ base_setup_subcommand_main() {
BASE_SETUP_MANIFEST="$1"
export BASE_SETUP_MANIFEST
;;
--notify)
setup_enable_notifications
;;
--no-notify)
setup_disable_notifications
;;
--recreate-venv)
setup_enable_recreate_venv
;;
Expand All @@ -93,5 +101,14 @@ base_setup_subcommand_main() {
BASE_SETUP_PROJECT_NAME="$project_name"
export BASE_SETUP_PROJECT_NAME
log_debug "Running 'basectl setup' (DRY_RUN=$(setup_is_dry_run && printf true || printf false))."
if setup_notifications_enabled; then
trap 'setup_notify_completion "$?"' EXIT
fi
setup_run_install
exit_code=$?
if setup_notifications_enabled; then
trap - EXIT
setup_notify_completion "$exit_code"
fi
return "$exit_code"
}
34 changes: 34 additions & 0 deletions cli/bash/commands/basectl/subcommands/setup_common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,22 @@ setup_enable_recreate_venv() {
export BASE_SETUP_RECREATE_VENV=true
}

setup_enable_notifications() {
export BASE_SETUP_NOTIFY=true
}

setup_disable_notifications() {
export BASE_SETUP_NOTIFY=false
}

setup_recreate_venv_enabled() {
[[ "${BASE_SETUP_RECREATE_VENV:-false}" == true ]]
}

setup_notifications_enabled() {
[[ "${BASE_SETUP_NOTIFY:-true}" == true ]]
}

setup_virtualenv_exists() {
local venv_dir

Expand Down Expand Up @@ -139,6 +151,28 @@ setup_recovery_project_layer() {
printf "%s\n" "Review the Python error above, then rerun 'basectl setup -v' for more detail."
}

setup_notify_completion() {
local exit_code="$1"
local message title

setup_notifications_enabled || return 0
setup_is_dry_run && return 0
[[ "$OSTYPE" == darwin* ]] || return 0
command -v osascript >/dev/null 2>&1 || return 0

if ((exit_code == 0)); then
title="Base setup complete"
message="Base CLI setup completed successfully."
else
title="Base setup failed"
message="Base CLI setup failed. Check the terminal for details."
fi

osascript -e 'on run argv
display notification (item 2 of argv) with title (item 1 of argv)
end run' "$title" "$message" >/dev/null 2>&1 || true
}

setup_find_brew_bin() {
local candidate

Expand Down
83 changes: 83 additions & 0 deletions cli/bash/commands/basectl/tests/setup.bats
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ EOF
chmod +x "$TEST_MOCKBIN/xcrun"
}

create_osascript_stub() {
cat > "$TEST_MOCKBIN/osascript" <<'EOF'
#!/usr/bin/env bash
printf '%s\n' "$@" > "${BASE_SETUP_TEST_STATE_DIR:?}/osascript-args"
EOF
chmod +x "$TEST_MOCKBIN/osascript"
}

create_brew_stub() {
cat > "$TEST_MOCKBIN/brew" <<'EOF'
#!/usr/bin/env bash
Expand Down Expand Up @@ -434,6 +442,7 @@ EOF
[[ "$output" == *"Usage:"* ]]
[[ "$output" == *"basectl setup [options]"* ]]
[[ "$output" == *"--dev"* ]]
[[ "$output" == *"--no-notify"* ]]
[[ "$output" == *"--recreate-venv"* ]]
[[ "$output" == *"Prepare the local Base CLI environment on macOS."* ]]
}
Expand Down Expand Up @@ -551,6 +560,24 @@ EOF
[ -f "$venv_dir/pyvenv.cfg" ]
}

@test "basectl setup sends a best-effort success notification by default" {
local installer

create_xcode_stubs
create_osascript_stub
installer="$(create_homebrew_installer_stub)"

run_base_command \
BASE_SETUP_ALLOW_NONINTERACTIVE_XCODE_INSTALL=true \
BASE_SETUP_HOMEBREW_INSTALLER_SCRIPT="$installer" \
setup

[ "$status" -eq 0 ]
[ -f "$TEST_STATE_DIR/osascript-args" ]
[[ "$(cat "$TEST_STATE_DIR/osascript-args")" == *"Base setup complete"* ]]
[[ "$(cat "$TEST_STATE_DIR/osascript-args")" == *"Base CLI setup completed successfully."* ]]
}

@test "basectl setup forwards project setup arguments through the Base venv" {
local base_venv_dir="$TEST_HOME/.base.d/base/.venv"
local demo_venv_dir="$TEST_HOME/.base.d/demo/.venv"
Expand Down Expand Up @@ -608,6 +635,62 @@ EOF
[[ "$output" == *"Run 'xcode-select --install' in an interactive terminal, complete the installer, then rerun 'basectl setup'."* ]]
}

@test "basectl setup sends a best-effort failure notification by default" {
create_brew_stub
create_xcode_stubs
create_osascript_stub
touch "$TEST_STATE_DIR/python-installed"

run_base_command setup

[ "$status" -eq 1 ]
[ -f "$TEST_STATE_DIR/osascript-args" ]
[[ "$(cat "$TEST_STATE_DIR/osascript-args")" == *"Base setup failed"* ]]
[[ "$(cat "$TEST_STATE_DIR/osascript-args")" == *"Base CLI setup failed. Check the terminal for details."* ]]
}

@test "basectl setup skips notifications during dry-run" {
create_osascript_stub

run_base_command setup --dry-run

[ "$status" -eq 0 ]
[ ! -f "$TEST_STATE_DIR/osascript-args" ]
}

@test "basectl setup --no-notify disables setup notifications" {
local installer

create_xcode_stubs
create_osascript_stub
installer="$(create_homebrew_installer_stub)"

run_base_command \
BASE_SETUP_ALLOW_NONINTERACTIVE_XCODE_INSTALL=true \
BASE_SETUP_HOMEBREW_INSTALLER_SCRIPT="$installer" \
setup --no-notify

[ "$status" -eq 0 ]
[ ! -f "$TEST_STATE_DIR/osascript-args" ]
}

@test "BASE_SETUP_NOTIFY=false disables setup notifications" {
local installer

create_xcode_stubs
create_osascript_stub
installer="$(create_homebrew_installer_stub)"

run_base_command \
BASE_SETUP_NOTIFY=false \
BASE_SETUP_ALLOW_NONINTERACTIVE_XCODE_INSTALL=true \
BASE_SETUP_HOMEBREW_INSTALLER_SCRIPT="$installer" \
setup

[ "$status" -eq 0 ]
[ ! -f "$TEST_STATE_DIR/osascript-args" ]
}

@test "basectl setup --dev runs the Python developer prerequisite layer" {
local installer

Expand Down
Loading