Skip to content

Estate Consolidation P0 — be auto installs the beplus CLI version beplus.estate.json pins - #6

Merged
igorlamos merged 2 commits into
mainfrom
feature/estate-consolidation-p0-foundations
Oct 7, 2026
Merged

igorlamos merged 2 commits into
mainfrom
feature/estate-consolidation-p0-foundations

Conversation

@beplus-agent-claude

Copy link
Copy Markdown
Contributor

be auto now installs exactly the beplus CLI version that a repository pins in its beplus.estate.json. When there is no pin, it fails with a clear message. Today every deploy path runs be latest or a floating major, so a CLI release can change what an estate deploys without any commit in that estate. This PR is the part of that fix that lives in be.

This PR is part of Linear initiative I-34, Estate Consolidation: One Copy of Every Helper, project P0, Foundations & Guardrails. The initiative uses one branch per project and one commit per issue, so please review commit by commit, then squash-merge.

Issue Commit What
BE-213 4aa9ea2 be auto reads the pin from beplus.estate.json

BE-213: Pin the beplus CLI per repository

Linear: https://linear.app/bepluscloud/issue/BE-213

Problem

  • Deploy paths float. App Dockerfiles and base images run be latest. deploy-buildspec.yml runs be ${BE_CLI_VERSION:-2}, and the workflows default to '2'. setup-beplus/cli defaults to latest. This is EXTRACTION-PLAN Risk 1, which is still open.
  • be auto could not read a pin. It was a @todo inherited from n. It read .n-node-version, .node-version, .nvmrc and engines.node from package.json, which hold Node.js versions, not beplus CLI ones. The help text promised .bepluscloud, .beplus-version and package.json, but be never read any of them.

What changes

be auto now works like this:

  • Where it looks. It walks up from the working directory, including the root directory, to the nearest beplus.estate.json, and reads "cli": { "version" } from it.
  • What counts as a pin. The pin must be one exact version: x.y.z, with an optional leading v and an optional pre-release or build suffix. This uses be's existing is_exact_numeric_version, so the rules match the rest of be.
  • What it installs. It installs exactly that version. It never reads the release index, so it cannot fall back to the newest release.
  • Which manifest wins. The nearest manifest always decides. If that manifest has no pin, be auto fails, even when a manifest further up has one.
  • How it reads JSON. It uses node, or jq when node is missing. If neither is installed, it fails and says so. This adds no new hard dependency.
  • Other commands. be ls-remote auto, be which auto, be run auto, be exec auto and be rm auto all resolve the same pin. get_latest_resolved_version handles auto before any index lookup.

In the tests below, the release index offers 2.12.0 as the newest release:

Nearest beplus.estate.json be auto
"cli": { "version": "2.11.0" } Installs 2.11.0
"v2.11.0" / "2.12.0-beta.1" Resolves to 2.11.0 / 2.12.0-beta.1
None in $PWD or above it Exit 1: auto found no beplus.estate.json in <PWD> or above it, so no beplus CLI version is pinned
No cli, cli: {}, version: null Exit 1: <file> pins no beplus CLI version; add "cli": { "version": "x.y.z" }
"2", "2.11", "latest", "^2.11.0", "~2.11.0", ">=2.11.0", "2.11.x", "" Exit 1: … in <file> is "2.11", not one exact version like "2.11.0"
2.11, true, {"major":2} Exit 1: … in <file> is 2.11, not a version string like "2.11.0"
Broken JSON, an empty file, {} {} Exit 1: <file> could not be read as JSON by node|jq: …
Neither node nor jq on PATH Exit 1: reading the beplus CLI pin from <file> needs node or jq, and neither is on PATH

In every failure case, nothing is installed.

Removed:

  • The sources auto inherited from n: .n-node-version, .node-version, .nvmrc and engines.node.
  • The four functions that read them: get_file_node_version, get_package_engine_version, get_nvmrc_version and get_engine_version.
  • The help line that promised .bepluscloud, .beplus-version and package.json.

Files:

  • bin/be (+94 −115).
  • test/tests/auto.bats: new, 11 tests.
  • test/tests/shared-functions.bash (+18 −15): setup_tmp_mirror now accepts several versions and still works with one.
  • CHANGELOG.md: an entry under [Unreleased]. The version is not bumped (see Release below).

Gates

Command Result
npm run lint (shellcheck -S warning bin/be bin/bump scripts/install.sh scripts/release/*.sh) Pass. Exit 0, no findings. Re-run before opening this PR: exit 0. The info-level shellcheck bin/be count is the same before and after (1 = 1).
npm run test:host (PATH="$PWD/bin:$PATH" bats test/tests) on macOS with Homebrew bash 5.3 Pass. 54/54 ok. The baseline on origin/main 546c2e6 was 43/43; the 11 new tests are in auto.bats. The network suites (lookup, install, lsr, run/which, uninstall) still pass.
bats test/tests/auto.bats test/tests/checksums.bats test/tests/ordering.bats with macOS /bin/bash 3.2.57 (the macOS CI leg's bash) and jq 1.7.1-apple Pass. 24/24 ok.
bats test/tests/auto.bats, re-run before opening this PR Pass. 11/11 ok.
Negative check: auto.bats against origin/main's bin/be Fails as intended. 0/11 ok: every new test fails on the old be.
scripts/release/verify-version.sh pr origin/main feature/estate-consolidation-p0-foundations (the CI Version job) Fails, as expected. Exit 1 with bin/be changed but the version is still 0.9.0. Run bin/bump …. The release bump is a human step; see Waiting on Igor.

On this PR, expect CI to show Test (ubuntu-latest), Test (macos-latest) and Lint green. Version stays red until the bump lands.

Deviations from the issue

  • Where be lives. The issue places be in beplus/cli_v2 as @beplus/be. In fact be is the bash script bin/be in its own repo, beplus/be, with bats tests, a shellcheck lint, and main as its base and PR target. be auto is implemented here.
  • One pin source, no fallbacks. I removed .beplus-version (promised in the old help text but never read) and the sources inherited from n, instead of keeping any of them as a fallback. beplus.estate.json cli.version is now the only pin. One pin per repository is the point of the issue.
  • What "exact semver" means. I used be's existing is_exact_numeric_version: x.y.z, an optional leading v, and an optional pre-release or build suffix. Every floating form is rejected. The rest of be already accepts a leading v.
  • "Nearest" is strict. A nearer manifest without a pin fails, even when a manifest further up has one. This is the strictest reading of "nearest", and it means a pin can never come from somewhere unexpected.
  • No version bump. The initiative's agents never bump or release. This repo's CI requires a bump whenever bin/be changes, so the Version job stays red until Igor bumps.
  • Test harness change. setup_tmp_mirror now accepts several versions, so one mirror can hold both 2.11.0 and 2.12.0. It still works with one version.

Not in this PR

BE-213 scope items 2 to 4 live in other repos:

  • the cli: { version } schema in beplus.estate.json (in beplus/cli_v2, after BE-214)
  • setup-beplus/cli and setup-beplus/cdk-package defaulting to be auto (in beplus/setup-beplus)
  • the documented bump procedure

The estates adopt the pin in each estate's first adoption issue in P3. That means adding cli.version and switching their Dockerfiles, base images and buildspecs to be auto. None of that can run until this change is released, so @beplus/be 0.10.0 must be published first.

Release

  • Releases v0.10.0: ran bin/bump minor and described it in CHANGELOG.md. Not yet done; this is Igor's step, below.
  • No release: bin/be is unchanged. Does not apply: bin/be changed.

Waiting on Igor

  1. Bump the version on this branch.
    • Run bin/bump minor && git commit -am "Bump the version to v0.10.0 + update CHANGELOG".
    • This takes the version from 0.9.0 to v0.10.0 in package.json, package-lock.json and bin/be, and moves the [Unreleased] notes under ## [v0.10.0].
    • Check it with scripts/release/verify-version.sh pr origin/main feature/estate-consolidation-p0-foundations. Expected output: Merging this PR releases @beplus/be@0.10.0 (0.9.0 → 0.10.0).
    • Push the branch, then tick the first Release box above. The Version job turns green.
  2. Review and squash-merge into main.
    • The Release workflow then publishes @beplus/be 0.10.0 to npm. It also creates the tag v0.10.0 and its GitHub Release, and mirrors the version to GitHub Packages.
    • To check: curl -s https://registry.npmjs.org/@beplus%2fbe | jq -r '."dist-tags".latest' should print 0.10.0.
    • This must happen before setup-beplus or any estate switches to be auto.

Part of initiative I-34 (Estate Consolidation: One Copy of Every Helper), project P0. One branch per project, one commit per issue: review commit by commit, then squash-merge.

beplus-agent and others added 2 commits October 6, 2026 11:26
… (BE-213)

Every deploy path floats on `be latest` today, so a CLI release changes what
an estate deploys without a commit in that estate. `be auto` was meant to
read a committed pin, but it still ran the logic inherited from `n`: it read
`.n-node-version`, `.node-version`, `.nvmrc` or `engines.node` in
`package.json` (Node.js versions, not beplus CLI ones), while the help text
promised `.bepluscloud`, `.beplus-version` and `package.json`, none of which
was ever read.

`auto` now walks up from the working directory to the nearest
`beplus.estate.json` and reads `"cli": { "version": "x.y.z" }`. The pin must
be one exact version (a leading v and a pre-release are accepted, as
everywhere in be), and `be auto` installs exactly it without reading the
release index. No manifest, a manifest without a pin, a floating pin (`2`,
`2.11`, `latest`, `^2.11.0`), a pin that is not a string or a file that is
not valid JSON exits non-zero with a message naming the file, and installs
nothing. The nearest manifest decides even when it pins nothing; one further
up never stands in for it. The manifest is read with node, which every deploy
path has, or with jq where there is no node, and be says so when it has
neither. `.beplus-version` is not kept as a second source: one pin per
repository, in the manifest, is the point.

`ls-remote`, `which`, `run`, `exec` and `rm` resolve `auto` the same way.
The help text and CHANGELOG (under Unreleased) describe it; the version is
not bumped.

test/tests/auto.bats installs from the local mirror, which `setup_tmp_mirror`
now builds for several versions, against an index whose newest release is
2.12.0 while the pin is 2.11.0. All 11 tests fail against main's be.

Co-Authored-By: Igor Lamos <igor@be.plus>
Co-Authored-By: Igor Lamos <igor@be.plus>
@igorlamos igorlamos self-assigned this Oct 7, 2026
@igorlamos
igorlamos merged commit edf80f8 into main Oct 7, 2026
4 checks passed
@igorlamos
igorlamos deleted the feature/estate-consolidation-p0-foundations branch October 7, 2026 21:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants