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
14 changes: 9 additions & 5 deletions .ai-context/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,19 @@ the canonical current command list.
Zip bundle for manual upload or copy/paste into AI tools.
- `basectl projects list` - list Base-managed projects discovered in the
workspace.
- `basectl workspace <status|check|doctor|clone|pull>` - inspect workspace
status, checks, and diagnostics; explicitly clone expected repositories from
a manifest; or explicitly sync a local manifest from a configured canonical
source.
- `basectl workspace <status|check|doctor|clone|pull|configure>` - inspect
workspace status, checks, and diagnostics; explicitly clone expected
repositories from a manifest; explicitly sync a local manifest from a
configured canonical source; or apply repo configuration across a workspace.
- `workspace status`, `workspace check`, and `workspace doctor` support
`--format json`; `workspace clone` and `workspace pull` use text output.
`--format json`; `workspace clone`, `workspace pull`, and
`workspace configure` use text output.
- `workspace clone` mutates repository checkouts only when invoked directly;
`workspace pull` mutates only the local workspace manifest after validating
the source.
- `workspace configure --dry-run` previews delegated `repo configure` calls;
without `--dry-run`, it skips missing or non-Base-managed repos, continues
after per-repo failures, and reports configured/skipped/failed counts.
- `basectl repo <init|clone|check|configure|agent-guidance|installer-template>` -
create repository baselines, clone GitHub repositories into the configured
workspace, configure GitHub repository settings and default branch protection,
Expand Down
4 changes: 4 additions & 0 deletions .ai-context/WORKFLOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ Base-managed repositories should carry `.github/workflows/project-intake.yml`
as the fallback for issues created outside `basectl gh issue create`.
`basectl repo init` seeds it for new repositories, and `basectl repo configure`
creates it when missing from older repositories.
When a shared repo or Project schema repair needs to roll across a local repo
family, use `basectl workspace configure --dry-run` first, then
`basectl workspace configure`; it delegates to the same idempotent per-repo
`repo configure` path and reports configured, skipped, and failed repos.
If a repo Project has GitHub's default `View 1` instead of the standard Base
views, use `basectl repo configure --replace-project` with `--repo`; Base
archives the old Project and recreates it from `base-project-template`.
Expand Down
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ basectl workspace status --manifest ~/work/workspace.yaml
basectl workspace check
basectl workspace doctor
basectl workspace clone --manifest ~/work/workspace.yaml --dry-run
basectl workspace configure --dry-run
```

By default this scans `workspace.root` from `~/.base.d/config.yaml` when that
Expand All @@ -461,8 +462,8 @@ directory of `BASE_HOME`, which matches the source-checkout sibling-repo layout.
Use `--workspace <path>` to inspect a different workspace root for one command.
Project list output is tab-separated as `<project-name><TAB><path>`.
`basectl projects list` and the read-only workspace status, check, and doctor
commands support `--format json` for machine-readable output. Workspace clone
and pull use text output only. Workspace status, check, and doctor are
commands support `--format json` for machine-readable output. Workspace clone,
pull, and configure use text output only. Workspace status, check, and doctor are
read-only. Status reports each discovered project's manifest
validity, whether the Base-managed project virtual environment is present, and
the latest recorded `basectl check <project>` date when one exists. Check
Expand Down Expand Up @@ -492,6 +493,16 @@ local workspace manifest explicitly. `--source <url-or-path>` and
validates the fetched manifest before writing and never mutates project
repositories.

Use `basectl workspace configure --dry-run` to preview applying
`basectl repo configure` across Base-managed repositories in the workspace, then
run `basectl workspace configure` to apply the repair path. With
`--manifest <path>`, Base walks the expected repository set, skips missing or
non-Base-managed repositories, and continues after per-repo failures. Without a
manifest, Base scans discovered local Base-managed projects under the workspace
root. This is the fastest way to roll out shared repo or Project schema repairs
across a local repo family while keeping each repository's `repo configure`
behavior idempotent.

Start a new Base-managed repository with:

```bash
Expand Down
5 changes: 5 additions & 0 deletions cli/bash/commands/basectl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ such command directories exist. Optional utility CLIs such as `caff` and
Optional repositories are reported but skipped unless `--include-optional` is
supplied, `--dry-run` previews the delegated clone work, and explicit
`--manifest <path>` takes precedence over `workspace.manifest`.
- `basectl workspace configure` applies the existing `basectl repo configure`
repair path across discovered Base-managed projects, or across present
Base-managed repositories from a configured or explicit workspace manifest.
It supports `--dry-run`, skips missing or non-Base-managed repositories, and
continues after per-repo failures.
- `basectl version` prints the installed Base version from the repo-root `VERSION` file.
- basectl-specific bootstrap subcommands live under `cli/bash/commands/basectl/subcommands/`.
- basectl tests live under `cli/bash/commands/basectl/tests/`.
2 changes: 1 addition & 1 deletion cli/bash/commands/basectl/basectl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Commands:
Update Base from Git and run setup.
projects list [options]
List Base-managed projects discovered in the workspace.
workspace <status|check|doctor|clone|pull> [options]
workspace <status|check|doctor|clone|pull|configure> [options]
Show workspace status, run checks/diagnostics, clone manifest repos, or sync manifest.
version
Show the installed Base version.
Expand Down
35 changes: 28 additions & 7 deletions cli/bash/commands/basectl/subcommands/workspace.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ Fetch and validate a canonical workspace manifest before updating the local mani
EOF
}

base_workspace_configure_usage() {
cat <<'EOF'
Usage:
basectl workspace configure [options]

Options:
--workspace <path> Workspace directory to configure. Defaults to workspace.root, then BASE_HOME's parent.
--manifest <path> Local workspace manifest describing expected repositories.
Overrides workspace.manifest from ~/.base.d/config.yaml.
--dry-run Show planned workspace configuration without applying repo changes.
-v Enable DEBUG logging for this subcommand.
-h, --help Show this help text.

Apply or repair Base-managed GitHub repo configuration across workspace repositories.
EOF
}

base_workspace_subcommand_usage() {
case "${1:-}" in
status|check|doctor)
Expand All @@ -68,17 +85,21 @@ base_workspace_subcommand_usage() {
pull)
base_workspace_pull_usage
;;
configure)
base_workspace_configure_usage
;;
*)
cat <<'EOF'
Usage:
basectl workspace <status|check|doctor|clone|pull> [options]
basectl workspace <status|check|doctor|clone|pull|configure> [options]

Commands:
status Show workspace status. Supports --format text|json.
check Run workspace checks. Supports --format text|json.
doctor Run workspace diagnostics. Supports --format text|json.
clone Clone or validate expected repositories from a workspace manifest.
pull Fetch and validate a canonical workspace manifest source.
status Show workspace status. Supports --format text|json.
check Run workspace checks. Supports --format text|json.
doctor Run workspace diagnostics. Supports --format text|json.
clone Clone or validate expected repositories from a workspace manifest.
pull Fetch and validate a canonical workspace manifest source.
configure Apply repo configure across workspace repositories.

Run `basectl workspace <command> --help` for command-specific options.
EOF
Expand All @@ -102,7 +123,7 @@ base_workspace_subcommand_main() {
base_workspace_subcommand_usage
return 0
;;
status|check|doctor|clone|pull)
status|check|doctor|clone|pull|configure)
shift
;;
*)
Expand Down
24 changes: 24 additions & 0 deletions cli/bash/commands/basectl/tests/completions.bats
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ EOF
COMP_CWORD=3; \
_base_basectl_completion; \
printf "workspace_pull_options=%s\n" "${COMPREPLY[*]}"; \
COMP_WORDS=(basectl workspace configure --); \
COMP_CWORD=3; \
_base_basectl_completion; \
printf "workspace_configure_options=%s\n" "${COMPREPLY[*]}"; \
COMP_WORDS=(basectl onboard --); \
COMP_CWORD=2; \
_base_basectl_completion; \
Expand Down Expand Up @@ -215,6 +219,7 @@ EOF
[[ "$output" == *"workspace_status_options=--workspace --manifest --format"* ]]
[[ "$output" == *"workspace_clone_options=--workspace --manifest --include-optional --dry-run"* ]]
[[ "$output" == *"workspace_pull_options=--source --manifest --dry-run"* ]]
[[ "$output" == *"workspace_configure_options=--workspace --manifest --dry-run"* ]]
[[ "$output" == *"onboard_options=--profile --dry-run --yes --no-profile"* ]]
[[ "$output" == *"onboard_projects=base demo"* ]]
[[ "$output" == *"onboard_profiles=dev sre ai dev,sre dev,ai sre,ai dev,sre,ai"* ]]
Expand Down Expand Up @@ -269,3 +274,22 @@ EOF
[[ "$output" == *"--size"* ]]
[[ "$output" != *"--type"* ]]
}

@test "Bash completion includes workspace configure options" {
run env \
BASE_HOME="$BASE_REPO_ROOT" \
bash -c '\
source "$BASE_HOME/lib/shell/completions/basectl_completion.sh"; \
COMP_WORDS=(basectl workspace ""); \
COMP_CWORD=2; \
_base_basectl_completion; \
printf "commands=%s\n" "${COMPREPLY[*]}"; \
COMP_WORDS=(basectl workspace configure ""); \
COMP_CWORD=3; \
_base_basectl_completion; \
printf "options=%s\n" "${COMPREPLY[*]}"'

[ "$status" -eq 0 ]
[[ "$output" == *"commands=status check doctor clone pull configure"* ]]
[[ "$output" == *"options=--workspace --manifest --dry-run"* ]]
}
6 changes: 3 additions & 3 deletions cli/bash/commands/basectl/tests/help.bats
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ load ./basectl_helpers.bash
[[ "$output" == *"onboard [project] [options]"* ]]
[[ "$output" == *"update [options]"* ]]
[[ "$output" == *"projects list [options]"* ]]
[[ "$output" == *"workspace <status|check|doctor|clone|pull> [options]"* ]]
[[ "$output" == *"workspace <status|check|doctor|clone|pull|configure> [options]"* ]]
[[ "$output" == *"Invoking \`basectl\` with no command starts a Base runtime shell"* ]]
[[ "$output" == *"--version"* ]]
[[ "$output" == *"Wrapper options:"* ]]
Expand Down Expand Up @@ -57,7 +57,7 @@ load ./basectl_helpers.bash
grep -Fqx ' ci <setup|check|doctor> <project> [options]' <<<"$output"
grep -Fqx ' release <check|plan|notes|publish> --version <version> [options]' <<<"$output"
grep -Fqx ' logs [options]' <<<"$output"
grep -Fqx ' workspace <status|check|doctor|clone|pull> [options]' <<<"$output"
grep -Fqx ' workspace <status|check|doctor|clone|pull|configure> [options]' <<<"$output"
[[ "$output" != *"-b DIR"* ]]
[[ "$output" != *"Force install"* ]]
[[ "$output" != *"-V"* ]]
Expand All @@ -66,7 +66,7 @@ load ./basectl_helpers.bash
@test "AI command context includes current clone and update surfaces" {
local commands_file="$BASE_REPO_ROOT/.ai-context/COMMANDS.md"

grep -Fqx -- "- \`basectl workspace <status|check|doctor|clone|pull>\` - inspect workspace" "$commands_file"
grep -Fqx -- "- \`basectl workspace <status|check|doctor|clone|pull|configure>\` - inspect" "$commands_file"
grep -Fqx -- " - \`workspace clone\` mutates repository checkouts only when invoked directly;" "$commands_file"
grep -Fqx -- "- \`basectl repo <init|clone|check|configure|agent-guidance|installer-template>\` -" "$commands_file"
grep -Fqx -- "- \`basectl update [project]\` - update Base or a named project using the" "$commands_file"
Expand Down
37 changes: 37 additions & 0 deletions cli/bash/commands/basectl/tests/workspace.bats
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,34 @@ EOF
[ "$output" = "ARGS=--source $source --manifest $manifest --dry-run" ]
}

@test "basectl workspace configure delegates to the Python projects layer" {
local python_bin="$TEST_HOME/.base.d/base/.venv/bin/python"
local workspace="$TEST_TMPDIR/workspace"
local manifest="$TEST_TMPDIR/workspace.yaml"

mkdir -p "$(dirname "$python_bin")" "$workspace/base"
touch "$manifest"
cat > "$python_bin" <<'EOF'
#!/usr/bin/env bash
if [[ "${1:-}" == "-m" && "${2:-}" == "base_projects" && "${3:-}" == "configure" ]]; then
printf 'ARGS=%s\n' "${*:4}"
exit 0
fi
printf 'unexpected workspace configure python args: %s\n' "$*" >&2
exit 1
EOF
chmod +x "$python_bin"
workspace="$(cd "$workspace" && pwd -P)"

run env \
HOME="$TEST_HOME" \
PATH="/usr/bin:/bin:/usr/sbin:/sbin" \
"$BASE_REPO_ROOT/bin/basectl" workspace configure --workspace "$workspace" --manifest "$manifest" --dry-run

[ "$status" -eq 0 ]
[ "$output" = "ARGS=--workspace $workspace --manifest $manifest --dry-run" ]
}

@test "basectl workspace commands print help without requiring the Base Python venv" {
run_basectl workspace status --help

Expand Down Expand Up @@ -181,6 +209,15 @@ EOF
[[ "$output" == *"--manifest <path>"* ]]
[[ "$output" == *"--dry-run"* ]]
[[ "$output" != *"--format <format>"* ]]

run_basectl workspace configure --help

[ "$status" -eq 0 ]
[[ "$output" == *"basectl workspace configure [options]"* ]]
[[ "$output" == *"--workspace <path>"* ]]
[[ "$output" == *"--manifest <path>"* ]]
[[ "$output" == *"--dry-run"* ]]
[[ "$output" != *"--format <format>"* ]]
}

@test "basectl workspace rejects unknown subcommands" {
Expand Down
11 changes: 7 additions & 4 deletions cli/python/base_projects/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@

import base_cli
from base_cli.config import read_user_config
from base_cli.paths import base_cache_root
from base_cli.paths import discover_manifest
from base_cli.paths import base_cache_root, discover_manifest
from base_projects.build_targets import build_targets_project_from_args
from base_projects.build_targets import list_build_targets_from_args
from base_projects.workspace_manifest import WorkspaceManifest
from base_projects.workspace_manifest import WorkspaceManifestRepo
from base_projects.workspace_manifest import WorkspaceManifestError
from base_projects.workspace_configure import workspace_configure_from_options
from base_projects.workspace_pull import pull_workspace_manifest
from base_projects.workspace_reports import ManifestEntry
from base_projects.workspace_reports import ProjectDiscoveryError
Expand Down Expand Up @@ -155,6 +155,9 @@ def dispatch_projects_command(
command_arguments,
lambda: workspace_pull_command(ctx, options),
),
"configure": lambda: require_no_args_and_run(
"configure", command_arguments, lambda: workspace_configure_from_options(ctx, options)
),
"current": lambda: current_project_from_args(ctx, command_arguments),
"manifest": lambda: manifest_project_from_args(ctx, command_arguments),
"resolve": lambda: resolve_project_from_args(ctx, command_arguments, options.workspace),
Expand All @@ -172,8 +175,8 @@ def dispatch_projects_command(

ctx.log.error(
"Unknown projects command '%s'. Supported commands: list, current, manifest, resolve, "
"status, check, doctor, clone, test-command, demo-script, activation-sources, run-command, run-commands, "
"build-targets, build-target-list, pull.",
"status, check, doctor, clone, configure, test-command, demo-script, activation-sources, run-command, "
"run-commands, build-targets, build-target-list, pull.",
command,
)
return 2
Expand Down
Loading
Loading