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

- Added read-only workspace manifest support for `basectl workspace status`,
`check`, and `doctor` with `--manifest <path>`.
- Added read-only `basectl release check|plan|notes` commands backed by
manifest-owned release metadata.
- Added opt-in project Git `origin` reachability diagnostics with
`basectl check|doctor <project> --remote-network`.
- Added an explicit `ai` prerequisite profile for Codex CLI and Claude Code
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ Current implemented commands include:
- `basectl repo check [path]`
- `basectl repo configure [path]`
- `basectl repo agent-guidance [path]`
- `basectl release check --version <version>`
- `basectl release plan --version <version>`
- `basectl release notes --version <version>`
- `basectl activate <project>`
- `basectl test [project]`
- `basectl build <project> [target...]`
Expand Down Expand Up @@ -560,6 +563,21 @@ basectl config doctor
Base owns the meaning of `~/.base.d/config.yaml`, but users own how that file is
edited, backed up, or synced. See [docs/local-config.md](docs/local-config.md).

Inspect release readiness for a Base-managed repository with:

```bash
basectl release check --version 0.4.0
basectl release plan --version 0.4.0
basectl release notes --version 0.4.0
```

`basectl release` is read-only in this first slice. It validates the manifest
release contract, version file, changelog section, Git worktree state, GitHub
CLI authentication, and local and remote tag availability, then prints the
GitHub release target and any required Homebrew handoff declared by
`base_manifest.yaml`. It does not create tags, publish GitHub Releases, or edit
Homebrew tap repositories yet.

Use `--keep-last <count>` to retain the newest log files per CLI log directory
while pruning older logs. This retention mode applies only to `*.log` files;
temp and cache artifacts continue to use `--older-than`.
Expand Down Expand Up @@ -1147,7 +1165,7 @@ Base follows a few simple principles.
Base `0.3.0` is the current release. The implemented command surface covers
setup, checks, diagnostics, project discovery, project activation, project test
execution, mise integration, cleanup, updates, onboarding, repository baseline
creation, and GitHub workflow helpers.
creation, release readiness inspection, and GitHub workflow helpers.

For the documentation map and naming convention, see
[docs/README.md](docs/README.md). For the architecture and product direction,
Expand Down
13 changes: 13 additions & 0 deletions base_manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@ demo:
script: ./demo/demo.sh
description: Self-contained walkthrough of Base's core project workflow

release:
version_file: VERSION
changelog: CHANGELOG.md
tag_prefix: v
github:
repository: codeforester/base
release_title: "Base v{version}"
homebrew:
required: true
tap_repository: codeforester/homebrew-base
formula_path: Formula/base.rb
package: codeforester/base/base

# Optional relative path to a Homebrew Brewfile for ordinary macOS tools.
# Base runs `brew bundle --file=<path>` during setup when this is set.
# Keep Base-specific managed dependencies in `artifacts`; use Brewfile for
Expand Down
8 changes: 8 additions & 0 deletions cli/bash/commands/basectl/basectl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Commands:
Create repository baselines, agent guidance, and project installer templates.
ci <setup|check|doctor> <project> [options]
Run Base setup, checks, and diagnostics in non-interactive CI.
release <check|plan|notes> --version <version> [options]
Inspect release readiness, plan, and changelog notes without publishing.
clean [--older-than <age>] [--keep-last <count>] [options]
Remove old Base CLI runtime logs, temp files, and cache entries.
logs [options]
Expand Down Expand Up @@ -204,6 +206,11 @@ basectl_do_ci() {
base_ci_subcommand_main "$@"
}

basectl_do_release() {
basectl_source_subcommand_module release || return 1
base_release_subcommand_main "$@"
}

basectl_do_clean() {
basectl_source_subcommand_module clean || return 1
base_clean_subcommand_main "$@"
Expand Down Expand Up @@ -358,6 +365,7 @@ basectl_main() {
run) basectl_do_run "$@" ;;
repo) basectl_do_repo "$@" ;;
ci) basectl_do_ci "$@" ;;
release) basectl_do_release "$@" ;;
clean) basectl_do_clean "$@" ;;
logs) basectl_do_logs "$@" ;;
config) basectl_do_config "$@" ;;
Expand Down
42 changes: 42 additions & 0 deletions cli/bash/commands/basectl/subcommands/release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# shellcheck shell=bash
[[ -n "${_base_release_subcommand_sourced:-}" ]] && return
_base_release_subcommand_sourced=1
readonly _base_release_subcommand_sourced

base_release_subcommand_usage() {
cat <<'EOF'
Usage:
basectl release check --version <version> [options]
basectl release plan --version <version> [options]
basectl release notes --version <version> [options]

Options:
--version <version> Release version to inspect.
--manifest <path> Use a specific base_manifest.yaml path.
-h, --help Show this help text.

Inspect release readiness, plan, and changelog notes without creating tags,
publishing GitHub Releases, or editing Homebrew taps.
EOF
}

base_release_subcommand_main() {
local release_command="${1:-}"
local wrapper="$BASE_HOME/bin/base-wrapper"

case "$release_command" in
""|-h|--help|help)
base_release_subcommand_usage
return 0
;;
check|plan|notes)
;;
*)
base_release_subcommand_usage >&2
fatal_error "Unknown release command '$release_command'."
;;
esac

[[ -x "$wrapper" ]] || fatal_error "Base Python wrapper '$wrapper' is missing or is not executable."
"$wrapper" --project base base_release "$@"
}
2 changes: 2 additions & 0 deletions cli/bash/commands/basectl/tests/help.bats
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ load ./basectl_helpers.bash
[[ "$output" == *"run <project> <command> [options]"* ]]
[[ "$output" == *"repo <init|check|configure|agent-guidance|installer-template> [options]"* ]]
[[ "$output" == *"ci <setup|check|doctor> <project> [options]"* ]]
[[ "$output" == *"release <check|plan|notes> --version <version> [options]"* ]]
[[ "$output" == *"clean [--older-than <age>] [--keep-last <count>] [options]"* ]]
[[ "$output" == *"logs [options]"* ]]
[[ "$output" == *"config <path|show|doctor>"* ]]
Expand Down Expand Up @@ -52,6 +53,7 @@ load ./basectl_helpers.bash
grep -Fqx ' run <project> <command> [options]' <<<"$output"
grep -Fqx ' repo <init|check|configure|agent-guidance|installer-template> [options]' <<<"$output"
grep -Fqx ' ci <setup|check|doctor> <project> [options]' <<<"$output"
grep -Fqx ' release <check|plan|notes> --version <version> [options]' <<<"$output"
grep -Fqx ' logs [options]' <<<"$output"
grep -Fqx ' workspace <status|check|doctor> [options]' <<<"$output"
[[ "$output" != *"-b DIR"* ]]
Expand Down
43 changes: 43 additions & 0 deletions cli/bash/commands/basectl/tests/release.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bats

load ./basectl_helpers.bash


@test "basectl release prints help without requiring the Base Python venv" {
run_basectl release --help

[ "$status" -eq 0 ]
[[ "$output" == *"Usage:"* ]]
[[ "$output" == *"basectl release check --version <version>"* ]]
[[ "$output" == *"basectl release plan --version <version>"* ]]
[[ "$output" == *"basectl release notes --version <version>"* ]]
}

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

mkdir -p "$(dirname "$python_bin")"
printf 'project:\n name: demo\nartifacts: []\n' > "$manifest"
cat > "$python_bin" <<'EOF'
#!/usr/bin/env bash
if [[ "${1:-}" == "-m" && "${2:-}" == "base_release" ]]; then
printf 'BASE_PROJECT=%s\n' "$BASE_PROJECT" > "${BASE_TEST_RELEASE_STATE:?}"
printf 'ARGS=%s\n' "${*:3}"
exit 0
fi
printf 'unexpected release python args: %s\n' "$*" >&2
exit 1
EOF
chmod +x "$python_bin"

run env \
HOME="$TEST_HOME" \
PATH="/usr/bin:/bin:/usr/sbin:/sbin" \
BASE_TEST_RELEASE_STATE="$TEST_TMPDIR/release-state" \
"$BASE_REPO_ROOT/bin/basectl" release plan --version 1.2.3 --manifest "$manifest"

[ "$status" -eq 0 ]
[ "$output" = "ARGS=plan --version 1.2.3 --manifest $manifest" ]
[ "$(cat "$TEST_TMPDIR/release-state")" = "BASE_PROJECT=base" ]
}
1 change: 1 addition & 0 deletions cli/python/base_release/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Read-only release assistant for Base-managed projects."""
5 changes: 5 additions & 0 deletions cli/python/base_release/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .engine import main


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