feat(launcher): per-project Claude volume override (.scc/config.ini volume=) #55

Merged
to merged 5 commits from dev into main 2026-05-26 17:01:57 +02:00
Owner

Summary

Adds a fourth field to .scc/config.ini (and matches the existing env / project / default resolution pattern): volume = ... lets a project opt into its own Docker named volume for the Claude install instead of sharing the global scc-claude.

Motivation: the shared scc-claude volume holds whichever Claude binary the FIRST flavor installed. Switching to a flavor with a substantially different runtime (e.g. fedora/android-flutter, no Node) silently re-execs the cached binary from the previous flavor — which can fail with cannot execute: required file not found if the runtime layout drifted.

Per-project pin → per-project volume → fresh, flavor-appropriate Claude install.

Resolution

SCC_CLAUDE_VOLUME env > .scc/config.ini volume= > default scc-claude. Symmetric with the existing image/platform plumbing.

Not part of trust

A project pinning a different cached binary is no greater risk than image= (which already gates and writes the binary in the first place); the existing SCC_CLAUDE_VOLUME env path doesn't gate either. Adding volume to the trust hash would be inconsistent. Existing trust files keep working unchanged.

Test plan

  • bats --recursive tests/ -> 61/61 pass (was 59; 2 new cases)
  • bash -n scc.sh clean
  • CI test job
  • After merge + build: drop volume = scc-claude-android-flutter into videoguard-app/.scc/config.ini, run scc, expect fresh Claude install in the isolated volume and no "required file not found" from cross-flavor binary reuse
## Summary Adds a fourth field to `.scc/config.ini` (and matches the existing env / project / default resolution pattern): `volume = ...` lets a project opt into its own Docker named volume for the Claude install instead of sharing the global `scc-claude`. **Motivation:** the shared `scc-claude` volume holds whichever Claude binary the FIRST flavor installed. Switching to a flavor with a substantially different runtime (e.g. fedora/android-flutter, no Node) silently re-execs the cached binary from the previous flavor — which can fail with `cannot execute: required file not found` if the runtime layout drifted. Per-project pin → per-project volume → fresh, flavor-appropriate Claude install. ## Resolution `SCC_CLAUDE_VOLUME` env > `.scc/config.ini` `volume=` > default `scc-claude`. Symmetric with the existing image/platform plumbing. ## Not part of trust A project pinning a different cached binary is no greater risk than `image=` (which already gates and writes the binary in the first place); the existing `SCC_CLAUDE_VOLUME` env path doesn't gate either. Adding `volume` to the trust hash would be inconsistent. Existing trust files keep working unchanged. ## Test plan - bats --recursive tests/ -> 61/61 pass (was 59; 2 new cases) - bash -n scc.sh clean - CI test job - After merge + build: drop `volume = scc-claude-android-flutter` into videoguard-app/.scc/config.ini, run scc, expect fresh Claude install in the isolated volume and no "required file not found" from cross-flavor binary reuse
to added 3 commits 2026-05-26 16:32:52 +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>
to referenced this pull request from a commit 2026-05-26 16:51:45 +02:00
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>
to merged commit ea0a805dcf into main 2026-05-26 17:01:57 +02:00
to referenced this pull request from a commit 2026-05-26 17:14:10 +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!55
No description provided.