Compare commits

...
Author SHA1 Message Date
Claude 10963c848b Fix Neovim not found after install by symlinking /usr/local/bin/nvim
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
2026-05-22 18:27:10 +00:00
Jason Ross e5448b71ba Merge pull request #9 from JMR-dev/claude/pulumi-repo-setup-failure-KEncj
Fix Pulumi repo setup failure by installing from official tarball
2026-05-22 13:14:27 -05:00
Claude 6661aba3d6 Fix Pulumi repo setup failure by installing from official tarball
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
2026-05-22 18:13:43 +00:00
Jason Ross 16f058f83c Merge pull request #8 from JMR-dev/claude/debian-arm64-install-errors-ekxEt
Fix five Debian arm64 install failures
2026-05-22 13:05:53 -05:00
+55 -26
View File
@@ -168,6 +168,7 @@ _ARCH_GO = {"x86_64": "amd64", "aarch64": "arm64"}
_ARCH_MINIKUBE = {"x86_64": "amd64", "aarch64": "arm64"}
_ARCH_DEB = {"x86_64": "amd64", "aarch64": "arm64"}
_ARCH_NVIM = {"x86_64": "x86_64", "aarch64": "arm64"}
_ARCH_PULUMI = {"x86_64": "x64", "aarch64": "arm64"}
def _url_format(template: str, version: Optional[str]) -> str:
"""Interpolate {version}, {arch}, {arch_go}, {os}, {os_go}, {os_zig}, {os_nvim}."""
@@ -370,6 +371,8 @@ def is_special_pkg_installed(pkg: str) -> bool:
return Path("/usr/local/bin/minikube").exists() or has_cmd("minikube")
if pkg == "bashtop":
return Path("/usr/local/bin/bashtop").exists() or (Path.home() / "bashtop").exists()
if pkg == "pulumi":
return Path("/opt/pulumi/pulumi").exists() or has_cmd("pulumi")
if pkg == "pipx":
return has_cmd("pipx")
if pkg == "poetry":
@@ -654,27 +657,6 @@ def setup_dotnet_repo() -> None:
"sudo apt-get update"
)
def setup_pulumi_repo() -> None:
if PKG_MGR == "dnf":
if _repo_file_exists("/etc/yum.repos.d/pulumi.repo"):
return
_write_dnf_repo(
"pulumi", "Pulumi",
"https://yum.releases.pulumi.com/",
"https://api.pulumi.com/releases/sdk/rpm-keyring.gpg",
)
elif PKG_MGR == "apt-get":
if _repo_file_exists("/etc/apt/sources.list.d/pulumi-releases.list"):
return
shell(
"curl -fsSL https://api.pulumi.com/releases/sdk/apt-keyring.gpg | "
"sudo gpg --dearmor -o /usr/share/keyrings/pulumi-releases-keyring.gpg && "
"echo 'deb [signed-by=/usr/share/keyrings/pulumi-releases-keyring.gpg] "
"https://apt.releases.pulumi.com/ stable main' | "
"sudo tee /etc/apt/sources.list.d/pulumi-releases.list > /dev/null && "
"sudo apt-get update"
)
_REPO_GROUPS: list[tuple[set[str], callable]] = [
(
{"containerd.io", "docker-buildx-plugin", "docker-ce-cli",
@@ -686,14 +668,13 @@ _REPO_GROUPS: list[tuple[set[str], callable]] = [
({"vivaldi-stable"}, setup_vivaldi_repo),
({"temurin-25-jdk"}, setup_temurin_repo),
({"dotnet-sdk-10.0"}, setup_dotnet_repo),
({"pulumi"}, setup_pulumi_repo),
]
# ── special package installers ────────────────────────────────────────────────
_SPECIAL_PKGS: set[str] = (
set() if IS_MACOS
else {"github-desktop", "zoom", "obsidian", "minikube", "bashtop", "pipx", "poetry"}
else {"github-desktop", "zoom", "obsidian", "minikube", "bashtop", "pipx", "poetry", "pulumi"}
)
# GUI apps — skipped by default (headless); included only when --gui is passed.
@@ -833,6 +814,46 @@ def _install_bashtop(_tmp: Path) -> None:
print(f" bashtop installed. Clone at {clone_dir}, binary at /usr/local/bin/bashtop")
def _install_pulumi(tmp: Path) -> None:
version = _fetch_text("https://www.pulumi.com/latest-version")
if not version:
err("Could not determine latest Pulumi version")
return
os_token = _OS_GO[OS]
arch_token = _ARCH_PULUMI[ARCH]
tarball = f"pulumi-v{version}-{os_token}-{arch_token}.tar.gz"
base = f"https://github.com/pulumi/pulumi/releases/download/v{version}"
dest = tmp / tarball
if not _download(f"{base}/{tarball}", dest):
return
checksums = _fetch_text(f"{base}/pulumi-{version}-checksums.txt")
if not checksums:
err("Could not fetch Pulumi checksums")
return
expected = next(
(line.split()[0] for line in checksums.splitlines() if line.endswith(tarball)),
None,
)
if not expected:
err(f"No checksum entry for {tarball}")
return
actual = _sha256_of(dest)
if actual != expected:
err(f"Pulumi SHA256 mismatch: expected {expected}, got {actual}")
return
print(" SHA256 OK")
install_dir = "/opt/pulumi"
print(f" Extracting Pulumi to /opt ...")
run(["mkdir", "-p", "/opt"], as_sudo=True, check=False)
run(["rm", "-rf", install_dir], as_sudo=True)
run(["tar", "-C", "/opt", "-xzf", str(dest)], as_sudo=True)
_append_profile_line("pulumi", f'export PATH="$PATH:{install_dir}"')
print(f" Pulumi {version} installed to {install_dir}")
def _install_pipx(_tmp: Path) -> None:
if not has_cmd("python3"):
err("Python 3 is not installed — cannot install pipx")
@@ -862,6 +883,8 @@ def install_special_pkg(pkg: str, tmp: Path) -> None:
_install_minikube(tmp)
elif pkg == "bashtop":
_install_bashtop(tmp)
elif pkg == "pulumi":
_install_pulumi(tmp)
elif pkg == "pipx":
_install_pipx(tmp)
elif pkg == "poetry":
@@ -982,7 +1005,7 @@ _DEFAULT_INSTALL_PATHS: dict[str, Path] = {
"zig": Path("/usr/local/bin/zig"),
"nvm": Path("~/.nvm"),
"pyenv": Path("~/.pyenv"),
"neovim": Path(f"/opt/nvim-{_OS_NVIM[OS]}-{_ARCH_NVIM[ARCH]}"),
"neovim": Path("/usr/local/bin/nvim"),
"oh-my-zsh": Path("~/.oh-my-zsh"),
}
@@ -1250,8 +1273,14 @@ def _install_neovim(pkg: CustomPackage, tmp: Path) -> None:
run(["rm", "-rf", install_dir], as_sudo=True)
run(["tar", "-C", "/opt", "-xzf", str(dest)], as_sudo=True)
_append_profile_line("neovim", f'export PATH="$PATH:{install_dir}/bin"')
print(f" Neovim installed to {install_dir}")
# Symlink into /usr/local/bin so `nvim` is on PATH for every shell type
# (login, interactive, scripts) without relying on /etc/profile.d, which
# is only sourced by login shells — terminal emulators typically launch
# non-login interactive shells.
run(["mkdir", "-p", "/usr/local/bin"], as_sudo=True, check=False)
symlink = "/usr/local/bin/nvim"
run(["ln", "-sf", f"{install_dir}/bin/nvim", symlink], as_sudo=True)
print(f" Neovim installed to {install_dir}, symlinked at {symlink}")
def _clone_nvim_config() -> None: