Skip to content

fix(devcontainer): sync to python_template's zero-build config - #1126

Merged
blooop merged 5 commits into
mainfrom
fix/devcontainer-zero-build
Sep 3, 2026
Merged

blooop merged 5 commits into
mainfrom
fix/devcontainer-zero-build

Conversation

@blooop

@blooop blooop commented Aug 24, 2026 •

Copy link
Copy Markdown
Owner

The devcontainer now pulls ghcr.io/blooop/python_template/devcontainer:latest
instead of building the image locally.

  • The duplicate claude-code features block is gone. The feature is baked into
    that image already (ci/devcontainer.json is what CI builds it from, and that
    declares ../claude-code), so declaring it here made the spec build a derived
    image on the first launch of every branch and reinstall what the pulled image
    already had.
  • --network=host is gone. A container per branch is the point of dl, and
    host networking undoes it: every port a dev server binds becomes a host port,
    so two branches collide on the first one they share.
  • The ~/.ssh and ~/.config/gh mounts are gone. dl supplies GH_TOKEN, and
    the gh mount never worked in the first place because gh keeps its token in the
    system keyring, so the mounted hosts.yml carried no oauth_token.
  • name moved to the end of the file. It is the only per-repo line here, and
    sitting next to the image block -- the block the template changes most -- it
    conflicted on every pixi run update-from-template-repo.

The tree is byte-identical to blooop/python_template#170 apart from that name.

Depends on blooop/python_template#170. The published image already exists and is
public, so this works before that merges.

🤖 Generated with Claude Code

Summary by Sourcery

Adopt the published zero-build devcontainer image while simplifying branch isolation, credential handling, and repository initialization.

New Features:

  • Use a shared post-create workflow to prepare SSH host verification, install project dependencies, and run repository-supported optional tasks.

Bug Fixes:

  • Prevent branch devcontainers from rebuilding duplicate features, sharing host networking, and relying on ineffective SSH or GitHub CLI configuration mounts.

Enhancements:

  • Align the devcontainer with the published zero-build Python template image and document host-based Claude authentication through a shared writable configuration mount.
  • Clarify the Claude Code feature’s runtime behavior, authentication model, troubleshooting guidance, and security trade-offs.
  • Make the devcontainer setup adaptable to repositories with different available Pixi tasks.

CI:

  • Keep Claude Code feature installation in the CI image configuration so published images contain the feature once.

Documentation:

  • Update Claude Code documentation to describe the published-image workflow, shared configuration mount, host authentication, and associated security implications.

Chores:

  • Move the repository-specific devcontainer name away from template-managed configuration to reduce update conflicts.

The devcontainer now pulls ghcr.io/blooop/python_template/devcontainer:latest
instead of building the image locally.

- The duplicate claude-code `features` block is gone. The feature is baked into
  that image already (ci/devcontainer.json is what CI builds it from, and that
  declares ../claude-code), so declaring it here made the spec build a derived
  image on the first launch of every branch and reinstall what the pulled image
  already had.
- `--network=host` is gone. A container per branch is the point of `dl`, and
  host networking undoes it: every port a dev server binds becomes a host port,
  so two branches collide on the first one they share.
- The `~/.ssh` and `~/.config/gh` mounts are gone. `dl` supplies GH_TOKEN, and
  the gh mount never worked in the first place because gh keeps its token in the
  system keyring, so the mounted hosts.yml carried no oauth_token.
- `name` moved to the end of the file. It is the only per-repo line here, and
  sitting next to the image block -- the block the template changes most -- it
  conflicted on every `pixi run update-from-template-repo`.

The tree is byte-identical to blooop/python_template#170 apart from that name.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @blooop, you have reached your weekly rate limit of 250000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

The PR synchronizes the runtime devcontainer with python_template’s published zero-build image, moving Claude Code feature installation to the CI-built image, removing branch-conflicting host networking and ineffective credential mounts, and documenting the resulting shared ~/.claude authentication and security model.

Sequence diagram for host-authenticated Claude Code startup

sequenceDiagram
    participant Host as Host machine
    participant Config as Host ~/.claude
    participant Container as Branch container
    participant Claude as Claude Code

    Host->>Claude: Authenticate on host
    Claude->>Config: Write credentials
    Container->>Config: Read live bind mount
    Container->>Claude: Start with CLAUDE_CONFIG_DIR
    Claude->>Config: Refresh credentials and workspace state
Loading

Flow diagram for concurrent branch container networking

flowchart LR
    BranchA[Branch container A] -->|default bridge network| PortA[Branch-local dev server port]
    BranchB[Branch container B] -->|default bridge network| PortB[Branch-local dev server port]
    PortA -.->|no host-port collision| PortB
Loading

File-Level Changes

Change Details Files
Switch the branch devcontainer from local, feature-derived builds to the published zero-build Python template image.
  • Point the runtime configuration at the public GHCR image.
  • Remove the local feature declaration and host-networking argument from the runtime specification.
  • Keep Claude Code feature installation in the CI image definition.
  • Remove Dockerfile setup that only supported deleted SSH and GitHub CLI mounts.
  • Move the per-repository name field to reduce template-update conflicts.
.devcontainer/devcontainer.json
.devcontainer/ci/devcontainer.json
.devcontainer/Dockerfile
Replace the Claude configuration mount and authentication model with a shared host directory.
  • Mount the complete host ~/.claude directory read-write and set CLAUDE_CONFIG_DIR accordingly.
  • Document host-side Claude authentication and live credential/state sharing across containers.
  • Add or clarify host initialization and mount verification behavior.
  • Update troubleshooting and setup guidance for the directory mount and workspace-specific onboarding state.
.devcontainer/claude-code/README.md
.devcontainer/claude-code/TROUBLESHOOTING.md
.devcontainer/devcontainer.json
Document the security and networking trade-offs of the new devcontainer model.
  • Explain that all Claude configuration, hooks, and credentials are writable from the container and affect the host.
  • Recommend using the setup only with trusted repositories and treating the container as non-isolating for credentials and configuration.
  • Document host networking as an opt-in authentication workaround and its port-collision, extension-installation, and host-access costs.
.devcontainer/claude-code/README.md
.devcontainer/claude-code/TROUBLESHOOTING.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions

Copy link
Copy Markdown

Performance Report for d55ae7a

Metric Value
Total tests 2936
Total time 131.29s
Mean 0.0447s
Median 0.0020s
Top 10 slowest tests
Test Time (s)
test.test_bench_examples.TestBenchExamples::test_example_meta 16.544
test.test_split_render_examples::test_split_render_subprocess_media 5.313
test.test_hash_persistent.TestCrossProcessDeterminism::test_hash_stable_across_two_processes[ResultBool] 5.042
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_drift.py] 3.665
test.test_over_time_save_perf::test_save_faster_without_aggregated_tab 3.379
test.test_over_time_repeats.TestMaxSliderPoints::test_default_subsampling_caps_at_max 2.631
test.test_render.TestCollect::test_collect_constructs_far_fewer_objects_than_render 2.456
test.test_generated_examples::test_generated_example[cartesian_animation/example_cartesian_animation.py] 2.430
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_noise.py] 2.416
test.test_generated_examples::test_generated_example[result_types/result_image/example_result_image_to_video.py] 2.323

Full report

Updated by Performance Tracking workflow

The earlier commit on this branch removed the ~/.config/gh bind mount and
justified it with the claim that the mount never worked -- that `gh` keeps its
token in the system keyring, so the mounted hosts.yml carries no oauth_token.

That claim is false on this machine. ~/.config/gh/hosts.yml carries a real
oauth_token, and in a running container using that mount, with GH_TOKEN and
GITHUB_TOKEN unset, `gh auth status` reports:

    Logged in to github.com account blooop (/home/vscode/.config/gh/hosts.yml)

gh writes to a keyring only where one is available and falls back to the file
otherwise, so the keyring claim held for some environment but not this one, and
it got generalised into the config as if it always held.

Nothing broke under `dl`, which is why it went unnoticed: `dl` forwards
GH_TOKEN, devpod applies workspace env after the devcontainer's own, and the
env token wins wherever both are present. The entry paths that lost gh auth
they previously had are the non-`dl` ones -- a plain `devpod up`, and VS Code's
Reopen in Container. So the mount comes back.

The ~/.ssh removal is unaffected and stays removed. It was verified separately
in a live container: devpod forwards an ssh agent at a socket path of its own,
`ssh-add -l` lists the host key, and `git ls-remote` against a git@github.com:
origin succeeds with an empty ~/.ssh. The old hardcoded
SSH_AUTH_SOCK=/home/vscode/.ssh/agent.sock actively overrode that working
forwarded socket, so it stays gone too.
@blooop

blooop commented Aug 24, 2026

Copy link
Copy Markdown
Owner Author

Pushed a correction to this branch.

The earlier commit removed the ~/.config/gh bind mount on the grounds that it never worked -- that gh keeps its token in the system keyring, leaving the mounted hosts.yml without an oauth_token. That is false on this machine: hosts.yml carries a real oauth_token, and in a running container using the mount, with GH_TOKEN/GITHUB_TOKEN unset, gh auth status reports Logged in to github.com account blooop (/home/vscode/.config/gh/hosts.yml).

Nothing broke under dl because dl forwards GH_TOKEN and the env token wins. The entry paths that lost gh auth were the non-dl ones -- a plain devpod up and VS Code's Reopen in Container. So the mount is back.

The ~/.ssh removal is unaffected and stays removed: devpod forwards an ssh agent at its own socket path (verified live -- ssh-add -l lists the host key and git ls-remote succeeds with an empty ~/.ssh), and the old hardcoded SSH_AUTH_SOCK=/home/vscode/.ssh/agent.sock overrode that working socket.

@github-actions

Copy link
Copy Markdown

Performance Report for 5cfa7a4

Metric Value
Total tests 2936
Total time 156.18s
Mean 0.0532s
Median 0.0020s
Top 10 slowest tests
Test Time (s)
test.test_bench_examples.TestBenchExamples::test_example_meta 18.110
test.test_split_render_examples::test_split_render_subprocess_media 6.361
test.test_hash_persistent.TestCrossProcessDeterminism::test_hash_stable_across_two_processes[ResultBool] 6.132
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_drift.py] 4.567
test.test_over_time_save_perf::test_save_faster_without_aggregated_tab 4.486
test.test_generated_examples::test_generated_example[cartesian_animation/example_cartesian_animation.py] 3.016
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_noise.py] 2.970
test.test_generated_examples::test_generated_example[result_types/result_image/example_result_image_to_video.py] 2.748
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_step.py] 2.697
test.test_over_time_repeats.TestMaxSliderPoints::test_default_subsampling_caps_at_max 2.482

Full report

Updated by Performance Tracking workflow

…rek-install

Synced from blooop/python_template.

The previous postCreateCommand ended in `pixi run prek-install`. This repo does
not define that task, so `pixi run` exited 127, the lifecycle hook failed, and
container creation failed outright -- the workspace never came up. Found by
launching the container; neither JSON validation nor CI sees it.
postCreateCommand now calls .devcontainer/post-create.sh, which runs the task
only where it exists.

The script also seeds known_hosts. devpod forwards an ssh agent, so the host's
~/.ssh does not need mounting -- but the agent carries authentication, not host
identity, and on a fresh container the first git operation over an ssh remote
died with "Host key verification failed". Interactive users can click through
that; scripts, agents and in-container CI cannot.
@github-actions

Copy link
Copy Markdown

Performance Report for 2a8154e

Metric Value
Total tests 2936
Total time 148.90s
Mean 0.0507s
Median 0.0020s
Top 10 slowest tests
Test Time (s)
test.test_bench_examples.TestBenchExamples::test_example_meta 17.003
test.test_split_render_examples::test_split_render_subprocess_media 6.151
test.test_hash_persistent.TestCrossProcessDeterminism::test_hash_stable_across_two_processes[ResultBool] 6.085
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_drift.py] 4.441
test.test_over_time_save_perf::test_save_faster_without_aggregated_tab 4.001
test.test_generated_examples::test_generated_example[cartesian_animation/example_cartesian_animation.py] 3.087
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_noise.py] 2.868
test.test_over_time_repeats.TestMaxSliderPoints::test_default_subsampling_caps_at_max 2.832
test.test_generated_examples::test_generated_example[result_types/result_image/example_result_image_to_video.py] 2.669
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_step.py] 2.484

Full report

Updated by Performance Tracking workflow

The prek job failed on two findings in the new script:

- SC2020: `tr ' ,' '\n\n'` duplicates the replacement char. tr pads a short
  set2 out to set1's length on its own, so one `\n` does the same job.
- SC2043: `for task in prek-install` loops over a literal single word, which
  shellcheck reads as bad quoting. The list is meant to grow per repo, so it
  is now an array and the loop expands it.
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

Performance Report for 5c306ad

Metric Value
Total tests 2936
Total time 150.95s
Mean 0.0514s
Median 0.0020s
Top 10 slowest tests
Test Time (s)
test.test_bench_examples.TestBenchExamples::test_example_meta 17.202
test.test_hash_persistent.TestCrossProcessDeterminism::test_hash_stable_across_two_processes[ResultBool] 6.110
test.test_split_render_examples::test_split_render_subprocess_media 6.065
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_drift.py] 4.522
test.test_over_time_save_perf::test_save_faster_without_aggregated_tab 4.063
test.test_generated_examples::test_generated_example[cartesian_animation/example_cartesian_animation.py] 3.084
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_noise.py] 2.857
test.test_generated_examples::test_generated_example[result_types/result_image/example_result_image_to_video.py] 2.719
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_step.py] 2.510
test.test_over_time_repeats.TestMaxSliderPoints::test_default_subsampling_caps_at_max 2.356

Full report

Updated by Performance Tracking workflow

The PR claims this tree is byte-identical to python_template's apart from
`name`, and it was not: this file's ~/.ssh comment had drifted to a shorter
version that never mentions why post-create.sh runs ssh-keyscan. The template's
wording -- auth from the forwarded agent, host identity from keyscan -- is the
one that explains the script sitting next to it, and having the block match
byte for byte is the whole point of moving `name` to the end of the file.
With this the two files differ only in `name`.
@blooop
blooop enabled auto-merge September 3, 2026 12:53
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

Performance Report for e3eb752

Metric Value
Total tests 2936
Total time 156.10s
Mean 0.0532s
Median 0.0020s
Top 10 slowest tests
Test Time (s)
test.test_bench_examples.TestBenchExamples::test_example_meta 18.163
test.test_split_render_examples::test_split_render_subprocess_media 6.358
test.test_hash_persistent.TestCrossProcessDeterminism::test_hash_stable_across_two_processes[ResultBool] 6.172
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_drift.py] 4.619
test.test_over_time_save_perf::test_save_faster_without_aggregated_tab 4.492
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_noise.py] 3.067
test.test_generated_examples::test_generated_example[cartesian_animation/example_cartesian_animation.py] 2.996
test.test_over_time_repeats.TestMaxSliderPoints::test_default_subsampling_caps_at_max 2.992
test.test_generated_examples::test_generated_example[result_types/result_image/example_result_image_to_video.py] 2.757
test.test_generated_examples::test_generated_example[regression/example_regression_tuning_step.py] 2.712

Full report

Updated by Performance Tracking workflow

@blooop
blooop merged commit 709cfc6 into main Sep 3, 2026
7 of 8 checks passed
@blooop
blooop deleted the fix/devcontainer-zero-build branch September 3, 2026 13:01
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.

1 participant