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
83 changes: 75 additions & 8 deletions cli/bash/commands/basectl/subcommands/setup_common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -437,29 +437,95 @@ setup_base_python_package_check_message() {
fi
}

setup_project_setup_pythonpath() {
local base_pythonpath old_pythonpath

base_pythonpath="$BASE_HOME/lib/python:$BASE_HOME/cli/python"
old_pythonpath="${PYTHONPATH-}"
if [[ -n "$old_pythonpath" ]]; then
base_pythonpath="$base_pythonpath:$old_pythonpath"
fi
printf '%s\n' "$base_pythonpath"
}

setup_resolve_project_name() {
local manifest_arg project python_bin venv_dir

if [[ -n "${BASE_SETUP_PROJECT_NAME:-}" ]]; then
printf '%s\n' "$BASE_SETUP_PROJECT_NAME"
return 0
fi

venv_dir="$(setup_venv_dir)"
python_bin="$(setup_base_venv_python_bin "$venv_dir")" || fatal_error "Base virtual environment Python was not found at '$venv_dir/bin/python'."

manifest_arg="${BASE_SETUP_MANIFEST:-}"
project="$(
env BASE_HOME="$BASE_HOME" PYTHONPATH="$(setup_project_setup_pythonpath)" "$python_bin" -c '
from pathlib import Path
import sys

from base_cli.paths import discover_manifest
from base_setup.manifest import read_manifest

manifest_arg = sys.argv[1]
start_dir = Path(sys.argv[2])
if manifest_arg:
manifest_path = Path(manifest_arg).expanduser().resolve()
else:
manifest_path = discover_manifest(start_dir)

print(read_manifest(manifest_path).project_name if manifest_path else "base")
' "$manifest_arg" "$PWD"
)" || fatal_error "Unable to resolve Base project name from manifest."

printf '%s\n' "$project"
}

setup_validate_dev_project() {
local project

if ! setup_dev_dependencies_enabled; then
return 0
fi

if setup_is_dry_run && ! setup_base_python_package_installed "$(setup_pyyaml_package)"; then
log_info "[DRY-RUN] Would validate that --dev is only used for the Base project after PyYAML is installed."
return 0
fi

project="$(setup_resolve_project_name)"
if [[ "$project" != base ]]; then
print_error "--dev is only supported for the Base project. Run without --dev for project '$project'."
return 1
fi
}

setup_run_project_artifact_setup() {
local exit_code project wrapper
local exit_code project python_bin venv_dir
local args=()

if setup_is_dry_run && ! setup_base_python_package_installed "$(setup_pyyaml_package)"; then
log_info "[DRY-RUN] Would run Python project setup layer after PyYAML is installed."
return 0
fi

project="${BASE_SETUP_PROJECT_NAME:-base}"
wrapper="$BASE_HOME/bin/base-wrapper"
[[ -x "$wrapper" ]] || fatal_error "Base Python wrapper '$wrapper' is missing or is not executable."
venv_dir="$(setup_venv_dir)"
python_bin="$(setup_base_venv_python_bin "$venv_dir")" || fatal_error "Base virtual environment Python was not found at '$venv_dir/bin/python'."
project="$(setup_resolve_project_name)"

if setup_is_dry_run; then
args+=(--dry-run)
fi
if [[ -n "${BASE_SETUP_MANIFEST:-}" ]]; then
args+=(--manifest "$BASE_SETUP_MANIFEST")
elif [[ "$project" == base ]]; then
args+=(--manifest "$BASE_HOME/base_manifest.yaml")
fi
args+=("$project")

log_info "Running Python project setup layer."
"$wrapper" --project "$project" base_setup "${args[@]}"
env BASE_HOME="$BASE_HOME" BASE_PROJECT="$project" PYTHONPATH="$(setup_project_setup_pythonpath)" "$python_bin" -m base_setup "${args[@]}"
exit_code=$?

exit_if_error "$exit_code" "Python project setup layer failed."
Expand Down Expand Up @@ -674,13 +740,14 @@ setup_run_install() {
setup_install_homebrew
setup_install_xcode_tools
setup_install_python
setup_create_virtualenv
setup_install_pyyaml
setup_install_click
setup_validate_dev_project || return 1
if setup_dev_dependencies_enabled; then
setup_install_bats
setup_install_gh
fi
setup_create_virtualenv
setup_install_pyyaml
setup_install_click
setup_run_project_artifact_setup

if setup_is_dry_run; then
Expand Down
95 changes: 92 additions & 3 deletions cli/bash/commands/basectl/tests/setup.bats
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,16 @@ if [[ "${1:-}" == "-m" && "${2:-}" == "venv" && -n "${3:-}" ]]; then
pyyaml_package="${BASE_SETUP_PYYAML_PACKAGE:-PyYAML}"
click_package="${BASE_SETUP_CLICK_PACKAGE:-click}"
if [[ "${1:-}" == "-m" && "${2:-}" == "base_setup" ]]; then
shift 2
printf '%s\n' "$@" > "${BASE_SETUP_TEST_STATE_DIR:?}/project-setup-args"
printf '%s\n' "${BASE_PROJECT:-}" > "${BASE_SETUP_TEST_STATE_DIR:?}/project-setup-project"
touch "${BASE_SETUP_TEST_STATE_DIR:?}/project-setup-ran"
exit 0
fi
if [[ "${1:-}" == "-c" ]]; then
printf 'base\n'
exit 0
fi
if [[ "${1:-}" == "-m" && "${2:-}" == "pip" && "${3:-}" == "show" && "${4:-}" == "$pyyaml_package" ]]; then
[[ -f "${BASE_SETUP_TEST_STATE_DIR:?}/pyyaml-installed" ]]
exit $?
Expand All @@ -130,9 +137,16 @@ VENVEOF
exit 0
fi
if [[ "${1:-}" == "-m" && "${2:-}" == "base_setup" ]]; then
shift 2
printf '%s\n' "$@" > "${BASE_SETUP_TEST_STATE_DIR:?}/project-setup-args"
printf '%s\n' "${BASE_PROJECT:-}" > "${BASE_SETUP_TEST_STATE_DIR:?}/project-setup-project"
touch "${BASE_SETUP_TEST_STATE_DIR:?}/project-setup-ran"
exit 0
fi
if [[ "${1:-}" == "-c" ]]; then
printf 'base\n'
exit 0
fi
printf 'unexpected python3 args: %s\n' "$*" >&2
exit 1
PYEOF
Expand Down Expand Up @@ -218,9 +232,16 @@ if [[ "${1:-}" == "-m" && "${2:-}" == "venv" && -n "${3:-}" ]]; then
pyyaml_package="${BASE_SETUP_PYYAML_PACKAGE:-PyYAML}"
click_package="${BASE_SETUP_CLICK_PACKAGE:-click}"
if [[ "${1:-}" == "-m" && "${2:-}" == "base_setup" ]]; then
shift 2
printf '%s\n' "$@" > "${BASE_SETUP_TEST_STATE_DIR:?}/project-setup-args"
printf '%s\n' "${BASE_PROJECT:-}" > "${BASE_SETUP_TEST_STATE_DIR:?}/project-setup-project"
touch "${BASE_SETUP_TEST_STATE_DIR:?}/project-setup-ran"
exit 0
fi
if [[ "${1:-}" == "-c" ]]; then
printf 'base\n'
exit 0
fi
if [[ "${1:-}" == "-m" && "${2:-}" == "pip" && "${3:-}" == "show" && "${4:-}" == "$pyyaml_package" ]]; then
[[ -f "${BASE_SETUP_TEST_STATE_DIR:?}/pyyaml-installed" ]]
exit $?
Expand All @@ -246,9 +267,16 @@ VENVEOF
exit 0
fi
if [[ "${1:-}" == "-m" && "${2:-}" == "base_setup" ]]; then
shift 2
printf '%s\n' "$@" > "${BASE_SETUP_TEST_STATE_DIR:?}/project-setup-args"
printf '%s\n' "${BASE_PROJECT:-}" > "${BASE_SETUP_TEST_STATE_DIR:?}/project-setup-project"
touch "${BASE_SETUP_TEST_STATE_DIR:?}/project-setup-ran"
exit 0
fi
if [[ "${1:-}" == "-c" ]]; then
printf 'base\n'
exit 0
fi
printf 'unexpected python3 args: %s\n' "$*" >&2
exit 1
PYEOF
Expand Down Expand Up @@ -360,6 +388,15 @@ if [[ "${1:-}" == "-m" && "${2:-}" == "base_setup" ]]; then
touch "$BASE_SETUP_TEST_STATE_DIR/project-setup-ran"
exit "$(cat "$BASE_SETUP_TEST_STATE_DIR/project-setup-exit-code")"
fi
if [[ "${1:-}" == "-c" ]]; then
manifest_path="${3:-}"
if [[ -n "$manifest_path" && -f "$manifest_path" ]]; then
awk '/^[[:space:]]*name:/ { print $2; exit }' "$manifest_path"
else
printf 'base\n'
fi
exit 0
fi
if [[ "${1:-}" == "-m" && "${2:-}" == "pip" && "${3:-}" == "show" && "${4:-}" == "$pyyaml_package" ]]; then
[[ -f "${BASE_SETUP_TEST_STATE_DIR:?}/pyyaml-installed" ]]
exit $?
Expand Down Expand Up @@ -503,7 +540,7 @@ EOF
[ -f "$venv_dir/pyvenv.cfg" ]
}

@test "basectl setup forwards project setup arguments through base-wrapper" {
@test "basectl setup forwards explicit 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"
local manifest_path="$TEST_TMPDIR/demo_manifest.yaml"
Expand All @@ -515,8 +552,7 @@ EOF
touch "$TEST_STATE_DIR/python-installed"
touch "$TEST_STATE_DIR/pyyaml-installed"
touch "$TEST_STATE_DIR/click-installed"
create_base_venv_stub "$base_venv_dir"
create_project_setup_venv_stub "$demo_venv_dir"
create_project_setup_venv_stub "$base_venv_dir"
printf 'project:\n name: demo\nartifacts: []\n' > "$manifest_path"

run_base_command setup --dry-run --manifest "$manifest_path" demo
Expand All @@ -526,6 +562,56 @@ EOF
[ -f "$TEST_STATE_DIR/project-setup-ran" ]
[ "$(cat "$TEST_STATE_DIR/project-setup-project")" = "demo" ]
[ "$(cat "$TEST_STATE_DIR/project-setup-args")" = "$(printf '%s\n' --dry-run --manifest "$manifest_path" demo)" ]
[ ! -e "$demo_venv_dir/bin/python" ]
}

@test "basectl setup infers omitted project argument from the manifest" {
local base_venv_dir="$TEST_HOME/.base.d/base/.venv"
local demo_venv_dir="$TEST_HOME/.base.d/demo/.venv"
local manifest_path="$TEST_TMPDIR/demo_manifest.yaml"

create_brew_stub
create_xcode_stubs
touch "$TEST_STATE_DIR/xcode-installed"
mkdir -p "$TEST_TMPDIR/CommandLineTools"
touch "$TEST_STATE_DIR/python-installed"
touch "$TEST_STATE_DIR/pyyaml-installed"
touch "$TEST_STATE_DIR/click-installed"
create_project_setup_venv_stub "$base_venv_dir"
printf 'project:\n name: demo\nartifacts: []\n' > "$manifest_path"

run_base_command setup --dry-run --manifest "$manifest_path"

[ "$status" -eq 0 ]
[ -f "$TEST_STATE_DIR/project-setup-ran" ]
[ "$(cat "$TEST_STATE_DIR/project-setup-project")" = "demo" ]
[ "$(cat "$TEST_STATE_DIR/project-setup-args")" = "$(printf '%s\n' --dry-run --manifest "$manifest_path" demo)" ]
[ ! -e "$demo_venv_dir/bin/python" ]
}

@test "basectl setup --dev fails for non-Base projects before installing developer dependencies" {
local base_venv_dir="$TEST_HOME/.base.d/base/.venv"
local manifest_path="$TEST_TMPDIR/demo_manifest.yaml"

create_brew_stub
create_xcode_stubs
touch "$TEST_STATE_DIR/xcode-installed"
mkdir -p "$TEST_TMPDIR/CommandLineTools"
touch "$TEST_STATE_DIR/python-installed"
touch "$TEST_STATE_DIR/pyyaml-installed"
touch "$TEST_STATE_DIR/click-installed"
create_project_setup_venv_stub "$base_venv_dir"
printf 'project:\n name: demo\nartifacts: []\n' > "$manifest_path"

run_base_command setup --dev --manifest "$manifest_path"

[ "$status" -eq 1 ]
[[ "$output" == *"--dev is only supported for the Base project. Run without --dev for project 'demo'."* ]]
[[ "$output" != *"FATAL"* ]]
[[ "$output" != *"Encountered a fatal error"* ]]
[ ! -f "$TEST_STATE_DIR/bats-install-ran" ]
[ ! -f "$TEST_STATE_DIR/gh-install-ran" ]
[ ! -f "$TEST_STATE_DIR/project-setup-ran" ]
}

@test "basectl setup propagates Python project setup failure" {
Expand All @@ -549,6 +635,7 @@ EOF

@test "basectl setup --dev installs developer dependencies" {
local installer
local expected_args

create_xcode_stubs
installer="$(create_homebrew_installer_stub)"
Expand All @@ -563,6 +650,8 @@ EOF
[[ "$output" == *"Installing GitHub CLI formula 'gh' via Homebrew."* ]]
[ -f "$TEST_STATE_DIR/bats-install-ran" ]
[ -f "$TEST_STATE_DIR/gh-install-ran" ]
expected_args="$(printf '%s\n' --manifest "$BASE_REPO_ROOT/base_manifest.yaml" base)"
[ "$(cat "$TEST_STATE_DIR/project-setup-args")" = "$expected_args" ]
}

@test "basectl setup backs up an existing non-venv path before creating the Base virtual environment" {
Expand Down
Loading