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).