fix(entrypoint): locate Claude binary by path, not command -v (Claude 2.1+ native install) #56

Merged
to merged 7 commits from dev into main 2026-05-26 17:14:21 +02:00
Owner

Summary

Patch fix for the entrypoint's Claude-install path.

Claude Code 2.1+ ships as a native binary, dropped at ~/.local/bin/claude by install.sh — but the installer no longer adds that dir to $PATH (it just prints a manual setup hint). The entrypoint's command -v claude lookup then returned empty and aborted with claude not on PATH after install — installer layout changed? even though the install had succeeded.

Fix: check the known native-install paths (/root/.local/bin/claude, $HOME/.local/bin/claude) directly before falling back to a PATH-widened command -v. The fallback preserves compatibility with older installer layouts.

Surfaced by

Fresh per-project volume + android-flutter image — the volume override feature itself worked, install succeeded ("Claude Code 2.1.150 successfully installed"), but the next entrypoint step bailed.

Test plan

  • 61/61 bats tests still pass (entrypoint logic runs inside the container, not on the bats host)
  • CI test job
  • After merge + tag-and-build: re-run scc in videoguard-app → entrypoint should find the just-installed binary at /root/.local/bin/claude, copy it to the volume, and exec successfully

Version

scc.sh.version bumped 1.4.0 → 1.4.1 (patch).

## Summary Patch fix for the entrypoint's Claude-install path. Claude Code 2.1+ ships as a native binary, dropped at `~/.local/bin/claude` by `install.sh` — but the installer no longer adds that dir to `$PATH` (it just prints a manual setup hint). The entrypoint's `command -v claude` lookup then returned empty and aborted with `claude not on PATH after install — installer layout changed?` even though the install had succeeded. Fix: check the known native-install paths (`/root/.local/bin/claude`, `$HOME/.local/bin/claude`) directly before falling back to a PATH-widened `command -v`. The fallback preserves compatibility with older installer layouts. ## Surfaced by Fresh per-project volume + android-flutter image — the volume override feature itself worked, install succeeded ("Claude Code 2.1.150 successfully installed"), but the next entrypoint step bailed. ## Test plan - 61/61 bats tests still pass (entrypoint logic runs inside the container, not on the bats host) - CI test job - After merge + tag-and-build: re-run `scc` in `videoguard-app` → entrypoint should find the just-installed binary at `/root/.local/bin/claude`, copy it to the volume, and exec successfully ## Version `scc.sh.version` bumped 1.4.0 → 1.4.1 (patch).
to added 6 commits 2026-05-26 17:07:09 +02:00
feat(launcher): daily Claude Code update check (1.1.2 → 1.2.0)
All checks were successful
build-and-push / test-1 (push) Successful in 26s
build-and-push / test (push) Successful in 0s
test / test (push) Successful in 24s
build-and-push / build (push) Successful in 3m46s
test / test (pull_request) Successful in 25s
bad4785181
Until now scc only installed Claude once, then served the cached binary
from the scc-claude volume forever — no version check, no nudge when
Anthropic shipped a fix. The only refresh path was the user knowing to
set SCC_FORCE_REINSTALL=1.

This adds a once-per-day check mirroring the existing scc.sh self-update
flow: scc.sh GETs @anthropic-ai/claude-code/latest from npm (~1 KB,
1 s timeout), compares it to the version cached in the volume, and warns
+ prompts when upstream is newer. Decisions are remembered per upstream
version so users don't get re-prompted for the same release.

Plumbing:

  - scc-entrypoint.sh writes `claude --version` into both
    /opt/scc-claude/version (canonical, lives with the binary) and
    /scc-meta/claude_version (bind-mounted from ~/.scc/volume-meta/, so
    the host launcher can read it without a docker-exec roundtrip).
    Re-captured after every reinstall; idempotent on the fast path.
  - scc.sh adds the /scc-meta mount alongside the existing volume mount.
    Older launchers don't mount /scc-meta; the entrypoint silently skips
    the mirror in that case, so a new image works with an old launcher.
  - New check_for_claude_update() in scc.sh: 24h-cached fetch, semver
    comparison via sort -V, per-version ack file at ~/.scc/claude-update-check.
    Fail-closed: non-TTY runs note and continue, never auto-update.

Three new env vars:

  - SCC_AUTO_UPDATE_CLAUDE=1       skip the prompt, auto-set
                                   SCC_FORCE_REINSTALL=1 on detection.
  - SCC_NO_CLAUDE_UPDATE_CHECK=1   skip the daily probe entirely.
  - (existing SCC_FORCE_REINSTALL=1 still works; suppresses the check
    when set, since reinstall is already happening.)

Tests:

  - tests/feature/helpers.bash exports SCC_NO_CLAUDE_UPDATE_CHECK=1
    alongside SCC_NO_UPDATE_CHECK=1 so the daily curl never fires under
    test. All 56 existing bats tests pass; no new tests added (the new
    function is HTTP-bound and matches the established pattern).

README "Claude binary handling" section gains a "Daily Claude update
check" subsection documenting the prompt, the env vars, and where the
metadata lives. scc.sh.version bumped 1.1.2 → 1.2.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat(launcher): SCC_PLATFORM + .scc/config.ini platform= override (1.2.0 → 1.3.0)
All checks were successful
build-and-push / test-1 (push) Successful in 28s
build-and-push / test (push) Successful in 0s
test / test (push) Successful in 24s
test / test (pull_request) Successful in 24s
build-and-push / build (push) Successful in 3m39s
58b13f1b57
Adds a third configurable field next to image and claude_args: a docker
--platform pin. Use case: running the new fedora/android-flutter image
(amd64-only, since Google ships no arm64 Android SDK) on Apple Silicon
under Rosetta-for-Linux. Without --platform, docker warns about arch
mismatch and Rosetta's behavior is implicit; with --platform=linux/amd64
the launcher tells docker exactly what to pull + run.

Resolution order matches the existing image plumbing:

  SCC_PLATFORM env > .scc/config.ini platform= > (omit, docker chooses)

Wired through both `docker pull` and `docker run` via a shared
platform_args array. The status line prints "(--platform linux/amd64)"
when set so you can tell at a glance whether emulation is in play.

Validation is minimal — `^[a-z0-9]+/[a-z0-9/.-]+$` catches the obvious
typo (`amd64` without the `linux/` prefix) but otherwise trusts docker
to error on garbage. Anything that looks like os/arch passes through.

Trust gate extended:

  - require_project_trust() takes a 4th argument (platform).
  - Trust file format gains a `platform=` line. Old trust files without
    the line keep working because ini_get returns "" for missing keys
    and ""="" compares equal when the project also doesn't pin platform.
    Re-prompt only fires when a project NEWLY pins a platform that
    wasn't in the old trust file.
  - SCC_PLATFORM env is user-explicit and does NOT trigger the trust
    prompt, matching the SCC_IMAGE behavior.

Tests: 3 new bats cases in project_config.bats:

  - platform = … in config.ini reaches docker run argv as
    `--platform linux/amd64`
  - SCC_PLATFORM env overrides the config.ini value
  - Malformed platform (`amd64`, no `linux/` prefix) rejected with rc=2

All 59/59 tests pass (was 56 before).

README's "Schema" + "Trust prompt" sections updated to document the new
field, the resolution order, and the bypass env vars. scc.sh.version
bumped 1.2.0 → 1.3.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat(launcher): per-project Claude volume via .scc/config.ini volume= (1.3.0 → 1.4.0)
All checks were successful
build-and-push / test-1 (push) Successful in 25s
build-and-push / test (push) Successful in 0s
test / test (push) Successful in 26s
build-and-push / build (push) Successful in 3m48s
test / test (pull_request) Successful in 25s
68233c9c41
The named volume `scc-claude` is shared across all image flavors — first
image to install Claude wins, every subsequent flavor exec's that same
binary. When two flavors have substantially different runtime layouts
(e.g. default Fedora-with-Node vs android-flutter without it, or amd64
under Rosetta vs native arm64), the cached binary from flavor A can fail
to exec under flavor B's environment.

Add a project-pinnable override so each project can opt into its own
Claude volume, matching the existing image= / claude_args= / platform=
pattern:

  [scc]
  image  = forge.stacktop.network/openstacktop/scc:android-flutter-fedora-latest
  volume = scc-claude-android-flutter

Resolution: SCC_CLAUDE_VOLUME env > .scc/config.ini volume= > default
"scc-claude". CLAUDE_VOLUME loses `readonly` so it can be reassigned
after project-config parsing; the three downstream usages (status label,
state label, bind mount) are unchanged.

Not part of the trust gate: a project pointing at a different cached
binary is no greater risk than image= itself (which already gates), and
the existing SCC_CLAUDE_VOLUME env path doesn't gate either — adding it
to the trust hash now would be asymmetric. Existing trust files keep
working unchanged.

Tests:

  - volume = … in config.ini → docker run argv contains
    `my-project-claude:/opt/scc-claude` and NOT `scc-claude:/opt/scc-claude`
  - SCC_CLAUDE_VOLUME env overrides the config.ini value

All 61/61 bats tests pass (was 59 before).

README schema + resolution-priority table updated; help text mentions the
new field and the env precedence. scc.sh.version bumped 1.3.0 → 1.4.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chore: trigger forgejo PR recompute (#55)
All checks were successful
build-and-push / test-1 (push) Successful in 24s
build-and-push / test (push) Successful in 0s
test / test (push) Successful in 25s
test / test (pull_request) Successful in 25s
build-and-push / build (push) Successful in 3m38s
a5e818d56a
Merge branch 'main' into dev
All checks were successful
build-and-push / test-1 (push) Successful in 24s
build-and-push / test (push) Successful in 0s
test / test (push) Successful in 24s
test / test (pull_request) Successful in 25s
build-and-push / build (push) Successful in 3m34s
40a6208e86
Resolves the squash-vs-individual-commits conflict pattern. Main contains
the squashed PR #54 (claude update check + platform override) as one
commit; dev contained the same changes as three separate commits plus
the new volume= work on top. Git's 3-way merge couldn't auto-resolve
since both sides modified the same regions, but dev's content is a
strict superset of main's (it has main's changes via the 3-commit
sequence plus the volume= additions), so all four conflicting files
resolved by taking dev's side:

  README.md
  scc.sh
  scc.sh.version
  tests/feature/project_config.bats

61/61 bats tests pass post-resolution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix(entrypoint): locate Claude binary by path, not via command -v (1.4.0 → 1.4.1)
All checks were successful
build-and-push / test-1 (push) Successful in 25s
build-and-push / test (push) Successful in 0s
test / test (push) Successful in 25s
test / test (pull_request) Successful in 24s
build-and-push / build (push) Successful in 3m46s
a94cd6fc7f
Claude Code 2.1+ ships as a single native binary and the install.sh
drops it at ~/.local/bin/claude WITHOUT adding that directory to PATH
(the installer just prints "echo 'export PATH=...' >> your shell config"
as a manual setup hint). The entrypoint was using `command -v claude`
to find what the installer just dropped, which returned empty and
aborted the install with "claude not on PATH after install — installer
layout changed?" — even though the binary was successfully written.

Fix: check the known native-install locations directly
(/root/.local/bin/claude, $HOME/.local/bin/claude) before falling back
to a PATH-widened `command -v` for older or relocated layouts. The
fallback keeps backward compatibility with the previous installer that
did expose claude on PATH; the explicit-path checks handle the 2.1+
native-install flow.

Surfaced by:
  - fresh `scc-claude-android-flutter` volume + `scc` against the
    android-flutter image. Install succeeded ("Claude Code successfully
    installed! Version: 2.1.150 Location: ~/.local/bin/claude") but the
    next entrypoint step bailed out with the missing-on-PATH error.

scc.sh.version bumped 1.4.0 → 1.4.1; 61/61 bats tests still pass (the
entrypoint runs inside the container, not on the bats host, so coverage
is unchanged).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merge branch 'main' into dev
All checks were successful
build-and-push / test-1 (push) Successful in 26s
build-and-push / test (push) Successful in 0s
test / test (push) Successful in 25s
test / test (pull_request) Successful in 24s
build-and-push / build (push) Successful in 3m41s
efac92a816
Same squash-vs-individual-commits conflict pattern as the previous merge
back (PR #55). Main has my volume= change as a squash commit; dev has
the same change as its original commit plus the 1.4.1 entrypoint patch
on top. Conflicts on scc.sh (version line) and scc.sh.version (only),
both resolved by taking dev's side — dev contains main's 1.4.0 plus the
1.4.1 patch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
to merged commit ff591c8530 into main 2026-05-26 17:14:21 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
openstacktop/scc!56
No description provided.