Skip to content

Fix dev container build failing on restricted networks - #101

Closed
ayeshurun wants to merge 1 commit into
mainfrom
dev/alonyeshurun/symmetrical-couscous
Closed

Fix dev container build failing on restricted networks#101
ayeshurun wants to merge 1 commit into
mainfrom
dev/alonyeshurun/symmetrical-couscous

Conversation

@ayeshurun

Copy link
Copy Markdown
Owner

Problem

The dev container fails to build. The ghcr.io/devcontainers/features/node:2 feature dies with ECONNRESET while installing pnpm:

npm error network request to https://registry.npmjs.org/pnpm failed,
reason: Client network socket disconnected before secure TLS connection was established
ERROR: Feature "Node.js (via nvm), yarn and pnpm." failed to install!

Root cause

Networks that enforce an internal package feed proxy block the public artifact CDNs. Probing from inside the container:

Host Result
registry.npmjs.org blocked (HTTP 000)
files.pythonhosted.org blocked (HTTP 000)
pypi.org/simple/ 200
github.com release assets 302
deb.debian.org 200

The host's ~/.npmrc and pip config point at an internal proxy, but those files are not visible inside docker buildx build, so tooling in the container falls back to the blocked public registries.

Note the PyPI index is reachable while the file CDN is not — probing only pypi.org gives a false all-clear.

Changes

1. Remove the Node.js feature. Node existed solely to run npm install -g changie. Nothing in this repository is JavaScript — there is no package.json and no JS source. The feature pulled in nvm, yarn, and pnpm to install one Go binary.

2. Install changie from its GitHub release. changie ships standalone binaries. Now downloaded at a pinned version with a hardcoded per-arch SHA256, verified before install. This removes the npm registry from the critical path entirely.

Made non-fatal with a loud warning: the container is fully usable for Python development without changie, and hard-failing postCreate over an optional authoring tool is worse UX.

3. Fix apt-get running without sudo (pre-existing bug). postCreateCommand runs as the non-root vscode user, so apt-get update failed with Permission denied and set -e aborted the script before pip ever ran. This has been broken for everyone since e70ab4c — it was masked because the build never got far enough to run postCreate.

4. Remove the stale yarn apt source (pre-existing bug). The base image ships /etc/apt/sources.list.d/yarn.list with an expired signing key, so every apt-get update exits 100 with NO_PUBKEY 62D54FD4003F6525. Removed rather than masked with || true, so manual apt-get later in the container's life also works.

5. Forward PIP_INDEX_URL / PIP_EXTRA_INDEX_URL from the host. Vendor-neutral and opt-in — verified these resolve to an empty string when unset, and that pip treats an empty value as its default index. No behavior change for contributors who don't set them.

Deliberately not done

No internal feed URLs are hardcoded. This is a public repository; baking in packagefeedproxy.microsoft.io would break external contributors and leak internal infrastructure. The fix is to remove unnecessary public-registry dependencies, not to reconfigure feeds.

Verification

Ran the real devcontainer up end-to-end (@devcontainers/cli 0.88.0, macOS arm64):

{"outcome":"success","remoteUser":"vscode"}

Inside the resulting container:

changie version v1.26.0
/usr/local/bin/changie
apt-get update OK
imports OK        # pytest, mkdocs, msal

Config also validated with the env vars unset, confirming the external-contributor path resolves cleanly.

Follow-up (not in this PR)

.github/workflows/changelog-existence.yml still does npm i -g changie. That works on GitHub-hosted runners, but is inconsistent with this change and would break on a self-hosted runner behind a feed proxy. Left alone to keep this PR surgical.

Notes

  • No upstream issue is linked. AGENTS.md requires - Resolves #<issue>; please add one if this is retargeted at microsoft/fabric-cli.
  • Changelog entry added under .changes/unreleased/.

The dev container failed to build because the Node.js feature installs pnpm
from the public npm registry, which is unreachable on networks that enforce an
internal package feed proxy.

Node.js existed only to run `npm install -g changie`. Nothing in this
repository is JavaScript, so the feature is removed and changie is installed
from its GitHub release as a pinned, checksum-verified binary.

Two pre-existing bugs in the postCreate script surfaced once the build got far
enough to run it:

* The script called `apt-get` without sudo, but postCreateCommand runs as the
  non-root `vscode` user, so it failed with "Permission denied" and aborted
  before pip ever ran.
* The base image ships an apt source for yarn whose signing key is no longer
  valid, making every `apt-get update` exit 100. Nothing uses yarn, so the
  broken source is removed rather than masked.

Also forwards PIP_INDEX_URL and PIP_EXTRA_INDEX_URL from the host, since
container builds do not inherit host pip configuration. Both resolve to an
empty value when unset, which leaves pip on its default index.

Co-authored-by: Copilot App <[email protected]>
@ayeshurun

Copy link
Copy Markdown
Owner Author

Closing in favour of #97, which solves this more completely. Recording why, since the reasoning is the useful part.

This PR does not actually fix the reported bug. It relied on:

"remoteEnv": { "PIP_INDEX_URL": "${localEnv:PIP_INDEX_URL}" }

That passthrough is a no-op on the affected machines, because the internal mirror is configured in a pip config file, not an environment variable:

$ env | grep -E "^PIP_"
(nothing)

$ pip config debug
global:
  global.index-url: https://<internal-mirror>/pypi/simple/

So ${localEnv:PIP_INDEX_URL} expands to empty, pip falls back to the default index, and the build still fails against the blocked files.pythonhosted.org.

My earlier end-to-end run passed only because I had manually exported PIP_INDEX_URL in my own shell before testing — a condition the reporting user does not have. That is a testing error on my part: I validated the fix under conditions I had created rather than the ones in the bug report.

#97 uses a git-ignored .devcontainer/local.env file instead, which works regardless of how the host configures pip. I rebuilt #97 with no PIP_* variables set and it succeeded.

#97 is also ahead on several other points: sudo availability and passwordless checks, proxy preservation through sudo (env_reset otherwise drops http_proxy), an EXIT trap for temp-dir cleanup, --proto =https, connect/max timeouts, hard-failing instead of warning, and extracting the binary to stdout so a substituted archive cannot use a symlink to redirect the privileged install.

The one thing this PR did better was pinning per-arch SHA256 constants rather than trusting checksums.txt from the same origin. I have raised that on #97 as a documented trade-off rather than porting it, since it would change an already-reviewed security model and costs automated version tracking.

No code carried over. The docs gap I found while verifying #97 is fixed there directly in ffaaa5b.

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