diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3d8fd80..dec7026 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -64,6 +64,10 @@ jobs: if: steps.base_demo.outputs.supported == 'true' run: ../base/bin/basectl run base-demo --workspace .. hello + - name: Run base-demo Python command + if: steps.base_demo.outputs.supported == 'true' + run: ../base/bin/basectl run base-demo --workspace .. python-info + - name: Test base-demo if: steps.base_demo.outputs.supported == 'true' run: ../base/bin/basectl test base-demo --workspace .. diff --git a/README.md b/README.md index 14bec98..9659dcc 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ test ./tests/validate.sh hello ./src/hello.sh env ./src/env.sh manifest ./src/manifest.sh +python-info PYTHONPATH=lib/python python -m base_demo_cli $ basectl run base-demo hello hello from base-demo @@ -77,6 +78,8 @@ because activation sources `.base/activate.sh` into the project shell. - `.base/activate.sh` demonstrates project activation state. - `src/hello.sh`, `src/env.sh`, and `src/manifest.sh` are tiny command targets for `basectl run`. +- `lib/python/base_demo_cli` is a tiny Python command target that runs inside + the Base-managed project environment. - `demo/demo.sh` is the interactive walkthrough. - `tests/validate.sh` verifies that the repository baseline is intact. @@ -91,12 +94,13 @@ each field maps to a visible Base workflow: | `project.name` | `basectl projects list` | Gives Base the stable project name used by setup, check, doctor, run, test, activate, and demo. | | `brewfile` | `basectl setup base-demo` | Delegates ordinary Homebrew dependencies to `brew bundle`. This demo keeps the Brewfile empty on purpose. | | `activate.source` | `basectl activate base-demo` | Sources project-owned shell state into the activated project shell. | -| `commands` | `basectl run base-demo --list` | Declares named project commands such as `hello`, `env`, and `manifest`. | +| `commands` | `basectl run base-demo --list` | Declares named project commands such as `hello`, `env`, `manifest`, and `python-info`. | | `test.command` | `basectl test base-demo` | Defines the project-owned validation command. | | `demo.script` | `basectl demo base-demo` | Defines the project-owned interactive walkthrough. | | `artifacts` | `basectl setup base-demo` | Lists Base-managed artifacts. The baseline demo uses an empty list to avoid unnecessary installs. | -The demo intentionally uses shell scripts and no external runtime dependencies. +The demo intentionally uses shell scripts, one standard-library Python module, +and no external runtime dependencies. More specialized examples, such as Python, Go, Docker, or service demos, should stay small or move into separate demo repositories when they need their own setup story. diff --git a/base_manifest.yaml b/base_manifest.yaml index 49cd4cd..a942476 100644 --- a/base_manifest.yaml +++ b/base_manifest.yaml @@ -13,6 +13,7 @@ commands: hello: ./src/hello.sh env: ./src/env.sh manifest: ./src/manifest.sh + python-info: PYTHONPATH=lib/python python -m base_demo_cli test: command: ./tests/validate.sh diff --git a/demo/demo.sh b/demo/demo.sh index 9e6c4a6..7e2348e 100755 --- a/demo/demo.sh +++ b/demo/demo.sh @@ -133,6 +133,7 @@ manifest_step() { run_command grep -n "hello: ./src/hello.sh" "$BASE_DEMO_ROOT/base_manifest.yaml" run_command grep -n "env: ./src/env.sh" "$BASE_DEMO_ROOT/base_manifest.yaml" run_command grep -n "manifest: ./src/manifest.sh" "$BASE_DEMO_ROOT/base_manifest.yaml" + run_command grep -n "python-info: PYTHONPATH=lib/python python -m base_demo_cli" "$BASE_DEMO_ROOT/base_manifest.yaml" run_command grep -n "command: ./tests/validate.sh" "$BASE_DEMO_ROOT/base_manifest.yaml" run_command grep -n "script: ./demo/demo.sh" "$BASE_DEMO_ROOT/base_manifest.yaml" pause @@ -177,6 +178,7 @@ command_discovery_step() { require_contains "run command list" "$output" "hello" require_contains "run command list" "$output" "env" require_contains "run command list" "$output" "manifest" + require_contains "run command list" "$output" "python-info" pause } @@ -191,7 +193,7 @@ run_step() { } inspection_step() { - local env_output manifest_output + local env_output manifest_output python_output step 8 "Inspection Commands" env_output="$(capture_command "$BASE_DEMO_BASECTL" run "$BASE_DEMO_PROJECT" --workspace "$BASE_DEMO_WORKSPACE" env)" @@ -203,6 +205,11 @@ inspection_step() { printf '%s\n' "$manifest_output" require_contains "manifest command" "$manifest_output" "base-demo manifest" require_contains "manifest command" "$manifest_output" "commands:" + + python_output="$(capture_command "$BASE_DEMO_BASECTL" run "$BASE_DEMO_PROJECT" --workspace "$BASE_DEMO_WORKSPACE" python-info)" + printf '%s\n' "$python_output" + require_contains "python command" "$python_output" "base-demo python cli" + require_contains "python command" "$python_output" "BASE_PROJECT=base-demo" pause } diff --git a/lib/python/base_demo_cli/__init__.py b/lib/python/base_demo_cli/__init__.py new file mode 100644 index 0000000..dee2d21 --- /dev/null +++ b/lib/python/base_demo_cli/__init__.py @@ -0,0 +1 @@ +"""Small Python CLI used by the Base demo project.""" diff --git a/lib/python/base_demo_cli/__main__.py b/lib/python/base_demo_cli/__main__.py new file mode 100644 index 0000000..634b13e --- /dev/null +++ b/lib/python/base_demo_cli/__main__.py @@ -0,0 +1,22 @@ +"""Entry point for the base-demo Python CLI.""" + +from __future__ import annotations + +import os + + +def _print_env(name: str) -> None: + print(f"{name}={os.environ.get(name, 'unset')}") + + +def main() -> int: + print("base-demo python cli") + _print_env("BASE_PROJECT") + _print_env("BASE_PROJECT_ROOT") + _print_env("BASE_PROJECT_MANIFEST") + _print_env("BASE_PROJECT_VENV_DIR") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/demo_test.bats b/tests/demo_test.bats index 4c21e89..4ea58dd 100755 --- a/tests/demo_test.bats +++ b/tests/demo_test.bats @@ -45,6 +45,7 @@ case "$*" in printf 'hello ./src/hello.sh\n' printf 'env ./src/env.sh\n' printf 'manifest ./src/manifest.sh\n' + printf 'python-info PYTHONPATH=lib/python python -m base_demo_cli\n' ;; run\ base-demo\ --workspace\ *\ hello) printf 'hello from base-demo\n' @@ -59,6 +60,10 @@ case "$*" in printf 'base-demo manifest\n' printf 'commands:\n' ;; + run\ base-demo\ --workspace\ *\ python-info) + printf 'base-demo python cli\n' + printf 'BASE_PROJECT=base-demo\n' + ;; test\ base-demo\ --workspace\ *) printf 'Repository baseline is present.\n' ;; @@ -90,6 +95,7 @@ EOF [[ "$output" == *"BASE_DEMO_PROJECT_KIND=reference-demo"* ]] [[ "$output" == *"hello from base-demo"* ]] [[ "$output" == *"base-demo manifest"* ]] + [[ "$output" == *"base-demo python cli"* ]] [[ "$output" == *"Repository baseline is present."* ]] [[ "$output" == *"base-demo walkthrough complete."* ]] grep -Fq "basectl projects list --workspace " "$state_file" @@ -99,6 +105,7 @@ EOF grep -Eq "^basectl run base-demo --workspace .+ hello$" "$state_file" grep -Eq "^basectl run base-demo --workspace .+ env$" "$state_file" grep -Eq "^basectl run base-demo --workspace .+ manifest$" "$state_file" + grep -Eq "^basectl run base-demo --workspace .+ python-info$" "$state_file" grep -Eq "^basectl test base-demo --workspace .+$" "$state_file" grep -Eq "^basectl demo base-demo --workspace .+ --dry-run -- --non-interactive$" "$state_file" } diff --git a/tests/validate.sh b/tests/validate.sh index 3f141bd..899b216 100755 --- a/tests/validate.sh +++ b/tests/validate.sh @@ -13,6 +13,8 @@ required_files=( src/hello.sh src/env.sh src/manifest.sh + lib/python/base_demo_cli/__init__.py + lib/python/base_demo_cli/__main__.py demo/demo.sh tests/demo_test.bats .github/workflows/tests.yml @@ -62,6 +64,11 @@ grep -Fq 'manifest: ./src/manifest.sh' base_manifest.yaml || { exit 1 } +grep -Fq 'python-info: PYTHONPATH=lib/python python -m base_demo_cli' base_manifest.yaml || { + printf 'base_manifest.yaml does not declare the python-info command.\n' >&2 + exit 1 +} + grep -Fq 'script: ./demo/demo.sh' base_manifest.yaml || { printf 'base_manifest.yaml does not declare the demo script.\n' >&2 exit 1