Skip to content
Open
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
6 changes: 6 additions & 0 deletions .ai-context/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ the v1.x compatibility window; new automation should use `--debug-wrapper`.
product assessment prompt with current Base metadata, and `--output <path>`
writes the rendered Markdown; Base does not send the prompt to an AI provider.
- `basectl docs` - open the Base documentation home page on GitHub.
- `bin/base-matrix <validate|run>` - validate and run versioned local macOS
install-matrix scenarios through the fixed four-account, non-interactive
runas contract. This is maintainer validation tooling, not hosted CI.
- `tools/macos_matrix_admin.sh [--dry-run|--remove]` - preview, apply, or
remove the one-time macOS account/sudoers preparation required by the local
matrix; it is intentionally outside ordinary `basectl setup`.
- `basectl projects list` - list Base-managed projects discovered in the
workspace.
- `basectl workspace <status|check|doctor|onboarding|agent-brief|clone|pull|update|init|configure|setup|test>` -
Expand Down
2 changes: 2 additions & 0 deletions .ai-context/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ The current command surface covers:
- explicit `linux-lab` prerequisite profile for host-side Multipass checks and
setup
- Ubuntu/Debian runtime checks, diagnostics, and source-checkout validation
- local macOS install-matrix scenario validation with a guarded four-account
runas contract; real Homebrew/account mutations remain maintainer-run

Recent trust and workspace updates are part of the current implementation:

Expand Down
7 changes: 7 additions & 0 deletions .ai-context/WORKFLOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ cross-command workflows, setup/check/doctor interactions, shell profile
behavior, installation layout assumptions, or public behavior that cannot be
proven by a focused unit test.

The local macOS install matrix is a maintainer-run validation boundary, not a
hosted CI job. Review `tools/macos_matrix_admin.sh --dry-run` before applying
the one-time account and sudoers contract, then use `bin/base-matrix` to run
versioned scenarios through `sudo -n -u <matrix-account> -i`. Persisted result
logs are redacted and mode `0600`; real Homebrew upgrades and user-owned shell
state remain outside ordinary CI.

Documentation-only changes usually need `git diff --check`.
First external PRs should start from an issue labeled `good first issue`.
Contributor-facing guidance lives in `CONTRIBUTING.md`; the workflow policy and
Expand Down
25 changes: 25 additions & 0 deletions bin/base-matrix
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env bash

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"

if [[ -n "${BASE_MATRIX_PYTHON:-}" ]]; then
PYTHON_BIN="$BASE_MATRIX_PYTHON"
elif [[ -x "$HOME/.base.d/base/.venv/bin/python" ]]; then
PYTHON_BIN="$HOME/.base.d/base/.venv/bin/python"
else
PYTHON_BIN="python3"
fi

base_cli_source="${BASE_CLI_SOURCE_DIR:-}"
if [[ -z "$base_cli_source" && -f "$REPO_ROOT/../base-cli/lib/python/base_cli/__init__.py" ]]; then
base_cli_source="$REPO_ROOT/../base-cli/lib/python"
fi
if [[ -n "$base_cli_source" ]]; then
export PYTHONPATH="$base_cli_source:$REPO_ROOT/cli/python${PYTHONPATH:+:$PYTHONPATH}"
else
export PYTHONPATH="$REPO_ROOT/cli/python${PYTHONPATH:+:$PYTHONPATH}"
fi
exec "$PYTHON_BIN" -m base_matrix "$@"
112 changes: 112 additions & 0 deletions cli/bash/commands/basectl/tests/macos_matrix_admin.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env bats

load ./basectl_helpers.bash

setup() {
TEST_ROOT="$BATS_TEST_TMPDIR/matrix-admin"
mkdir -p "$TEST_ROOT/bin"
SHELLS_FILE="$TEST_ROOT/shells"
SUDOERS_FILE="$TEST_ROOT/sudoers.d/base-matrix"
STATE_FILE="$TEST_ROOT/state"
printf '/bin/zsh\n' >"$SHELLS_FILE"
}

write_stub_commands() {
cat >"$TEST_ROOT/bin/id" <<'EOF'
#!/bin/sh
if [ "$1" = "-u" ]; then
printf '0\n'
else
printf 'matrix-admin\n'
fi
EOF
cat >"$TEST_ROOT/bin/dscl" <<'EOF'
#!/bin/sh
state="${BASE_MATRIX_DSCL_STATE:?}"
if [ "$2" = "-read" ]; then
user="${3##*/}"
shell="$(awk -F= -v user="$user" '$1 == user {print $2; exit}' "$state" 2>/dev/null)"
printf 'UserShell: %s\n' "${shell:-/bin/zsh}"
exit 0
fi
if [ "$2" = "-create" ]; then
user="${3##*/}"
printf '%s=%s\n' "$user" "$5" >>"$state"
exit 0
fi
exit 2
EOF
cat >"$TEST_ROOT/bin/visudo" <<'EOF'
#!/bin/sh
exit 0
EOF
cat >"$TEST_ROOT/bin/install" <<'EOF'
#!/bin/sh
while [ "$#" -gt 0 ]; do
case "$1" in
-o|-g|-m) shift 2 ;;
*) break ;;
esac
done
source="$1"
destination="$2"
mkdir -p "$(dirname "$destination")"
cp "$source" "$destination"
EOF
chmod +x "$TEST_ROOT/bin/id" "$TEST_ROOT/bin/dscl" "$TEST_ROOT/bin/visudo" "$TEST_ROOT/bin/install"
}

run_admin() {
run env \
PATH="$TEST_ROOT/bin:$PATH" \
BASE_MATRIX_BASH_PATH=/opt/homebrew/bin/bash \
BASE_MATRIX_SHELLS_FILE="$SHELLS_FILE" \
BASE_MATRIX_SUDOERS_FILE="$SUDOERS_FILE" \
BASE_MATRIX_STATE_FILE="$STATE_FILE" \
BASE_MATRIX_OPERATOR=matrix-admin \
BASE_MATRIX_DSCL="$TEST_ROOT/bin/dscl" \
BASE_MATRIX_DSCL_STATE="$TEST_ROOT/dscl-state" \
BASE_MATRIX_VISUDO="$TEST_ROOT/bin/visudo" \
BASE_MATRIX_INSTALL="$TEST_ROOT/bin/install" \
"$BASE_REPO_ROOT/tools/macos_matrix_admin.sh" "$@"
}

@test "macOS matrix admin dry-run is non-mutating and shows blast radius" {
run env \
BASE_MATRIX_BASH_PATH=/opt/homebrew/bin/bash \
BASE_MATRIX_SHELLS_FILE="$SHELLS_FILE" \
BASE_MATRIX_SUDOERS_FILE="$SUDOERS_FILE" \
BASE_MATRIX_STATE_FILE="$STATE_FILE" \
BASE_MATRIX_OPERATOR=matrix-admin \
"$BASE_REPO_ROOT/tools/macos_matrix_admin.sh" --dry-run

[ "$status" -eq 0 ]
[[ "$output" == *"DRY-RUN: would configure matrix accounts"* ]]
[[ "$output" == *"DRY-RUN: would grant matrix-admin passwordless sudo"* ]]
[[ "$output" == *"DRY-RUN: would set base-source-bash login shell"* ]]
[ ! -e "$STATE_FILE" ]
[ ! -e "$SUDOERS_FILE" ]
[ "$(cat "$SHELLS_FILE")" = "/bin/zsh" ]
}

@test "macOS matrix admin apply is idempotent and remove restores recorded state" {
write_stub_commands

run_admin
[ "$status" -eq 0 ]
[ -f "$STATE_FILE" ]
[ -f "$SUDOERS_FILE" ]
grep -Fxq /opt/homebrew/bin/bash "$SHELLS_FILE"
grep -Fxq 'matrix-admin ALL = (base-source-bash, base-source-zsh, base-brew-bash, base-brew-zsh) NOPASSWD: ALL' "$SUDOERS_FILE"

run_admin
[ "$status" -eq 0 ]
[ "$(grep -Fc /opt/homebrew/bin/bash "$SHELLS_FILE")" -eq 1 ]

run_admin --remove
[ "$status" -eq 0 ]
[ ! -e "$STATE_FILE" ]
[ ! -e "$SUDOERS_FILE" ]
[ "$(cat "$SHELLS_FILE")" = "/bin/zsh" ]
[[ "$output" == *"macOS install-matrix admin setup removed."* ]]
}
7 changes: 7 additions & 0 deletions cli/python/base_matrix/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""Local macOS install-matrix scenario runner."""

from __future__ import annotations

from .runner import main

__all__ = ["main"]
7 changes: 7 additions & 0 deletions cli/python/base_matrix/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from __future__ import annotations

from .runner import main


if __name__ == "__main__":
raise SystemExit(main())
Loading
Loading