fix: replace host bind mounts with named volumes for SF auth - #8
Open
gambe94 wants to merge 4 commits into
Open
Conversation
Opening the devcontainer left you unauthenticated every rebuild, because
credentials live in the host ~/.sf and were not shared. That directory is now
bind-mounted, so auth is inherited and a login on either side persists to both.
post-create.sh resolves target-org — falling back to a committed alias, since
.sf/ is gitignored and never reaches a fresh clone — and fails loudly with the
exact login command when the org is not authorized, rather than leaving a shell
that looks fine and cannot deploy.
Also deletes .devcontainer/{Dockerfile,.zshrc,.p10k.zsh}: devcontainer.json
pulls the published image, so none of them were ever built, and the Dockerfile
claimed Node 20 where the image ships 24.
The README still documented the local Dockerfile/.zshrc/.p10k.zsh build that 28a3a3c deleted, describing Ubuntu 22.04, Node 18, and Oh My Zsh + Powerlevel10k — none of which the published gforceinnovation/sf-devcontainer image ships. It also never mentioned that Salesforce auth is now inherited from the host via the ~/.sf bind mount. Rewrite it to match what a developer actually gets: no local build to edit or rebuild, real aliases from the image's .zshrc, host-inherited auth (including the production-org exposure caveat), and what post-create.sh does when the target org isn't authorized. Also replace the GitHub Actions "::error::" annotation syntax in post-create.sh with a plain message — it only renders in Actions; here it just printed literally. Exit code and behavior are unchanged.
Commits 28a3a3c and ac4f998 bind-mounted the host's ~/.sf and ~/.sfdx into the devcontainer on the premise that Salesforce CLI auth would be inherited. That premise is false: macOS stores the CLI's encryption key in the OS Keychain, not in ~/.sf/~/.sfdx. Bind-mounting carries the encrypted auth files but not the key, and a Linux container cannot reach the host Keychain - every org fails `sf org list` with AuthDecryptError. Measured on this machine with both bind-mounted orgs. Fix: named Docker volumes instead of host bind mounts, so auth is created and decrypted entirely inside the container (container-side key) and persists across rebuilds via the volume, independent of host credentials. This is the same pattern already proven in sf-docker-images' examples/docker-compose.yml and its own .devcontainer/devcontainer.json. Also: - Fix /commandhistory being root-owned (container runs as vscode, UID 1000) by chowning it in postCreateCommand before npm install runs. - Rewrite post-create.sh to stop checking for/describing a host mount, and to point users at logging in inside the container on auth failure. - Rewrite the README auth section (drop the "container can use every host org, including production" warning - that's no longer true) and add a version-accuracy note that the documented Starship shell requires sf-devcontainer >= 3.0.0, which is unreleased; :latest is still v2.0.0 with Oh My Zsh + Powerlevel10k. Do not revert this to a bind mount - it will silently break every org's auth again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
post-create.sh trusted `sf config get target-org`, but that resolves the host's /workspace/.sf/config.json — the workspace is bind-mounted, so it reads as sf's *Local* config and outranks the Global config in the named auth volume. The result was a script that resolved an alias only the host could reach and failed even when the container was correctly authorized, while the SF_DEFAULT_ORG_ALIAS fallback never fired (it was gated on the target-org being *unset*, which never happens on a developer machine). - Verify the resolved org with `sf org display` before accepting it; fall back to SF_DEFAULT_ORG_ALIAS when it is missing OR unauthorized. - Pin the alias with `sf config set target-org --global` only after it verifies, so it lands in the volume instead of writing container state back into the host's bind-mounted .sf/. - Repoint SF_DEFAULT_ORG_ALIAS from the github1 scratch org (expires 2026-08-15) to the gabor_dev Dev Hub, so the fallback does not rot. - Drop --set-default from the documented login commands for the same write-back reason, and replace `sf org display --verbose | jq .sfdxAuthUrl` with `sf org auth show-sfdx-auth-url` — recent sf releases redact the former. Verified against gforceinnovation/sf-devcontainer:latest with the exact mounts from devcontainer.json: authorized org passes (exit 0), fresh clone with no local .sf passes (exit 0), empty auth volume still fails loudly (exit 1), and the host's .sf/config.json and target-org are left untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
This devcontainer used to consume the published
sf-devcontainerimage and inheritSalesforce org auth by bind-mounting the host's
~/.sf/~/.sfdxdirectly into thecontainer. That's broken: the SF CLI's stored auth tokens are encrypted with a key from
the host OS's keychain (macOS Keychain here), which a Linux container can't read — every
org fails
sf org listwithAuthDecryptErroreven though the credential files areright there.
~/.sf/~/.sfdxare now named Docker volumes instead(
sf-develop-demo-sf-config/sf-develop-demo-sfdx-config). Auth is created and readentirely inside the container via
sf org login web(or non-interactively viaSF_AUTH_URL), and persists across rebuilds without ever touching host-encrypted files.Trade-off: a host login is no longer automatically visible in the container (and vice
versa) — you log in once per container, matching how
sf-docker-images' own referencedevcontainer and Docker Compose recipe already work.
.devcontainer/post-create.shandREADME.mdare updated to match: error messages nowpoint at logging in inside the container instead of "either side persists to both,"
and the README's auth section explains the volume model and why the old bind mount
broke.
Also included from earlier in this branch: dropped the local
Dockerfile/.zshrc/.p10k.zsh— this devcontainer now pullsgforceinnovation/sf-devcontainer:latestdirectly instead of building locally, so it always matches the maintained image
(Starship, not Oh My Zsh/Powerlevel10k — see
sf-docker-imagesPR #15 for that switch).Test plan
Dev Containers: Rebuild Container— new named volumes created, container startssf org login web --alias <org> --set-defaultinside the container succeedssf org listshows the orgConnected, noAuthDecryptErrorDev Containers: Rebuild Containeragain (no volume removal) — auth still present,post-create.shpasses without re-authenticatingthey hit directly)
🤖 Generated with Claude Code