Two bugs in pnpmEnvPrefix:
1. pnpm setup configures global-bin-dir as $PNPM_HOME/bin, not
$PNPM_HOME. We were only prepending $PNPM_HOME to PATH, so pnpm
still complained 'configured global bin directory ... is not in
PATH' and refused to install.
2. The bash -c command was wrapped in double quotes, which caused the
OUTER sh to expand $PNPM_HOME, $PATH, $HOME before bash ever
saw them. PNPM_HOME was empty in the outer shell, so the resulting
PATH was ':$PATH'.
Fix: wrap bash -c argument in single quotes so the inner bash does
all variable expansion; prepend both $PNPM_HOME/bin and $PNPM_HOME
to PATH for safety.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tests that exercise error paths in errLog/warn were causing
GitHub Actions to annotate test output with 'Error: R] ...'
(GHA eats '[ERRO' from '[ERROR]' lines and surfaces the rest as
workflow errors), making real failures hard to distinguish from
expected behavior.
- issues.go: route logIssue output through issueLogWriter (io.Writer,
defaults to os.Stdout). errorCount/issues/notices recording is
unchanged so tests still assert via errorCount.
- testmain_test.go: TestMain sets issueLogWriter = io.Discard for
the whole test binary.
- issues_test.go: TestIssuesLogging temporarily redirects
issueLogWriter to a bytes.Buffer (replacing os.Pipe stdout
capture).
go test -v ./... now contains zero stray '[ERROR]' lines; real
test failures remain visible and 'go test' still exits non-zero
on failure, failing the workflow.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Integration tests now run the full bootstrap (system + flatpak + custom +
AI + VM packages) instead of '--only custom --no-vm --no-ai', so that
custom-package prerequisites (zsh, gh, etc.) installed via SystemPackages
are actually available.
- Fix 'pnpm setup' failing with ERR_PNPM_UNKNOWN_SHELL in CI containers
by exporting SHELL=/bin/bash before invoking it.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add env: GITHUB_TOKEN to the Linux workflow step, then read it in
ci/main.go and inject it into each test container via WithSecretVariable
(for both GITHUB_TOKEN and GH_TOKEN). This prevents 403 rate-limit
errors on GitHub API calls (neovim/nvm releases) and authenticates
gh CLI for gh extension install inside the containers.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Unauthenticated GitHub API calls share the runner IP (60 req/hour limit).
net.go already uses GITHUB_TOKEN as a Bearer token when present.
gh CLI also needs GH_TOKEN to authenticate for gh extension install.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Track error count in issues.go alongside the existing issues slice.
Add hasErrors() helper. In main.go, call osExit(1) after writeRunLog()
if any [ERROR] entries were recorded, so CI steps correctly fail when
errors occur (e.g. GitHub API 403 rate-limit hits in the macOS job).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add explicit token inputs to the checkout and softprops/action-gh-release
steps so the GitHub token is wired through instead of relying on implicit
defaults. The workflow-level contents: write permission already scopes
the token correctly for tag/release creation.
Pushing a tag matching v* (or running the workflow manually with an
existing tag) cross-compiles the four supported targets via
`make build-all`, generates a SHA256SUMS file, and publishes a GitHub
release with all binaries attached.
Replace the Python bootstrap script with a Go implementation that
cross-compiles to native binaries for Linux and macOS on x86_64 and
aarch64. The Go port preserves all sections of the original (system
packages, optional Flatpak GUI apps, custom downloads, and the macOS
firecracker VM bridge) and adds first-class pacman support so the tool
works on Arch-family distros alongside Debian, RHEL, and macOS.
A Makefile produces a host binary via `make build` and the full
four-target matrix under dist/ via `make build-all`.
When python3-full is not installed on Debian/Ubuntu (common with Python
3.13), the _decimal C extension fails to import, crashing any pip
invocation with RuntimeError. Add _python3_decimal_ok() to probe for
this, and _fix_python3_decimal() to install python3-full (apt) or
python3-libs (dnf). _install_pip() now calls these before attempting
any ensurepip/pip operations so the error is fixed automatically rather
than producing an unrecoverable crash.
https://claude.ai/code/session_01Lrg3UV7AJN9KRXyTTWGgZi
`run()` and `shell()` now default to a 30-minute cap (generous enough for
heavy apt/brew installs and large downloads, bounded enough to catch a
true hang). Callers can override per-call.
All remaining direct subprocess.run sites have explicit timeouts sized to
the work they do: short caps for read-only probes (rpm, dpkg, brew list,
flatpak info, gh auth status, xcode-select -p, sysctl, VBoxManage), a
60-minute cap for the pyenv Python compile, a 15-minute cap for the
interactive `gh auth login` browser flow, and ConnectTimeout+30s for the
VM SSH helper. A small `_probe()` wrapper centralizes the probe pattern.
On timeout the bootstrap now warns and either fails the check gracefully
or returns rc=124, rather than blocking indefinitely.
https://claude.ai/code/session_01447pnRM4ogyxs5tVCUNDZW
A hung `python3 -m pip --version` inside `_pip_installed()` could block
the bootstrap indefinitely, since `subprocess.run` was called without a
timeout. Treat a timeout (or a missing python3) as "pip not installed"
so the bootstrap proceeds to install it rather than hanging.
https://claude.ai/code/session_01447pnRM4ogyxs5tVCUNDZW
On re-runs, ~/.config/nvim is moved aside to ~/.config/nvim-1
(or nvim-2, nvim-3, ... — first free number) and a fresh clone is
created. End-of-run notices report the backup path so it's visible
after the rest of the bootstrap output scrolls by.
Previously rmtree'd ~/.config/nvim on every full bootstrap run, destroying
any user edits, plugin state, and undo history. Now skips when the
directory is already a clone of the expected repo, and refuses to touch
unfamiliar contents (different remote, or non-git directory).
The PATH line written to /etc/profile.d/neovim.sh was only sourced by
login shells via /etc/profile. zsh — set as the default shell by this
script — uses /etc/zsh/zprofile which sources /etc/profile only for
login shells, and terminal emulators typically launch non-login
interactive shells. The result on Debian 13 ARM64 (and other distros):
nvim installed correctly to /opt/nvim-linux-arm64 but was not on PATH
in a normal terminal session.
Symlink the binary into /usr/local/bin/nvim instead — that directory
is always on the default PATH on Linux and macOS (Apple Silicon and
Intel) regardless of shell type. The Zig install already follows this
pattern. Also switch the install-detection probe to the symlink so it
verifies an actually-callable nvim, not just the extracted directory.
https://claude.ai/code/session_01VP9hCLH1c5F5iv468jSkqs
Pulumi does not publish apt or yum repositories — the URLs the setup
function referenced (api.pulumi.com/releases/sdk/{apt,rpm}-keyring.gpg
and {apt,yum}.releases.pulumi.com) return 404 / do not resolve, which
broke `bootstrap_environment.py` on every fresh Linux run.
Replace `setup_pulumi_repo` with a special-package installer that
downloads the official GitHub release tarball, verifies the SHA256
against the published checksums file, extracts to /opt/pulumi, and
exposes the binaries via PATH — the same pattern used for Neovim.
macOS continues to install Pulumi via brew.
https://claude.ai/code/session_018L1gFoc8L3CYqLE2wNjpy6
- dotnet-sdk-10.0: add setup_dotnet_repo() to register the Microsoft apt
feed (packages.microsoft.com) before attempting the install
- lua: map to lua5.4 in apt-get overrides (Debian has no unversioned lua pkg)
- pulumi: add setup_pulumi_repo() to register apt.releases.pulumi.com before
attempting the install; add dnf/yum variant too
- qemu: map to qemu-system in apt-get overrides (Debian meta-package name)
- python3 -m ensurepip: Debian intentionally strips ensurepip from the system
Python package; fall back to apt-get install python3-pip automatically
Also add libnsl2 → libnsl-dev mapping for Debian (bonus pyenv build fix).
https://claude.ai/code/session_017KuwqSq7nCp2iWiTeaNivn
- Inverts the GUI opt-out model: GUI packages and Flatpak are now skipped
by default (headless-friendly), and --gui opts in to installing them.
Removes --no-gui entirely.
- Adds lazygit and pulumi to the default system package list.
https://claude.ai/code/session_01CsTAu2SNhE5ZAQm3RnVwp3