Skip to content

feat(helpers4-common): automatic git-config self-heal on every attach - #75

Merged
baxyz merged 2 commits into
mainfrom
feat/helpers4-common-self-heal
Sep 6, 2026
Merged

feat(helpers4-common): automatic git-config self-heal on every attach#75
baxyz merged 2 commits into
mainfrom
feat/helpers4-common-self-heal

Conversation

@baxyz

@baxyz baxyz commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Summary

First of two PRs (split from #74 — see below for why). This one is helpers4-common only.

The root cause

A client's own automatic behavior — VS Code copying ~/.gitconfig, SSH agent forwarding — happens outside any devcontainer Feature's control, verbatim, with no awareness that a path baked into the host's config might not resolve inside this particular container:

signingkey = ~/.ssh/id_baxyz_craft_ed25519.pub   # never existed in the container
[credential "https://gist.github.com"]
    helper = !/snap/gh/751/gh auth git-credential  # gh lives at /usr/local/bin/gh here

helpers4-common (1.0.1 → 1.1.0)

New postAttachCommand: git-config-self-heal.sh — automatic for every consumer, since every feature already depends on helpers4-common:

  • credential.helper (incl. per-URL scopes), gpg.program, gpg.ssh.program, core.editor: a shell-out to an absolute path that doesn't resolve here gets rewritten to the bare command name once a same-named binary is found on $PATH — bare, not a fresh absolute path, so it never goes stale again even if the tool moves on a future rebuild.
  • user.signingkey (gpg.format=ssh only): tries a same-basename file under ~/.ssh/~/.gnupg first, then recovers the public key live from a forwarded ssh-agent matched against user.email (ssh-add -L only, never touches private key material). On GitHub Codespaces, which doesn't forward a local ssh-agent at all — confirmed via GitHub's own community discussion, an official response states this explicitly — warns with a pointer to Codespaces secrets instead of silently doing nothing.

Also adds h4_detect_cloud_env to common.sh (Codespaces/Gitpod/DevPod/WSL detection).

A real bug found and fixed while testing: a multi-valued credential.helper (a blank "reset" entry followed by a real one — legitimate, common) makes plain git config <key> <value> refuse outright (exit 5). The first implementation swallowed that failure via 2>/dev/null || true while still reporting success. Fixed by targeting the one broken value via --replace-all <key> <new> <old-as-anchored-regex> and actually checking the exit status.

Why split from the original combined PR (#74)

#74 also included a dotfiles-sync refactor that depends on h4_detect_cloud_env. While testing, discovered that devcontainer features test --features dotfiles-sync . resolves dependsOn: ghcr.io/helpers4/devcontainer/helpers4-common:1 from the published GHCR registry, not this repo's local src/ tree — confirmed by inspecting the actual staged build content (missing git-config-self-heal.sh entirely) and the published manifest (docker manifest inspect ghcr.io/helpers4/devcontainer/helpers4-common:1 → still 1.0.1). Only the feature passed directly via --features gets fresh local content; a transitive dependency's CI test will fail if it needs behavior that isn't published yet — not a bug in the consumer, a limitation of testing interdependent changes within one PR. Landing helpers4-common here first, then dotfiles-sync in a follow-up once this is merged and released, sidesteps it entirely. Documented in AGENTS.md for next time.

#74 will be closed in favor of this PR + the dotfiles-sync follow-up.

Test plan

  • bash -n / jq empty on all changed scripts and manifests
  • Real Docker end-to-end: reproduced the exact reported .gitconfig (stale gh path + missing SSH signing key), ran the self-heal, confirmed both fixed; confirmed idempotence (clean second run); confirmed both warning paths (nothing resolvable locally, and the GitHub Codespaces-specific message)
  • test/helpers4-common/test.sh updated with functional assertions for the self-heal, passing in a real container
  • Verified the Codespaces-secrets doc URL referenced in the warning message resolves to a real, current GitHub docs page

🤖 Generated with Claude Code

baxyz and others added 2 commits September 6, 2026 21:12
…attach

A client's own automatic behavior — VS Code copying ~/.gitconfig,
SSH agent forwarding — happens outside any devcontainer Feature's
control, verbatim, with no awareness that a host-specific path might
not resolve inside this particular container: a credential.helper
shelling out to a snap-managed `gh` at a revision-pinned path that
doesn't exist here, or a gpg.format=ssh user.signingkey pointing at
a public key file that only ever existed on the host.

Adds postAttachCommand: git-config-self-heal.sh, which runs for
every helpers4 consumer automatically (no opt-in — every feature
already depends on helpers4-common since the dependsOn migration):

- credential.helper (incl. per-URL scopes), gpg.program,
  gpg.ssh.program, core.editor: when the value shells out to an
  absolute path that doesn't resolve here, rewritten to the bare
  command name once a same-named binary is found on $PATH. Bare, not
  a fresh absolute path: it never goes stale again even if the tool
  moves on a future rebuild.
- user.signingkey (gpg.format=ssh only): tries a same-basename file
  under ~/.ssh or ~/.gnupg first, then recovers the public key live
  from a forwarded ssh-agent matched against user.email (ssh-add -L
  only, never touches private key material). On GitHub Codespaces,
  which doesn't forward a local ssh-agent at all, warns with a
  pointer to Codespaces secrets and notes Codespaces signs
  GPG-format commits natively via its own managed proxy instead.

Also adds h4_detect_cloud_env to common.sh (Codespaces/Gitpod/DevPod/
WSL detection), shared with dotfiles-sync's own detection.

Found and fixed a real bug while testing against a multi-valued
credential.helper (a blank "reset" entry followed by a real one, a
legitimate and common pattern): plain `git config <key> <value>`
refuses outright (exit 5) the moment a key already has more than one
value, and the original _set implementation swallowed that failure
via `2>/dev/null || true` while still reporting success. Fixed by
targeting the one broken value specifically via `--replace-all <key>
<new> <old-as-anchored-regex>` and actually checking the exit status
before claiming a fix.

Verified end-to-end against the exact real .gitconfig snippet that
prompted this (credential.helper pointing at a since-moved snap gh
binary, gpg.format=ssh with a missing signingkey) in real Docker
containers, plus the multi-value regression case, both warning paths
(nothing resolvable, and the GitHub Codespaces-specific message),
and idempotence (a clean second run reports nothing left to fix).

Version bumped to 1.1.0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ting gotcha

Records why the automatic postAttachCommand self-heal lives in
helpers4-common rather than a dedicated feature (the one dependency
every feature already has, so it runs for every consumer
automatically), and the git config multi-value gotcha (`git config
<key> <value>` refuses outright the moment a key already has more
than one value) any future config-writing self-heal logic needs to
route around via --replace-all with an anchored value pattern.

Also documents a real testing-infrastructure discovery made while
building this: `devcontainer features test --features <consumer> .`
resolves a `dependsOn` reference from the published GHCR registry,
not this repo's local src/ tree — only the feature passed directly
via --features gets freshly-built local content. A consumer whose
test needs brand-new, not-yet-published behavior from a dependency
will fail CI until that dependency's own version bump actually merges
and releases first — not a bug in the consumer, a limitation of
testing interdependent changes within a single PR. This is why the
dotfiles-sync half of this work is a separate, follow-up PR rather
than bundled with this one.

Updates the feature table to helpers4-common 1.1.0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown

✅ PR Validation Passed

All checks passed!


📋 Pipeline Status

Job Status
🧾 Conventional Commits passing
🔖 Version Bump passing
🧪 Feature Tests passing
🐚 ShellCheck passing

🤖 Generated by @helpers4 CI • 2026-09-06

@baxyz
baxyz merged commit 24b7711 into main Sep 6, 2026
40 checks passed
@baxyz
baxyz deleted the feat/helpers4-common-self-heal branch September 6, 2026 21:21
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