From f56b168849cc88bb6b337d06a8e6a64cf1d9673a Mon Sep 17 00:00:00 2001 From: Atirna <288419661+atirna@users.noreply.github.com> Date: Mon, 7 Sep 2026 01:14:57 +0530 Subject: [PATCH 1/2] fix(cli): export Java trust-store settings to all shells --- cli/templates/devcontainer/Dockerfile.app | 37 +++---------------- .../devcontainer/sandcat/scripts/app-init.sh | 7 +++- .../devcontainer/sandcat/scripts/java-env.sh | 10 +++++ cli/test/init/stacks.bats | 12 ++++++ cli/test/run/run.bats | 25 +++++++++++++ 5 files changed, 58 insertions(+), 33 deletions(-) create mode 100644 cli/templates/devcontainer/sandcat/scripts/java-env.sh diff --git a/cli/templates/devcontainer/Dockerfile.app b/cli/templates/devcontainer/Dockerfile.app index b89d3a6e..9bb586ed 100644 --- a/cli/templates/devcontainer/Dockerfile.app +++ b/cli/templates/devcontainer/Dockerfile.app @@ -29,6 +29,7 @@ RUN rm -f /etc/sudoers.d/vscode COPY --chmod=755 sandcat/scripts/app-init.sh /usr/local/bin/app-init.sh COPY --chmod=755 sandcat/scripts/app-user-init.sh /usr/local/bin/app-user-init.sh +COPY --chmod=644 sandcat/scripts/java-env.sh /etc/profile.d/sandcat-java.sh COPY --chown=vscode:vscode sandcat/tmux.conf /home/vscode/.tmux.conf USER vscode @@ -39,47 +40,19 @@ ENV LANG="en_US.UTF-8" # __DEVBOX_INSTALL__ -# If a JDK is installed via devbox (java or scala stack, or a user entry -# in devbox.tools.json), bake JAVA_HOME and JAVA_TOOL_OPTIONS into .bashrc -# so VS Code's env probe picks them up before the entrypoint runs. Without -# JAVA_HOME, JVM tooling like Metals fails to find the JDK. -# -# JDK-distribution-agnostic detection: devbox always exposes bin/java in -# its profile as a symlink to the actual JDK inside /nix/store/. Following -# that symlink and stripping bin/ gives the canonical JAVA_HOME for -# whichever distribution the user picked (openjdk, temurin-bin-*, jdk, -# jetbrains.jdk*, or any future one) — no hardcoded layout assumptions. -# Every valid JDK derivation has $JAVA_HOME/lib/security/cacerts inside. -# -# JAVA_TOOL_OPTIONS points to a trust store copy that app-user-init.sh -# populates with the mitmproxy CA at runtime; until then it holds the -# default Java CAs (harmless). -RUN JAVA_BIN="$HOME/.local/share/devbox/global/default/.devbox/nix/profile/default/bin/java"; \ - if [ -e "$JAVA_BIN" ]; then \ - DEVBOX_JAVA="$(dirname $(dirname $(readlink -f "$JAVA_BIN")))"; \ - dir="$HOME/.local/share/sandcat"; mkdir -p "$dir"; \ - ln -sfn "$DEVBOX_JAVA" "$dir/java-home"; \ - { echo ''; \ - echo '# sandcat-java-env'; \ - echo '[ -L "$HOME/.local/share/sandcat/java-home" ] && export JAVA_HOME="$HOME/.local/share/sandcat/java-home"'; \ - echo '[ -f "$HOME/.local/share/sandcat/cacerts" ] && export JAVA_TOOL_OPTIONS="-Djavax.net.ssl.trustStore=$HOME/.local/share/sandcat/cacerts -Djavax.net.ssl.trustStorePassword=changeit"'; \ - } >> "$HOME/.bashrc"; \ - fi - # __AGENT_DOCKER_HOME_PREP__ USER root # Snapshot the image-side /home/vscode state that the agent-home volume # will mask at runtime (devbox profile with symlinks into /nix/store, the -# sandcat helper dir with java-home + baseline cacerts, and .bashrc env -# hooks). app-init.sh rsyncs this back into the volume when the snapshot +# sandcat helper dir with java-home + baseline cacerts, and .bashrc). app-init.sh +# rsyncs this back into the volume when the snapshot # hash changes, so rebuilds that add/remove packages or switch JDKs take # effect without `docker compose down -v` (which would wipe auth Claude # Code and force the IDE backend to re-upload). # -# The hash covers merged devbox.json + .bashrc — the two files that -# capture "what packages devbox installed" and "what env we bake". Any -# stack/tools/Java change flips at least one of them; unchanged rebuilds +# The hash covers merged devbox.json + .bashrc. Any stack or tools change +# flips at least one of them; unchanged rebuilds # leave the hash stable so app-init.sh skips the sync entirely. RUN mkdir -p /opt/sandcat/snapshots \ && cp -a /home/vscode/.local/share/devbox /opt/sandcat/snapshots/devbox \ diff --git a/cli/templates/devcontainer/sandcat/scripts/app-init.sh b/cli/templates/devcontainer/sandcat/scripts/app-init.sh index 9dc67026..c91eb534 100644 --- a/cli/templates/devcontainer/sandcat/scripts/app-init.sh +++ b/cli/templates/devcontainer/sandcat/scripts/app-init.sh @@ -124,7 +124,7 @@ else fi # Refresh image-managed home state (devbox profile, sandcat java-home -# symlink and baseline cacerts, .bashrc env hooks) into the agent-home +# symlink and baseline cacerts, .bashrc) into the agent-home # volume when the image snapshot has changed since last start. # # The volume masks anything the Dockerfile writes under /home/vscode after @@ -203,6 +203,11 @@ done # shell so app-user-init still receives sandcat placeholders and env vars. su - vscode -c '. /mitmproxy-config/sandcat.env 2>/dev/null || true; /usr/local/bin/app-user-init.sh' +# app-user-init.sh has now refreshed the Java trust store. Source the same +# profile script that login and interactive shells use so the agent process +# and every shell it starts inherit the Java settings too. +. /etc/profile.d/sandcat-java.sh + # Source all sandcat profile.d scripts from /etc/bash.bashrc so env vars # are available in non-login shells (e.g. VS Code integrated terminals). # Guard with a marker to avoid duplicating on container restart. diff --git a/cli/templates/devcontainer/sandcat/scripts/java-env.sh b/cli/templates/devcontainer/sandcat/scripts/java-env.sh new file mode 100644 index 00000000..e4ba75a7 --- /dev/null +++ b/cli/templates/devcontainer/sandcat/scripts/java-env.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +sandcat_home="${SANDCAT_HOME:-/home/vscode}" +if [ -L "$sandcat_home/.local/share/sandcat/java-home" ]; then + export JAVA_HOME="$sandcat_home/.local/share/sandcat/java-home" +fi +if [ -f "$sandcat_home/.local/share/sandcat/cacerts" ]; then + export JAVA_TOOL_OPTIONS="-Djavax.net.ssl.trustStore=$sandcat_home/.local/share/sandcat/cacerts -Djavax.net.ssl.trustStorePassword=changeit" +fi +unset sandcat_home diff --git a/cli/test/init/stacks.bats b/cli/test/init/stacks.bats index 6a97e09f..c2b54940 100644 --- a/cli/test/init/stacks.bats +++ b/cli/test/init/stacks.bats @@ -54,6 +54,18 @@ teardown() { refute_output --partial "openjdk" } +@test "Dockerfile installs the Java profile instead of .bashrc hooks" { + local dockerfile="$SCT_TEMPLATEDIR/devcontainer/Dockerfile.app" + local app_init="$SCT_TEMPLATEDIR/devcontainer/sandcat/scripts/app-init.sh" + run grep -F 'COPY --chmod=644 sandcat/scripts/java-env.sh /etc/profile.d/sandcat-java.sh' "$dockerfile" + assert_success + run grep -F '. /etc/profile.d/sandcat-java.sh' "$app_init" + assert_success + + run grep -F '# sandcat-java-env' "$dockerfile" + assert_failure +} + @test "stack_extension returns extension ID for stacks with extensions" { run stack_extension python assert_output "ms-python.python" diff --git a/cli/test/run/run.bats b/cli/test/run/run.bats index e2420b6a..4773e9ed 100755 --- a/cli/test/run/run.bats +++ b/cli/test/run/run.bats @@ -21,6 +21,31 @@ teardown() { unstub_all } +@test "Java profile reaches non-login child shells" { + local sandcat_home="$BATS_TEST_TMPDIR/vscode" + local java_env="$SCT_ROOT/templates/devcontainer/sandcat/scripts/java-env.sh" + mkdir -p "$sandcat_home/.local/share/sandcat" + ln -s /nix/store/example-jdk "$sandcat_home/.local/share/sandcat/java-home" + touch "$sandcat_home/.local/share/sandcat/cacerts" + + run env -i PATH="$PATH" SANDCAT_HOME="$sandcat_home" bash --noprofile --norc -c \ + ". '$java_env'; bash --noprofile --norc -c 'printf \"%s\\n%s\" \"\$JAVA_HOME\" \"\$JAVA_TOOL_OPTIONS\"'" + assert_success + assert_output "$sandcat_home/.local/share/sandcat/java-home +-Djavax.net.ssl.trustStore=$sandcat_home/.local/share/sandcat/cacerts -Djavax.net.ssl.trustStorePassword=changeit" +} + +@test "Java profile leaves non-JVM shells unconfigured" { + local sandcat_home="$BATS_TEST_TMPDIR/vscode" + local java_env="$SCT_ROOT/templates/devcontainer/sandcat/scripts/java-env.sh" + mkdir -p "$sandcat_home/.local/share/sandcat" + + run env -i PATH="$PATH" SANDCAT_HOME="$sandcat_home" bash --noprofile --norc -c \ + ". '$java_env'; printf '<%s>|<%s>' \"\${JAVA_HOME-}\" \"\${JAVA_TOOL_OPTIONS-}\"" + assert_success + assert_output "<>|<>" +} + # --- warn_stale_home_volume --- @test "no warning when volume does not exist (first run)" { From 0be3aba7a7ee386557d8fe4747c359040a291beb Mon Sep 17 00:00:00 2001 From: shejnowicz Date: Mon, 7 Sep 2026 11:28:29 +0200 Subject: [PATCH 2/2] refactor: rename java-env override to SANDCAT_USER_HOME Follow-up to the previous commit: SANDCAT_HOME already means "host CLI install dir" in install.sh (~/.local/share/sandcat), so reusing the same name inside the agent container for "the vscode user's home" gives one variable two meanings. Rename the test seam to SANDCAT_USER_HOME; behavior unchanged. Co-Authored-By: Claude Opus 5 (1M context) --- cli/templates/devcontainer/sandcat/scripts/java-env.sh | 2 +- cli/test/run/run.bats | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/templates/devcontainer/sandcat/scripts/java-env.sh b/cli/templates/devcontainer/sandcat/scripts/java-env.sh index e4ba75a7..f87a82ab 100644 --- a/cli/templates/devcontainer/sandcat/scripts/java-env.sh +++ b/cli/templates/devcontainer/sandcat/scripts/java-env.sh @@ -1,6 +1,6 @@ #!/bin/bash -sandcat_home="${SANDCAT_HOME:-/home/vscode}" +sandcat_home="${SANDCAT_USER_HOME:-/home/vscode}" if [ -L "$sandcat_home/.local/share/sandcat/java-home" ]; then export JAVA_HOME="$sandcat_home/.local/share/sandcat/java-home" fi diff --git a/cli/test/run/run.bats b/cli/test/run/run.bats index 4773e9ed..55097fb4 100755 --- a/cli/test/run/run.bats +++ b/cli/test/run/run.bats @@ -28,7 +28,7 @@ teardown() { ln -s /nix/store/example-jdk "$sandcat_home/.local/share/sandcat/java-home" touch "$sandcat_home/.local/share/sandcat/cacerts" - run env -i PATH="$PATH" SANDCAT_HOME="$sandcat_home" bash --noprofile --norc -c \ + run env -i PATH="$PATH" SANDCAT_USER_HOME="$sandcat_home" bash --noprofile --norc -c \ ". '$java_env'; bash --noprofile --norc -c 'printf \"%s\\n%s\" \"\$JAVA_HOME\" \"\$JAVA_TOOL_OPTIONS\"'" assert_success assert_output "$sandcat_home/.local/share/sandcat/java-home @@ -40,7 +40,7 @@ teardown() { local java_env="$SCT_ROOT/templates/devcontainer/sandcat/scripts/java-env.sh" mkdir -p "$sandcat_home/.local/share/sandcat" - run env -i PATH="$PATH" SANDCAT_HOME="$sandcat_home" bash --noprofile --norc -c \ + run env -i PATH="$PATH" SANDCAT_USER_HOME="$sandcat_home" bash --noprofile --norc -c \ ". '$java_env'; printf '<%s>|<%s>' \"\${JAVA_HOME-}\" \"\${JAVA_TOOL_OPTIONS-}\"" assert_success assert_output "<>|<>"