Skip to content

Backport envy-nx improvements: fixes, certs, tests, CI - #246

Merged
szymonos merged 11 commits into
mainfrom
chore/port-envy-nx-fixes-a
May 1, 2026
Merged

Backport envy-nx improvements: fixes, certs, tests, CI#246
szymonos merged 11 commits into
mainfrom
chore/port-envy-nx-fixes-a

Conversation

@szymonos

@szymonos szymonos commented May 1, 2026

Copy link
Copy Markdown
Owner

Summary

Backports parallel-functionality improvements from envy-nx (the cross-platform Nix descendant in rapid development) and lays down infrastructure to make future backports straightforward.

  • 6 code commits: bug fixes, perf improvements, user-scope cert bundle, POSIX guard, WSL safe.directory wiring
  • 5 tooling commits: bats/Pester unit tests, two CI workflows, BACKPORT.md playbook, shfmt + editorconfig + Makefile QoL

Commits

# SHA Subject
1 76b7dfd fix: backport bug fixes and small improvements from envy-nx
2 b3a2b49 perf: speed up Update-GitRepository and improve Invoke-GhRepoClone
3 4ca5363 feat(certs): user-scope cert bundle for tools that bypass system trust
4 67c8cce test: port unit tests, runners, and zsh-compat hook from envy-nx
5 58649ce fix: POSIX-guard bash-syntax /etc/profile.d/ files
6 cf3ba30 fix(wsl): register Windows-mount repo paths as git safe.directory
7 e126af8 ci: add unit tests and labeled integration test workflows
8 f53b87d docs: add BACKPORT.md, align-tables hook, gremlins auto-fix
9 84e98fc ci: add shfmt pre-commit hook + reformat existing shell scripts
10 ba21a57 chore(make): port MITM cert env, HOOK=id selector, and hooks list
11 247e751 fix(editorconfig): include bats and bash in 2-space indent group

Bug classes caught (by ports or by tests added in this PR)

  • Get-LogLine $ctx-leak (PR A) - new Pester test asserts it works without $ctx in caller scope
  • Join-Str missing DefaultParameterSetName - caught when running ported test against this repo's code
  • fix_gcloud_certs.sh missing ;; before esac - caught by shfmt
  • /etc/profile.d/{aliases_git,functions}.sh failing under dash - caught by dash -c '. <file>' probe in test_linux.yml

Validation done locally

  • make lint and make lint-all both green (13 hooks)
  • make test green: 22 bats + 42 Pester
  • dash -c '. <file>' clean for all 5 /etc/profile.d/ files
  • All shell config files defined functions correctly under bash login shell

Test plan

  • CI: Unit Tests workflow passes (bats + Pester on every PR)
  • CI: Linux Integration Test passes (Docker build + dash-source verification - fired by test:integration label, added below)
  • CI: Preflight Checks passes (pre-commit hooks)
  • Manual: re-run wsl/wsl_setup.ps1 <distro> against an existing distro and verify:
    • git config --global --get-all safe.directory includes /mnt/c/Users/.../source/repos/* patterns
    • /etc/profile.d/{aliases_git,functions}.sh source cleanly under dash -c '. <file>'
    • ~/.config/certs/ca-custom.crt is populated when -AddCertificate flag was used; cert env vars present in user's bash login shell (echo \$NODE_EXTRA_CA_CERTS)
  • Manual: from inside the same distro, run ~/source/repos/szymonos/envy-nx/nix/setup.sh and confirm the Determinate Nix installer's sh -lc self-test no longer fails on aliases_git.sh parsing

Documented follow-ups (not in this PR)

  • Pester tests for Update-GitRepository / Invoke-GhRepoClone (commit 2 has no unit coverage)
  • Bats tests for setup_profile_user.sh cert env-var wiring
  • Auto-detect MITM proxy in wsl_setup.ps1 and call wsl_certs_add.ps1 automatically (Phase 2 - new functionality)
  • Add Dockerfile.alpine to test_linux.yml matrix for distro coverage
  • Optional: validate_docs_words.py + cspell hook for markdown spellcheck

🤖 Generated with Claude Code

szymonos added 10 commits May 1, 2026 20:42
- Get-LogLine: replace caller-scoped $ctx with $LogContext parameter
  (the function broke unless called from Show-LogContext's frame).
- linux_setup.sh: pass -WarningAction SilentlyContinue to
  Invoke-GhRepoClone so $cloned doesn't capture warning text and
  fail the subsequent integer comparison.
- upgrade_system.sh: quiet apt-get update/dist-upgrade with -qq.
- ConvertTo-Cfg: widen $OrderedDict parameter type to
  System.Collections.IDictionary so hashtables and other dictionaries
  pipe in cleanly.
- wsl_setup.ps1: read default-distro gh config via
  `cat ~/.config/gh/hosts.yml` instead of re-running setup_gh_https.sh
  with root on the default distro just to get its config back.
- Update-GitRepository: resolve upstream tracking ref in a single
  `git rev-parse --abbrev-ref --symbolic-full-name @{upstream}`
  instead of `git remote` + `git branch --show-current`. Add a
  `git ls-remote --heads` pre-check so `git fetch --tags --prune
  --prune-tags --force` is skipped when the remote tip already
  matches the local tracking ref - cuts seconds off wsl_setup.ps1
  startup on slow disks (fetch always rewrites FETCH_HEAD and
  packed-refs even on no-op). 0/1/2 return contract preserved.
- Invoke-GhRepoClone: prefer SSH when a key is configured (probed
  once via `ssh -T git@github.com`); fall back to HTTPS on SSH
  clone failure with an explicit warning showing the URL and error.
Centralizes MITM proxy / private CA cert handling around a single
user-scope bundle at ~/.config/certs/ca-{custom,bundle}.crt that all
HTTP-aware tools can pick up via env vars - covers tools that ignore
the system CA store (Node.js, Python certifi, uv).

Producers (write to ~/.config/certs/):
- wsl/wsl_certs_add.ps1: after the existing system-store update,
  also writes ca-custom.crt (PEM bundle of intercepted MITM proxy
  certs only) and a ca-bundle.crt symlink to the system bundle.
- functions.sh: new `cert_intercept [host ...]` shell function pulls
  the TLS chain from a host via openssl s_client, skips the leaf cert,
  dedups against existing serials, appends intermediates/roots to
  ca-custom.crt. Lets users capture proxy certs from inside the
  distro without going via the Windows host.

Consumers:
- functions.sh: `fixcertpy` rewrite - prefers the user bundle when
  present, falls back to system cert dirs; accepts explicit cacert.pem
  paths as args; uses `find` instead of glob (zsh-safe); replaces
  bash-4 assoc-array dedup with portable space-separated string;
  Alpine no-op; clearer messages on permission failures.
- functions.sh: `sysinfo` zsh-compat fix - replaces mapfile + numeric
  array indexing (0-based in bash, 1-based in zsh) with `read` so the
  function works under zsh; sed `\s*` → `[[:space:]]*` for BSD sed.
- New .assets/config/bash_cfg/certs.sh: exports NODE_EXTRA_CA_CERTS
  + UV_SYSTEM_CERTS when ca-custom.crt exists, REQUESTS_CA_BUNDLE +
  SSL_CERT_FILE when ca-bundle.crt exists.
- setup_profile_allusers.sh: installs certs.sh to /etc/profile.d/.
- setup_profile_user.{sh,zsh}: source /etc/profile.d/certs.sh from
  ~/.bashrc and ~/.zshrc.
- setup_profile_user.sh: also writes ~/.vscode-server/server-env-setup
  so VS Code Server extensions see the cert env vars without sourcing
  ~/.bashrc (which the server doesn't do on launch).
Adds change-aware bats/Pester test infrastructure plus a static
zsh-compat lint, all backported from envy-nx and adapted to this
repo's module layout (InstallUtils/SetupUtils, bash_cfg/).

Pester (5 files, 42 tests) — coverage for parallel functions:
- GetLogLine.Tests.ps1 (SetupUtils/logs.ps1) — explicit assertion
  that Get-LogLine works without $ctx in caller scope (the bug
  fixed in PR A).
- ConvertCfg.Tests.ps1 (SetupUtils/common.ps1) — covers the
  IDictionary parameter-type widening from PR A; sections,
  comments, header preservation, hashtable input, roundtrip.
- ConvertPEM.Tests.ps1 (SetupUtils/certs.ps1) — covers the PEM
  helpers used by the wsl_certs_add.ps1 cert-bundle write in PR C.
- InvokeCommandRetry.Tests.ps1 (InstallUtils/common.ps1).
- JoinStr.Tests.ps1 (InstallUtils/common.ps1).

Bats (2 files, 22 tests) — coverage for shell functions:
- test_functions.bats — sysinfo / fixcertpy / cert_intercept from
  PR C: PEM bundle parsing, dedup logic, fall-back paths.
- test_fixcertpy_discovery.bats — focused tests for fixcertpy's
  pip-show parsing patterns.

Hooks:
- run_pester.py — parses .Tests.ps1 dot-source lines to map source
  → tests, runs only the relevant tests for staged files.
- run_bats.py — same change-aware contract for bats.
- check_zsh_compat.py — flags bare `name() {`, numeric array
  subscripts (1-based in zsh vs 0-based in bash), unquoted glob
  for-loops (zsh nomatch abort), unguarded BASH_SOURCE / compgen /
  COMP_WORDS. Inline `# zsh-ok` suppression. Wired to
  .assets/config/bash_cfg/*.sh in pre-commit-config.

Pre-commit / Makefile:
- .pre-commit-config.yaml: adds bats-tests, pester-tests,
  check-zsh-compat local hooks.
- Makefile: adds test / test-unit / test-bats / test-pester
  targets.

Also fixes a Join-Str bug caught by the new tests:
- modules/InstallUtils/Functions/common.ps1: add
  DefaultParameterSetName='None' so Join-Str works without
  -SingleQuote/-DoubleQuote (matches the no-quote branch in the
  function body, which was previously unreachable).
setup_profile_allusers.sh installs aliases_git.sh and functions.sh
into /etc/profile.d/. On Debian/Ubuntu /bin/sh is dash, and dash
sources /etc/profile.d/*.sh from /etc/profile when invoked as a
login shell - notably by the Determinate Nix installer's self-test
(`sh -lc <build>`). dash chokes on the bash `function` keyword:

  sh: 2: /etc/profile.d/aliases_git.sh: function: not found
  sh: 4: /etc/profile.d/aliases_git.sh: Syntax error: "}" unexpected

Surfaced when running envy-nx's nix/setup.sh inside a WSL distro
that this repo had previously provisioned.

Adds a POSIX-portable guard at the top of both files that no-ops
under dash but proceeds under bash and zsh:

  [ -n "${BASH_VERSION:-}${ZSH_VERSION:-}" ] || return 0

Verified end-to-end with `dash -c '. <file>'` for all five files
in /etc/profile.d/ (aliases.sh, aliases_git.sh, aliases_kubectl.sh,
certs.sh, functions.sh) - all source cleanly. bash regression-check
confirms all eight functions still load. 42/42 Pester + 22/22 bats
still pass.
When WSL is set up with this repo and the user later runs scripts
from /mnt/c/Users/<them>/source/repos/... (a common pattern when
also using the repos from Windows), every git invocation surfaces:

  fatal: detected dubious ownership in repository at '/mnt/c/...'

The .git dir's owner UID (Windows side) doesn't match the WSL
user's UID, so git refuses to operate. Surfaced by envy-nx's
nix/setup.sh self-test running git from /mnt/c/.

Adds the per-user safe.directory entries during WSL provisioning
in wsl_setup.ps1's GitHub setup region. Two globs cover the
typical layouts: source/repos/<repo> and source/repos/<org>/<repo>.
Writes to the WSL user's ~/.gitconfig (per-user, not system-wide),
guarded with grep -qFx to stay idempotent on re-runs.
tests.yml (every PR + push to main, ~2 min):
Runs `make test` (bats + Pester) on ubuntu-latest. Catches module
regressions (logs.ps1, common.ps1, certs.ps1) and shell-helper
regressions (sysinfo, fixcertpy, cert_intercept) before merge.
Pester ships with the runner's pwsh; bats installed via apt.

test_linux.yml (label-gated, ~10 min):
Builds .assets/docker/Dockerfile end-to-end (which runs
linux_setup.sh) and runs four verification probes against the
resulting image:
  - pwsh on PATH
  - oh-my-posh on PATH
  - /etc/profile.d/*.sh source cleanly under dash (regression
    check for the POSIX-guard fix on aliases_git.sh / functions.sh)
  - bash login shell exposes sysinfo / fixcertpy / cert_intercept /
    git_current_branch / git_resolve_branch as functions

Triggers (matching envy-nx's test:integration pattern):
  - workflow_dispatch (manual run from Actions tab)
  - PR labeled with `test:integration`
  - push to a PR that already has the `test:integration` label

Verification steps use continue-on-error and a final summary so a
single failure doesn't mask the rest of the picture.
BACKPORT.md is the playbook an agent (human or AI) follows when asked
to "backport recent envy-nx improvements." envy-nx is in rapid
(daily-ish) development; we deliberately don't rely on an upstream
marker on commits or CHANGELOG entries - every candidate is evaluated
at runtime against the rules in the doc.

Sections:
  - Last ported through (the cut-off SHA/tag, bumped per backport PR)
  - Procedure (9-step recipe from baseline through PR open)
  - Eligibility rules (Port / Skip / Investigate buckets)
  - Nix coupling signals (concrete patterns to grep for)
  - Path mapping (envy-nx layout -> this repo's layout, with skip cells)
  - Skip-list (paths that are never eligible)
  - Hard rules (commit-level invariants)
  - Module layout difference (vendored vs runtime-cloned ps-modules)
  - Examples (5 concrete walkthroughs from the bootstrap PR:
    pure copy, substantive port, surgical line fix, skip,
    investigate-and-extract)

Also ports tests/hooks/align_tables.py from envy-nx (mechanical
markdown table column alignment, MD060 compliance) and wires it into
.pre-commit-config.yaml. Stdlib-only, ~130 lines. Idempotent on
already-aligned tables.

Upgrades tests/hooks/gremlins.py to envy-nx's auto-fixing version
(byte-identical): silently fixes dashes, smart quotes, fancy spaces,
ZWS/ZWJ, ellipsis, soft hyphens; exits non-zero only on truly
unfixable chars (currently just middle dot). Pre-commit's
modified-files detection blocks the commit so the user can review
the auto-fixes before re-staging.
Adds scop/pre-commit-shfmt v3.13.1-1 to .pre-commit-config.yaml,
matching envy-nx's config (excludes .zsh files, no extra args -
relies on .editorconfig's `indent_size = 2` for shell files).

Bulk-applied to the 6 tracked .sh files that had drift:
  - .assets/provision/fix_gcloud_certs.sh: add missing `;;` before
    `esac` on the opensuse case branch (latent bug that would have
    caused fall-through into the next case if the case order ever
    changed; shfmt caught it as a structural fix)
  - .assets/provision/install_docker.sh: tighten `<<EOF >file`
    spacing (cosmetic)
  - .assets/provision/setup_gh_repos.sh: tighten `<<<"$x"` spacing
  - .assets/provision/{setup_gh_ssh,setup_gnome,setup_ssh}.sh:
    drop double blank lines after `set -euo pipefail`
Three additive Makefile improvements from envy-nx:

1. MITM proxy cert env in the prologue:
   - PREK_NATIVE_TLS=1 tells prek to use OpenSSL's native CA store
     (so it trusts the system trust anchors, including any custom
     ones installed via wsl_certs_add.ps1)
   - NODE_EXTRA_CA_CERTS is exported when ~/.config/certs/ca-custom.crt
     exists, so prek-managed Node hooks (markdownlint-cli2, cspell)
     work behind a MITM proxy
   Pairs with the user-scope cert bundle added in PR C.

2. HOOK=id parameter on lint/lint-diff/lint-all:
     make lint HOOK=shellcheck    # run a single hook
   Bare `make lint` still runs the full chain. lint-diff converted
   to multi-line if/else so the conditional reads correctly with
   the new $(HOOK) interpolation.

3. New `make hooks` target: lists hook IDs alphabetically by
   parsing .pre-commit-config.yaml. Useful companion to HOOK=id
   ("which hooks can I single out?").

Skipped from envy-nx: `mkdocs-serve` (no docs site), `release`/
`egsave`/`test-nix` targets (no release pipeline / no nix tests),
the `uv run` prefix on prek (this repo doesn't have uv-managed
prek), the `hooks-install`/`hooks-remove` rename (would break the
existing `make install` / `make upgrade` contract).
@szymonos szymonos added the test:integration Trigger integration test (test_linux.yml) on this PR label May 1, 2026
editorconfig: include bats and bash extensions in the 2-space indent
group so shfmt formats them correctly (matches envy-nx). Without this,
shfmt fell back to its default tab indent on .bats files in lint-all.

BACKPORT.md: bump "Last ported through" pointer to envy-nx 1.4.0
(commit c859bba, tagged v1.4.0, released 2026-05-01). The 1.4.0
release was reviewed against BACKPORT.md's eligibility rules - nothing
new to port. The release is overwhelmingly nix-only work (manifest-
driven nx CLI surface, four parity hooks defending it, nx_doctor
refactor, nx.sh family-file split). The two parallel-functionality
items (Update-GitRepository ls-remote pre-check, smarter
check-zsh-compat BASH_SOURCE detection) are respectively already
ported in PR B (b3a2b49) and not currently triggered by any wired
file in this repo.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:integration Trigger integration test (test_linux.yml) on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant