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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ and Base versions are tracked in the repo-root `VERSION` file.

### Changed

- Aligned `basectl devcontainer` and `basectl devenv-report` option parsing
with the shared `arg_parse` contract while preserving their public options
and project validation.
- Relicensed Base prospectively under Apache-2.0 starting with v1.9.0 to reduce
adoption friction for companies with copyleft-averse license review. Existing
MIT and AGPL releases retain their original licenses.
Expand Down
86 changes: 37 additions & 49 deletions cli/bash/commands/basectl/subcommands/devcontainer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
_base_devcontainer_subcommand_sourced=1
readonly _base_devcontainer_subcommand_sourced

import_base_lib arg/lib_arg.sh

base_devcontainer_subcommand_usage() {
cat <<'EOF'
Usage:
Expand All @@ -30,63 +32,49 @@ base_devcontainer_usage_error() {
base_devcontainer_subcommand_main() {
local project="" wrapper resolve_output resolved_name project_root manifest_path
local output_format="text" workspace_requested=0 write=0
local args=() setup_args=()
local args=() setup_args=() arg
local -a option_specs=(
"debug|flag|-v"
"workspace|value|--workspace"
"format|value|--format"
"write|flag|--write"
)
local -a positionals=()
local -A parsed_options=()

while (($#)); do
case "$1" in
for arg in "$@"; do
case "$arg" in
-h|--help|help)
base_devcontainer_subcommand_usage
return 0
;;
-v)
args+=(--debug)
shift
;;
--workspace)
[[ -n "${2:-}" ]] || {
base_devcontainer_usage_error "Option '--workspace' requires an argument."
return $?
}
workspace_requested=1
args+=(--workspace "$2")
shift 2
;;
--workspace=*)
workspace_requested=1
args+=("$1")
shift
;;
--format)
[[ -n "${2:-}" ]] || {
base_devcontainer_usage_error "Option '--format' requires an argument."
return $?
}
output_format="$2"
shift 2
;;
--format=*)
output_format="${1#--format=}"
shift
;;
--write)
write=1
shift
;;
-*)
base_devcontainer_usage_error "Unknown devcontainer option '$1'."
return $?
;;
*)
if [[ -n "$project" ]]; then
base_devcontainer_usage_error "The 'devcontainer' command accepts exactly one project name."
return $?
fi
project="$1"
shift
;;
esac
done

if ! base_arg_parse parsed_options positionals option_specs -- "$@"; then
base_devcontainer_subcommand_usage >&2
return 2
fi

if ((${#positionals[@]} > 1)); then
base_devcontainer_usage_error "The 'devcontainer' command accepts exactly one project name."
return $?
fi
if ((${#positionals[@]} == 1)); then
project="${positionals[0]}"
fi
if [[ "${parsed_options[debug]:-}" == "1" ]]; then
args+=(--debug)
fi
if [[ -n "${parsed_options[workspace]+set}" ]]; then
workspace_requested=1
args+=(--workspace "${parsed_options[workspace]}")
fi
if [[ -n "${parsed_options[format]+set}" ]]; then
output_format="${parsed_options[format]}"
fi
write="${parsed_options[write]:-0}"

[[ "$output_format" == "text" || "$output_format" == "json" ]] || {
base_devcontainer_usage_error "Unsupported devcontainer format '$output_format'. Expected text or json."
return $?
Expand Down
80 changes: 35 additions & 45 deletions cli/bash/commands/basectl/subcommands/devenv_report.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
_base_devenv_report_subcommand_sourced=1
readonly _base_devenv_report_subcommand_sourced

import_base_lib arg/lib_arg.sh

base_devenv_report_subcommand_usage() {
cat <<'EOF'
Usage:
Expand All @@ -28,59 +30,47 @@ base_devenv_report_usage_error() {
base_devenv_report_subcommand_main() {
local project="" wrapper resolve_output resolved_name project_root manifest_path
local output_format="text" workspace_requested=0
local args=() setup_args=()
local args=() setup_args=() arg
local -a option_specs=(
"debug|flag|-v"
"workspace|value|--workspace"
"format|value|--format"
)
local -a positionals=()
local -A parsed_options=()

while (($#)); do
case "$1" in
for arg in "$@"; do
case "$arg" in
-h|--help|help)
base_devenv_report_subcommand_usage
return 0
;;
-v)
args+=(--debug)
shift
;;
--workspace)
[[ -n "${2:-}" ]] || {
base_devenv_report_usage_error "Option '--workspace' requires an argument."
return $?
}
workspace_requested=1
args+=(--workspace "$2")
shift 2
;;
--workspace=*)
workspace_requested=1
args+=("$1")
shift
;;
--format)
[[ -n "${2:-}" ]] || {
base_devenv_report_usage_error "Option '--format' requires an argument."
return $?
}
output_format="$2"
shift 2
;;
--format=*)
output_format="${1#--format=}"
shift
;;
-*)
base_devenv_report_usage_error "Unknown devenv-report option '$1'."
return $?
;;
*)
if [[ -n "$project" ]]; then
base_devenv_report_usage_error "The 'devenv-report' command accepts exactly one project name."
return $?
fi
project="$1"
shift
;;
esac
done

if ! base_arg_parse parsed_options positionals option_specs -- "$@"; then
base_devenv_report_subcommand_usage >&2
return 2
fi

if ((${#positionals[@]} > 1)); then
base_devenv_report_usage_error "The 'devenv-report' command accepts exactly one project name."
return $?
fi
if ((${#positionals[@]} == 1)); then
project="${positionals[0]}"
fi
if [[ "${parsed_options[debug]:-}" == "1" ]]; then
args+=(--debug)
fi
if [[ -n "${parsed_options[workspace]+set}" ]]; then
workspace_requested=1
args+=(--workspace "${parsed_options[workspace]}")
fi
if [[ -n "${parsed_options[format]+set}" ]]; then
output_format="${parsed_options[format]}"
fi

[[ "$output_format" == "text" || "$output_format" == "json" ]] || {
base_devenv_report_usage_error "Unsupported devenv-report format '$output_format'. Expected text or json."
return $?
Expand Down
38 changes: 37 additions & 1 deletion cli/bash/commands/basectl/tests/devcontainer.bats
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,28 @@
load ./basectl_helpers.bash


@test "basectl devcontainer parses options through reusable arg helper" {
local state_file="$TEST_TMPDIR/arg-parse-state"

run env \
HOME="$TEST_HOME" \
BASE_HOME="$BASE_REPO_ROOT" \
BASE_BASH_LIBS_DIR="${BASE_BASH_LIBS_DIR:-}" \
BASE_TEST_ARG_PARSE_STATE="$state_file" \
bash -c '
source "$BASE_HOME/base_init.sh"
source "$BASE_HOME/cli/bash/commands/basectl/subcommands/devcontainer.sh"
base_arg_parse() {
printf "%s\n" "$*" > "${BASE_TEST_ARG_PARSE_STATE:?}"
return 2
}
base_devcontainer_subcommand_main demo --format json
'

[ "$status" -eq 2 ]
[[ "$(cat "$state_file")" == "parsed_options positionals option_specs -- demo --format json" ]]
}

@test "basectl devcontainer delegates resolved manifest to base_setup export action" {
local python_bin="$TEST_HOME/.base.d/base/.venv/bin/python"
local workspace="$TEST_TMPDIR/workspace"
Expand Down Expand Up @@ -33,7 +55,7 @@ EOF
HOME="$TEST_HOME" \
PATH="/usr/bin:/bin:/usr/sbin:/sbin" \
BASE_TEST_PROJECT_ROOT="$workspace/demo" \
"$BASE_REPO_ROOT/bin/basectl" devcontainer demo --workspace "$workspace" --format json --write
"$BASE_REPO_ROOT/bin/basectl" devcontainer demo --workspace "$workspace" --format json -v --write

[ "$status" -eq 0 ]
[[ "$output" == *"BASE_PROJECT=base"* ]]
Expand All @@ -57,3 +79,17 @@ EOF
[ "$status" -eq 2 ]
[[ "$output" == *"ERROR: Option '--workspace' requires an explicit project name."* ]]
}

@test "basectl devcontainer reports parser failures as usage errors" {
run_basectl devcontainer demo --unknown

[ "$status" -eq 2 ]
[[ "$output" == *"Usage:"* ]]
}

@test "basectl devcontainer rejects extra project positionals" {
run_basectl devcontainer demo other

[ "$status" -eq 2 ]
[[ "$output" == *"ERROR: The 'devcontainer' command accepts exactly one project name."* ]]
}
38 changes: 37 additions & 1 deletion cli/bash/commands/basectl/tests/devenv-report.bats
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,28 @@
load ./basectl_helpers.bash


@test "basectl devenv-report parses options through reusable arg helper" {
local state_file="$TEST_TMPDIR/arg-parse-state"

run env \
HOME="$TEST_HOME" \
BASE_HOME="$BASE_REPO_ROOT" \
BASE_BASH_LIBS_DIR="${BASE_BASH_LIBS_DIR:-}" \
BASE_TEST_ARG_PARSE_STATE="$state_file" \
bash -c '
source "$BASE_HOME/base_init.sh"
source "$BASE_HOME/cli/bash/commands/basectl/subcommands/devenv_report.sh"
base_arg_parse() {
printf "%s\n" "$*" > "${BASE_TEST_ARG_PARSE_STATE:?}"
return 2
}
base_devenv_report_subcommand_main demo --format json
'

[ "$status" -eq 2 ]
[[ "$(cat "$state_file")" == "parsed_options positionals option_specs -- demo --format json" ]]
}

@test "basectl devenv-report delegates resolved manifest to base_setup report action" {
local python_bin="$TEST_HOME/.base.d/base/.venv/bin/python"
local workspace="$TEST_TMPDIR/workspace"
Expand Down Expand Up @@ -33,7 +55,7 @@ EOF
HOME="$TEST_HOME" \
PATH="/usr/bin:/bin:/usr/sbin:/sbin" \
BASE_TEST_PROJECT_ROOT="$workspace/demo" \
"$BASE_REPO_ROOT/bin/basectl" devenv-report demo --workspace "$workspace" --format json
"$BASE_REPO_ROOT/bin/basectl" devenv-report demo --workspace "$workspace" --format json -v

[ "$status" -eq 0 ]
[[ "$output" == *"BASE_PROJECT=base"* ]]
Expand All @@ -56,3 +78,17 @@ EOF
[ "$status" -eq 2 ]
[[ "$output" == *"ERROR: Option '--workspace' requires an explicit project name."* ]]
}

@test "basectl devenv-report reports parser failures as usage errors" {
run_basectl devenv-report demo --unknown

[ "$status" -eq 2 ]
[[ "$output" == *"Usage:"* ]]
}

@test "basectl devenv-report rejects extra project positionals" {
run_basectl devenv-report demo other

[ "$status" -eq 2 ]
[[ "$output" == *"ERROR: The 'devenv-report' command accepts exactly one project name."* ]]
}
Loading