Skip to content

Split tool pinning: keep Dockerfile tier pinned, unpin optional tools#51

Merged
BrettKinny merged 4 commits into
mainfrom
claude/pin-cli-tools-strategy-NfTpC
Apr 10, 2026
Merged

Split tool pinning: keep Dockerfile tier pinned, unpin optional tools#51
BrettKinny merged 4 commits into
mainfrom
claude/pin-cli-tools-strategy-NfTpC

Conversation

@BrettKinny
Copy link
Copy Markdown
Collaborator

The Dockerfile tier (delta, yq, xh, glow, gum, starship) stays pinned
with SHA256 checksums so docker build remains reproducible. Everything
installed by setup.sh or updated by sqrbx-update (opencode, editors,
TUIs, zellij, Go, nvm) now tracks the latest upstream release at install
time, matching the posture the npm-based AI tools already had.

This removes a chunk of maintenance churn (no more version/checksum
bumps across half the tool registry), gets users new features without
waiting for a squarebox release, and makes the README security story
match reality instead of overselling "all tools pinned".

Library changes (scripts/lib/tool-lib.sh):

  • sb_gh_latest_tag: query GitHub releases API
  • sb_latest_version: strip version_prefix from latest tag
  • sb_resolve_asset_version: for tools where asset version differs from
    tag (microsoft/edit), populate SB_ASSET_VERSION from the latest asset
  • sb_install now accepts "latest" and resolves both on demand

Runtime changes:

  • setup.sh: drop all *_VERSION vars + verify_checksum helper, call
    sb_install latest; nvm and Go also fetch latest
  • squarebox-update.sh: only verify checksums for dockerfile-group tools
    (via SB_CURRENT_TOOL), drop setup-checksums fetch, remove the now
    redundant edit_prepare_asset_version helper

Build/tooling:

  • Dockerfile: drop COPY setup-checksums.txt
  • scripts/update-versions.sh: only touches checksums.txt and Dockerfile
    ARGs for the dockerfile group
  • setup-checksums.txt: deleted

Docs:

  • README, SECURITY, CLAUDE, CONTRIBUTING rewritten to clearly describe
    the two tiers and their respective guarantees

https://claude.ai/code/session_01EGUSPwfTKFoiXfjd7LLtm2

The Dockerfile tier (delta, yq, xh, glow, gum, starship) stays pinned
with SHA256 checksums so docker build remains reproducible. Everything
installed by setup.sh or updated by sqrbx-update (opencode, editors,
TUIs, zellij, Go, nvm) now tracks the latest upstream release at install
time, matching the posture the npm-based AI tools already had.

This removes a chunk of maintenance churn (no more version/checksum
bumps across half the tool registry), gets users new features without
waiting for a squarebox release, and makes the README security story
match reality instead of overselling "all tools pinned".

Library changes (scripts/lib/tool-lib.sh):
- sb_gh_latest_tag: query GitHub releases API
- sb_latest_version: strip version_prefix from latest tag
- sb_resolve_asset_version: for tools where asset version differs from
  tag (microsoft/edit), populate SB_ASSET_VERSION from the latest asset
- sb_install now accepts "latest" and resolves both on demand

Runtime changes:
- setup.sh: drop all *_VERSION vars + verify_checksum helper, call
  sb_install <tool> latest; nvm and Go also fetch latest
- squarebox-update.sh: only verify checksums for dockerfile-group tools
  (via SB_CURRENT_TOOL), drop setup-checksums fetch, remove the now
  redundant edit_prepare_asset_version helper

Build/tooling:
- Dockerfile: drop COPY setup-checksums.txt
- scripts/update-versions.sh: only touches checksums.txt and Dockerfile
  ARGs for the dockerfile group
- setup-checksums.txt: deleted

Docs:
- README, SECURITY, CLAUDE, CONTRIBUTING rewritten to clearly describe
  the two tiers and their respective guarantees

https://claude.ai/code/session_01EGUSPwfTKFoiXfjd7LLtm2
Copilot AI review requested due to automatic review settings April 9, 2026 14:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR splits Squarebox tool installation into two trust tiers: a reproducible, checksum-verified Dockerfile tier, and an “optional tools” tier that installs the latest upstream releases at runtime (setup/update), reducing ongoing version/checksum maintenance.

Changes:

  • Removed setup-time version pinning + checksum verification for optional tools; setup.sh now installs most tools via sb_install <tool> latest (and fetches latest for Go/nvm).
  • Updated sqrbx-update to only enforce repo checksums for Dockerfile-tier tools; optional-tier updates track upstream latest without checksum gating.
  • Refactored tool-lib.sh to support latest installs (GitHub Releases API) and dynamic asset-version resolution; updated docs to describe the two-tier model.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
setup.sh Switches optional tool installs to upstream-latest; removes setup-checksums integration.
setup-checksums.txt Deleted (no longer used for optional tier).
scripts/lib/tool-lib.sh Adds latest-version resolution + asset-version resolution; sb_install accepts latest.
scripts/squarebox-update.sh Enforces checksums only for Dockerfile-tier tools; optional tier installs latest without verification.
scripts/update-versions.sh Now only updates Dockerfile-tier versions/checksums + Dockerfile ARGs.
Dockerfile Stops copying setup-checksums.txt into the image.
SECURITY.md Updates trust model to reflect new two-tier guarantees.
README.md Updates security section to describe pinned base-image tier vs latest optional tier.
CONTRIBUTING.md Updates contributor guidance for pinned vs optional tools.
CLAUDE.md Updates tooling/version update instructions to match the new tiers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/lib/tool-lib.sh Outdated
Comment on lines +145 to +177
# Some tools publish assets whose embedded version differs from the tag
# (e.g. microsoft/edit). For tools marked asset_version_from_api: true in
# tools.yaml, query the latest release, locate an asset matching this
# architecture, and extract a semver from its filename into SB_ASSET_VERSION.
sb_resolve_asset_version() {
local tool="$1"
[ "$(sb_get "$tool" asset_version_from_api)" = "true" ] || return 0
local repo body name ver
repo=$(sb_get "$tool" repo)
body=$(curl -fsSL "https://api.github.com/repos/${repo}/releases/latest" 2>/dev/null) || return 1
name=$(echo "$body" | jq -r '.assets[].name' | grep -F "${SB_ZARCH}" | head -1)
[ -z "$name" ] && { echo "Error: no ${SB_ZARCH} asset for ${repo}" >&2; return 1; }
ver=$(echo "$name" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
[ -z "$ver" ] && { echo "Error: no semver in asset name ${name}" >&2; return 1; }
SB_ASSET_VERSION="$ver"
export SB_ASSET_VERSION
}

# ── Verification hook (override before calling sb_install) ────────────

sb_verify() { :; }

# ── Install pipeline ─────────────────────────────────────────────────

sb_install() {
local tool="$1" version="$2"
local tool="$1" version="${2:-latest}"
if [ "$version" = "latest" ]; then
version=$(sb_latest_version "$tool") || return 1
fi
# Some tools (e.g. microsoft/edit) have an asset version that differs
# from the tag; sb_resolve_asset_version is a no-op unless the tool
# is marked asset_version_from_api: true in tools.yaml.
sb_resolve_asset_version "$tool" || return 1
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sb_resolve_asset_version always queries .../releases/latest and sets SB_ASSET_VERSION from the latest assets, regardless of the version passed to sb_install. This makes sb_install edit <specific-version> unreliable: if a newer edit release exists, the tag/version and asset version can diverge and the download URL will 404. Consider passing the requested version (or full tag) into sb_resolve_asset_version and querying the matching release (e.g., by tag) so explicit version installs remain correct.

Copilot uses AI. Check for mistakes.
Comment thread scripts/lib/tool-lib.sh Outdated
Comment on lines +152 to +154
local repo body name ver
repo=$(sb_get "$tool" repo)
body=$(curl -fsSL "https://api.github.com/repos/${repo}/releases/latest" 2>/dev/null) || return 1
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sb_resolve_asset_version returns failure with no diagnostic if the GitHub API request fails (e.g., 403 rate limit / transient network error) because the curl ... || return 1 path emits no message. It would be much easier to troubleshoot setup/update failures if this mirrored sb_gh_latest_tag by capturing HTTP status (and/or curl exit) and printing a specific error before returning non-zero.

Suggested change
local repo body name ver
repo=$(sb_get "$tool" repo)
body=$(curl -fsSL "https://api.github.com/repos/${repo}/releases/latest" 2>/dev/null) || return 1
local repo response body status curl_rc name ver
repo=$(sb_get "$tool" repo)
response=$(curl -sSL -w $'\n%{http_code}' "https://api.github.com/repos/${repo}/releases/latest" 2>/dev/null)
curl_rc=$?
if [ "$curl_rc" -ne 0 ]; then
echo "Error: failed to query latest release for ${repo} (curl exit ${curl_rc})" >&2
return 1
fi
status=${response##*$'\n'}
body=${response%$'\n'*}
if [ "$status" != "200" ]; then
echo "Error: GitHub API request for ${repo} failed with HTTP ${status}" >&2
return 1
fi

Copilot uses AI. Check for mistakes.
Comment thread scripts/lib/tool-lib.sh Outdated
Comment on lines +114 to +120
local response http_code body tag
response=$(curl -fsSL -w '\n%{http_code}' \
"https://api.github.com/repos/${repo}/releases/latest" 2>/dev/null) || true
http_code=$(echo "$response" | tail -1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" != "200" ]; then
echo "Error: GitHub API returned HTTP ${http_code} for ${repo}" >&2
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sb_gh_latest_tag uses curl -f ... || true and then only checks the captured %{http_code}. If curl fails before an HTTP response is produced (DNS/timeout/TLS), http_code can be empty and the resulting error (HTTP for <repo>) is not actionable. Consider capturing curl’s exit status and/or avoiding -f so you can reliably surface the HTTP code/body and provide a clearer message (including a dedicated 403/rate-limit case, which the other scripts currently handle explicitly).

Suggested change
local response http_code body tag
response=$(curl -fsSL -w '\n%{http_code}' \
"https://api.github.com/repos/${repo}/releases/latest" 2>/dev/null) || true
http_code=$(echo "$response" | tail -1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" != "200" ]; then
echo "Error: GitHub API returned HTTP ${http_code} for ${repo}" >&2
local response curl_status http_code body tag message
response=$(curl -sSL -w '\n%{http_code}' \
"https://api.github.com/repos/${repo}/releases/latest" 2>/dev/null)
curl_status=$?
http_code=$(echo "$response" | tail -1)
body=$(echo "$response" | sed '$d')
if [ "$curl_status" -ne 0 ]; then
echo "Error: failed to fetch latest release for ${repo} (curl exit ${curl_status})" >&2
return 1
fi
if [ -z "$http_code" ]; then
echo "Error: GitHub API did not return an HTTP status for ${repo}" >&2
return 1
fi
if [ "$http_code" = "403" ]; then
message=$(echo "$body" | jq -r '.message // empty' 2>/dev/null)
if [ -n "$message" ]; then
echo "Error: GitHub API returned HTTP 403 for ${repo}: ${message} (possible rate limit)" >&2
else
echo "Error: GitHub API returned HTTP 403 for ${repo} (possible rate limit)" >&2
fi
return 1
fi
if [ "$http_code" != "200" ]; then
message=$(echo "$body" | jq -r '.message // empty' 2>/dev/null)
if [ -n "$message" ]; then
echo "Error: GitHub API returned HTTP ${http_code} for ${repo}: ${message}" >&2
else
echo "Error: GitHub API returned HTTP ${http_code} for ${repo}" >&2
fi

Copilot uses AI. Check for mistakes.
claude added 3 commits April 10, 2026 02:30
Three related fixes to the GitHub API call paths in tool-lib.sh:

1. sb_resolve_asset_version now takes the version being installed and
   queries /releases/tags/<tag> for it, instead of always hitting
   /releases/latest. Previously, sb_install edit <specific-version>
   would download by the requested tag but resolve SB_ASSET_VERSION
   from whatever the latest release happened to be, so a 404 was
   possible if the latest release was newer than the requested one.
   None of our current callers hit this (setup.sh uses 'latest' and
   sqrbx-update passes the just-resolved latest version), but it's a
   latent bug in a reusable library.

2. Consolidated the GitHub API call logic into a private
   _sb_gh_api_get helper that reports curl exit codes, missing HTTP
   status, 403/rate-limit specifically, and surfaces the API's
   .message field in the error output. sb_gh_latest_tag previously
   produced "HTTP  for <repo>" when curl failed before receiving a
   response (DNS/timeout/TLS), which was not actionable.

3. sb_resolve_asset_version used to return silently on curl failure.
   It now prints a diagnostic via the shared helper.

Verified: library still loads, error path on a bogus repo now prints
a full contextual message including the API's rate-limit text.

https://claude.ai/code/session_01EGUSPwfTKFoiXfjd7LLtm2
@BrettKinny BrettKinny merged commit c3c55c5 into main Apr 10, 2026
1 check passed
@BrettKinny BrettKinny deleted the claude/pin-cli-tools-strategy-NfTpC branch April 10, 2026 04:09
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.

3 participants