chore: initial commit

This commit is contained in:
Stevan Freeborn
2026-07-07 18:28:35 -05:00
commit 914afa469f
44 changed files with 3917 additions and 0 deletions
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# run_once_linux_configure-shell.sh
# Configures zsh as the default shell and sets up the shell environment.
# This script runs once (unless deleted from ~/.local/share/chezmoi).
set -euo pipefail
echo "==> Configuring shell environment (Linux)..."
# --- Set zsh as default shell ---
ZSH_PATH=$(which zsh 2>/dev/null || echo "")
if [ -z "$ZSH_PATH" ]; then
echo " ERROR: zsh is not installed. Run the package install script first."
exit 1
fi
CURRENT_SHELL=$(getent passwd "$USER" | cut -d: -f7)
if [ "$CURRENT_SHELL" != "$ZSH_PATH" ]; then
echo " Setting zsh as default shell..."
chsh -s "$ZSH_PATH"
echo " Default shell set to: $ZSH_PATH"
else
echo " zsh is already the default shell"
fi
# --- Set up Rust (cargo) in environment ---
CARGO_ENV="$HOME/.cargo/env"
if [ -f "$CARGO_ENV" ]; then
RUST_ZSHENV_LINE='. "$HOME/.cargo/env"'
ZSHENV="$HOME/.zshenv"
if ! grep -qF "$RUST_ZSHENV_LINE" "$ZSHENV" 2>/dev/null; then
echo "$RUST_ZSHENV_LINE" >> "$ZSHENV"
echo " Added Rust cargo env to .zshenv"
fi
fi
# --- Set up Go in environment ---
if [ -d "/usr/local/go" ]; then
GO_ZSHENV_LINE='export PATH=$PATH:/usr/local/go/bin'
ZSHENV="$HOME/.zshenv"
if ! grep -qF "go/bin" "$ZSHENV" 2>/dev/null; then
echo "$GO_ZSHENV_LINE" >> "$ZSHENV"
echo " Added Go to PATH in .zshenv"
fi
fi
echo "==> Shell configuration complete!"
echo " NOTE: Log out and back in (or restart your terminal) for shell changes to take effect."
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# run_once_setup-ssh-keys.sh.tmpl
# Pulls SSH PRIVATE keys from Bitwarden and writes them to ~/.ssh/.
#
# Public keys (.pub files) are committed directly to the repo in dot_ssh/
# and are applied automatically by chezmoi — no Bitwarden needed for those.
#
# Requires: bw (Bitwarden CLI) to be logged in and unlocked.
# To unlock Bitwarden before running chezmoi:
# export BW_SESSION=$(bw unlock --raw)
# chezmoi apply
set -euo pipefail
echo "==> Setting up SSH keys from Bitwarden..."
SSH_DIR="$HOME/.ssh"
mkdir -p "$SSH_DIR"
chmod 700 "$SSH_DIR"
write_key() {
local name="$1"
local bw_item_name="$2"
local key_path="$SSH_DIR/$name"
if [ -f "$key_path" ]; then
echo " Key already exists: $name (skipping)"
return
fi
echo " Fetching key: $bw_item_name -> $name"
# bw get notes "Item Name" returns the secure note content
bw get notes "$bw_item_name" 2>/dev/null > "$key_path" || {
echo " WARNING: Could not fetch '$bw_item_name' from Bitwarden. Skipping."
rm -f "$key_path"
return
}
if [ ! -s "$key_path" ]; then
echo " WARNING: Key content was empty for '$bw_item_name'. Removing."
rm -f "$key_path"
return
fi
chmod 600 "$key_path"
echo " Wrote: $key_path"
}
# --- Add your SSH keys below ---
# Format: write_key "filename_in_~/.ssh" "Bitwarden secure note name"
#
# Example:
# write_key "stevan@freeborn.cloud" "SSH Key - stevan@freeborn.cloud"
# write_key "ftp_stevanfreeborn_com" "SSH Key - ftp_stevanfreeborn_com"
# write_key "tangled" "SSH Key - tangled"
# write_key "tinker" "SSH Key - tinker"
# write_key "gitea.freeborn.cloud" "SSH Key - gitea.freeborn.cloud"
write_key "bit_bastion.key" "SSH Key - bit_bastion.key"
write_key "stevan@freeborn.cloud" "SSH Key - stevan@freeborn.cloud"
write_key "ftp_stevanfreeborn_com" "SSH Key - ftp_stevanfreeborn_com"
write_key "tangled" "SSH Key - tangled"
write_key "zenbook_tinker" "SSH Key - zenbook_tinker"
write_key "gitea.freeborn.cloud" "SSH Key - gitea.freeborn.cloud"
write_key "id_ed25519" "SSH Key - id_ed25519"
write_key "macbookair" "SSH Key - macbookair"
write_key "macbookpro" "SSH Key - macbookpro"
write_key "truenas" "SSH Key - truenas"
write_key "blog.stevanfreeborn.com_github_actions" "SSH Key - blog.stevanfreeborn.com_github_actions"
write_key "commands_github_actions" "SSH Key - commands_github_actions"
write_key "onspring_qa_playwright_reports_render" "SSH Key - onspring_qa_playwright_reports_render"
write_key "onx_graph_github_actions" "SSH Key - onx_graph_github_actions"
write_key "restapiplayground.stevanfreeborn.com_github_actions" "SSH Key - restapiplayground.stevanfreeborn.com_github_actions"
write_key "steves_bot_github_actions" "SSH Key - steves_bot_github_actions"
echo "==> SSH key setup complete!"
echo " NOTE: Store your SSH private keys as Bitwarden Secure Notes with the names listed above."
@@ -0,0 +1,37 @@
#!/usr/bin/env pwsh
# run_once_windows_configure-shell.ps1
# Installs PowerShell modules and configures the shell environment.
# This script runs once (unless deleted from ~/.local/share/chezmoi).
$ErrorActionPreference = "Continue"
Write-Host "==> Configuring PowerShell shell environment..." -ForegroundColor Cyan
# --- Install PowerShell modules ---
$modules = @("PSReadLine", "Terminal-Icons", "posh-git")
foreach ($module in $modules) {
if (-not (Get-Module -ListAvailable -Name $module)) {
Write-Host " Installing module: $module..." -ForegroundColor Yellow
Install-Module -Name $module -Force -Scope CurrentUser -SkipPublisherCheck -ErrorAction SilentlyContinue
Write-Host " Installed: $module" -ForegroundColor Green
} else {
Write-Host " Module already installed: $module" -ForegroundColor DarkGray
}
}
# --- Set PowerShell execution policy ---
$policy = Get-ExecutionPolicy -Scope CurrentUser
if ($policy -ne "RemoteSigned" -and $policy -ne "Unrestricted") {
Write-Host " Setting execution policy to RemoteSigned for CurrentUser..." -ForegroundColor Yellow
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
}
# --- Ensure profile directory exists ---
$profileDir = Split-Path $PROFILE -Parent
if (-not (Test-Path $profileDir)) {
New-Item -ItemType Directory -Path $profileDir -Force | Out-Null
Write-Host " Created profile directory: $profileDir" -ForegroundColor Green
}
Write-Host "==> Shell configuration complete!" -ForegroundColor Cyan
@@ -0,0 +1,68 @@
#!/usr/bin/env pwsh
# run_once_windows_setup-ssh-keys.ps1.tmpl
# Pulls SSH private keys from Bitwarden and writes them to ~/.ssh/.
#
# Public keys (.pub files) are committed directly to the repo in dot_ssh/
# and are applied automatically by chezmoi — no Bitwarden needed for those.
#
# Requires: bw (Bitwarden CLI) to be logged in and unlocked.
# To unlock Bitwarden before running chezmoi:
# $env:BW_SESSION = bw unlock --raw
# chezmoi apply
$ErrorActionPreference = "Stop"
Write-Host "==> Setting up SSH keys from Bitwarden..." -ForegroundColor Cyan
$SshDir = Join-Path $env:USERPROFILE ".ssh"
New-Item -ItemType Directory -Force $SshDir | Out-Null
function Write-Key($name, $bwItemName) {
$keyPath = Join-Path $SshDir $name
if (Test-Path $keyPath) {
Write-Host " Key already exists: $name (skipping)" -ForegroundColor DarkGray
return
}
Write-Host " Fetching key: $bwItemName -> $name" -NoNewline
try {
$content = bw get notes "$bwItemName" 2>&1
if ($LASTEXITCODE -ne 0) { throw $content }
[System.IO.File]::WriteAllText($keyPath, $content)
& icacls $keyPath /inheritance:r /grant "$env:USERNAME:(R,W)" /q 2>&1 | Out-Null
Write-Host "" -NoNewline
Write-Host " Wrote: $keyPath" -ForegroundColor Green
} catch {
Write-Host "" -NoNewline
Write-Host " WARNING: Could not fetch '$bwItemName' from Bitwarden. Skipping." -ForegroundColor Yellow
if (Test-Path $keyPath) { Remove-Item $keyPath -Force }
}
}
# --- Add your SSH keys below ---
# Format: Write-Key "filename_in_~/.ssh" "Bitwarden secure note name"
#
# Example:
# Write-Key "stevan@freeborn.cloud" "SSH Key - stevan@freeborn.cloud"
Write-Key "bit_bastion.key" "SSH Key - bit_bastion.key"
Write-Key "stevan@freeborn.cloud" "SSH Key - stevan@freeborn.cloud"
Write-Key "ftp_stevanfreeborn_com" "SSH Key - ftp_stevanfreeborn_com"
Write-Key "tangled" "SSH Key - tangled"
Write-Key "zenbook_tinker" "SSH Key - zenbook_tinker"
Write-Key "gitea.freeborn.cloud" "SSH Key - gitea.freeborn.cloud"
Write-Key "id_ed25519" "SSH Key - id_ed25519"
Write-Key "macbookair" "SSH Key - macbookair"
Write-Key "macbookpro" "SSH Key - macbookpro"
Write-Key "truenas" "SSH Key - truenas"
Write-Key "blog.stevanfreeborn.com_github_actions" "SSH Key - blog.stevanfreeborn.com_github_actions"
Write-Key "commands_github_actions" "SSH Key - commands_github_actions"
Write-Key "onspring_qa_playwright_reports_render" "SSH Key - onspring_qa_playwright_reports_render"
Write-Key "onx_graph_github_actions" "SSH Key - onx_graph_github_actions"
Write-Key "restapiplayground.stevanfreeborn.com_github_actions" "SSH Key - restapiplayground.stevanfreeborn.com_github_actions"
Write-Key "steves_bot_github_actions" "SSH Key - steves_bot_github_actions"
Write-Host "==> SSH key setup complete!" -ForegroundColor Cyan
Write-Host " NOTE: Store your SSH private keys as Bitwarden Secure Notes with the names listed above." -ForegroundColor Yellow
@@ -0,0 +1,213 @@
#!/usr/bin/env bash
# run_onchange_linux_install-packages.sh
# Installs packages on Ubuntu/Debian via apt and manual installs for tools
# not available (or outdated) in apt.
# This script re-runs whenever packages/linux.txt changes.
#
# Hash of packages file (forces re-run on change):
# {{ include "packages/linux.txt" | sha256sum }}
set -euo pipefail
# Ensure snap binaries are visible (chezmoi scripts may not inherit /snap/bin)
[ -d "/snap/bin" ] && export PATH="$PATH:/snap/bin"
CHEZMOI_SOURCE_DIR="{{ .chezmoi.sourceDir }}"
echo "==> Updating apt package index..."
sudo apt-get update -q
echo "==> Installing apt packages..."
apt_packages=$(grep -v '^#' "$CHEZMOI_SOURCE_DIR/packages/linux.txt" | grep -v '^$' | tr '\n' ' ')
# shellcheck disable=SC2086
sudo apt-get install -y $apt_packages
# --- Tailscale ---
if ! command -v tailscale &>/dev/null; then
echo "==> Installing Tailscale..."
curl -fsSL https://tailscale.com/install.sh | sh
else
echo "==> Tailscale already installed"
fi
# --- Go ---
GO_VERSION=$(curl -fsSL "https://go.dev/dl/?mode=json" | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['version'].lstrip('go'))")
if ! command -v go &>/dev/null || [[ "$(go version 2>/dev/null | awk '{print $3}' | tr -d 'go')" != "$GO_VERSION" ]]; then
echo "==> Installing Go $GO_VERSION..."
curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" -o /tmp/go.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf /tmp/go.tar.gz
rm /tmp/go.tar.gz
echo "export PATH=\$PATH:/usr/local/go/bin" >> "$HOME/.zshenv"
else
echo "==> Go already installed"
fi
# --- Rust ---
if ! command -v rustup &>/dev/null; then
echo "==> Installing Rust via rustup..."
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path
source "$HOME/.cargo/env"
else
echo "==> Rust already installed"
fi
# --- NVM (Node Version Manager) ---
if [ ! -d "$HOME/.nvm" ]; then
echo "==> Installing NVM..."
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
export NVM_DIR="$HOME/.nvm"
# shellcheck disable=SC1091
[ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh"
nvm install --lts
else
echo "==> NVM already installed"
fi
# --- Oh My Zsh ---
if [ ! -d "$HOME/.oh-my-zsh" ]; then
echo "==> Installing Oh My Zsh..."
RUNZSH=no CHSH=no sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
else
echo "==> Oh My Zsh already installed"
fi
# --- Oh My Zsh plugins ---
ZSH_CUSTOM="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"
if [ ! -d "$ZSH_CUSTOM/plugins/zsh-autosuggestions" ]; then
echo "==> Installing zsh-autosuggestions..."
git clone https://github.com/zsh-users/zsh-autosuggestions "$ZSH_CUSTOM/plugins/zsh-autosuggestions"
else
echo "==> zsh-autosuggestions already installed"
fi
if [ ! -d "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" ]; then
echo "==> Installing zsh-syntax-highlighting..."
git clone https://github.com/zsh-users/zsh-syntax-highlighting "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting"
else
echo "==> zsh-syntax-highlighting already installed"
fi
# --- Neovim ---
if ! command -v nvim &>/dev/null; then
echo "==> Installing Neovim (latest)..."
curl -fsSL https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.tar.gz -o /tmp/nvim.tar.gz
sudo tar -C /opt -xzf /tmp/nvim.tar.gz
sudo ln -sf /opt/nvim-linux-x86_64/bin/nvim /usr/local/bin/nvim
rm /tmp/nvim.tar.gz
else
echo "==> Neovim already installed"
fi
# --- lazygit ---
if ! command -v lazygit &>/dev/null; then
echo "==> Installing lazygit..."
LAZYGIT_VERSION=$(curl -s "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | grep -Po '"tag_name": "v\K[^"]*')
curl -fsSL "https://github.com/jesseduffield/lazygit/releases/download/v${LAZYGIT_VERSION}/lazygit_${LAZYGIT_VERSION}_Linux_x86_64.tar.gz" -o /tmp/lazygit.tar.gz
sudo tar xf /tmp/lazygit.tar.gz lazygit -C /usr/local/bin
rm /tmp/lazygit.tar.gz
else
echo "==> lazygit already installed"
fi
# --- watchexec ---
if ! command -v watchexec &>/dev/null; then
echo "==> Installing watchexec..."
WE_VERSION=$(curl -s "https://api.github.com/repos/watchexec/watchexec/releases/latest" | grep -Po '"tag_name": "v\K[^"]*')
curl -fsSL "https://github.com/watchexec/watchexec/releases/download/v${WE_VERSION}/watchexec-${WE_VERSION}-x86_64-unknown-linux-musl.tar.xz" -o /tmp/watchexec.tar.xz
tar -xJf /tmp/watchexec.tar.xz -C /tmp "watchexec-${WE_VERSION}-x86_64-unknown-linux-musl/watchexec"
sudo mv "/tmp/watchexec-${WE_VERSION}-x86_64-unknown-linux-musl/watchexec" /usr/local/bin/watchexec
rm -rf /tmp/watchexec.tar.xz "/tmp/watchexec-${WE_VERSION}-x86_64-unknown-linux-musl"
else
echo "==> watchexec already installed"
fi
# --- bottom (btm) ---
if ! command -v btm &>/dev/null; then
echo "==> Installing bottom (btm)..."
curl -fsSLO https://github.com/ClementTsang/bottom/releases/latest/download/bottom_x86_64-unknown-linux-musl.tar.gz
tar -xzf bottom_x86_64-unknown-linux-musl.tar.gz
sudo mv btm /usr/local/bin/
rm bottom_x86_64-unknown-linux-musl.tar.gz
else
echo "==> bottom already installed"
fi
# --- Bitwarden CLI ---
if ! command -v bw &>/dev/null; then
echo "==> Installing Bitwarden CLI..."
BW_VERSION=$(curl -s "https://api.github.com/repos/bitwarden/clients/releases" | grep -Po '"tag_name": "cli-v\K[^"]*' | head -1)
if [ -z "$BW_VERSION" ]; then
echo "WARNING: Could not determine Bitwarden CLI version, skipping install."
else
curl -fsSL "https://github.com/bitwarden/clients/releases/download/cli-v${BW_VERSION}/bw-linux-${BW_VERSION}.zip" -o /tmp/bw.zip
unzip -o /tmp/bw.zip -d /tmp/bw
sudo mv /tmp/bw/bw /usr/local/bin/bw
sudo chmod +x /usr/local/bin/bw
rm -rf /tmp/bw.zip /tmp/bw
fi
else
echo "==> Bitwarden CLI already installed"
fi
# --- Doppler ---
if ! command -v doppler &>/dev/null; then
echo "==> Installing Doppler..."
curl -Ls --tlsv1.2 --proto "=https" --retry 3 https://cli.doppler.com/install.sh | sudo sh
else
echo "==> Doppler already installed"
fi
# --- GitHub CLI ---
if ! command -v gh &>/dev/null; then
echo "==> Installing GitHub CLI..."
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt-get update -q
sudo apt-get install gh -y
else
echo "==> GitHub CLI already installed"
fi
# --- uv (Python package/project manager) ---
if ! command -v uv &>/dev/null; then
echo "==> Installing uv..."
curl -LsSf https://astral.sh/uv/install.sh | sh
else
echo "==> uv already installed"
fi
# --- Docker ---
if ! command -v docker &>/dev/null; then
echo "==> Installing Docker..."
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker "$USER"
echo "NOTE: Log out and back in for Docker group membership to take effect."
else
echo "==> Docker already installed"
fi
# --- .NET SDK (8 + 10) ---
# Uses Microsoft's official dotnet-install.sh script
DOTNET_INSTALL_DIR="$HOME/.dotnet"
DOTNET_SCRIPT=$(mktemp /tmp/dotnet-install-XXXXXX.sh)
curl -fsSL https://dot.net/v1/dotnet-install.sh -o "$DOTNET_SCRIPT"
chmod +x "$DOTNET_SCRIPT"
for DOTNET_CHANNEL in "8.0" "10.0"; do
MAJOR="${DOTNET_CHANNEL%%.*}"
DOTNET_VERSION_INSTALLED=$(find "$DOTNET_INSTALL_DIR/sdk" -maxdepth 1 -name "${MAJOR}.*" 2>/dev/null | head -1 || echo "")
if [ -z "$DOTNET_VERSION_INSTALLED" ]; then
echo "==> Installing .NET SDK $DOTNET_CHANNEL..."
"$DOTNET_SCRIPT" --channel "$DOTNET_CHANNEL" --install-dir "$DOTNET_INSTALL_DIR"
else
echo "==> .NET SDK $DOTNET_CHANNEL already installed"
fi
done
rm -f "$DOTNET_SCRIPT"
echo ""
echo "==> Linux package installation complete!"
@@ -0,0 +1,112 @@
#!/usr/bin/env pwsh
# run_onchange_windows_install-packages.ps1.tmpl
# Installs all packages from packages/windows.json via winget.
# This script re-runs whenever packages/windows.json changes.
#
# Hash of packages file (forces re-run on change):
# {{ include "packages/windows.json" | sha256sum }}
$ErrorActionPreference = "Continue"
Write-Host "==> Installing packages via winget..." -ForegroundColor Cyan
# Use the chezmoi source packages file directly
$sourcePackages = "{{ .chezmoi.sourceDir }}/packages/windows.json"
$sourcePackages = [System.IO.Path]::GetFullPath($sourcePackages)
if (-not (Test-Path $sourcePackages)) {
Write-Warning "packages/windows.json not found at: $sourcePackages"
exit 1
}
$packageList = Get-Content $sourcePackages -Raw | ConvertFrom-Json
$packages = $packageList.Sources[0].Packages
$total = $packages.Count
$installed = 0
$skipped = 0
$failed = 0
foreach ($pkg in $packages) {
$id = $pkg.PackageIdentifier
Write-Host " Checking $id..." -NoNewline
# Check if already installed
$check = winget list --id $id --exact --accept-source-agreements 2>$null | Select-String $id
if ($check) {
Write-Host " already installed" -ForegroundColor DarkGray
$skipped++
continue
}
# Install
Write-Host " installing..." -NoNewline
$result = winget install --id $id --exact --silent --accept-package-agreements --accept-source-agreements 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Host " done" -ForegroundColor Green
$installed++
} else {
Write-Host " FAILED (exit $LASTEXITCODE)" -ForegroundColor Red
$failed++
}
}
Write-Host ""
Write-Host "==> Package install complete: $installed installed, $skipped already present, $failed failed" -ForegroundColor Cyan
# =============================================================================
# Manual installs — tools not available on winget
# =============================================================================
$LocalBin = Join-Path $env:USERPROFILE ".local\bin"
if (-not (Test-Path $LocalBin)) {
New-Item -ItemType Directory -Force $LocalBin | Out-Null
}
# Ensure ~/.local/bin is in the user PATH
$userPath = [System.Environment]::GetEnvironmentVariable("PATH", "User")
if ($userPath -notlike "*$LocalBin*") {
[System.Environment]::SetEnvironmentVariable("PATH", "$userPath;$LocalBin", "User")
$env:PATH = "$env:PATH;$LocalBin"
Write-Host " Added $LocalBin to user PATH." -ForegroundColor Green
}
function Install-GitHubBinary {
param(
[string]$Name,
[string]$Repo,
[string]$AssetPattern,
[string]$BinaryName = "$Name.exe"
)
$BinPath = Join-Path $LocalBin $BinaryName
if (Test-Path $BinPath) {
Write-Host " $Name already installed" -ForegroundColor DarkGray
return
}
Write-Host " Installing $Name..." -NoNewline
try {
$release = Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest"
$asset = $release.assets | Where-Object { $_.name -like $AssetPattern } | Select-Object -First 1
if (-not $asset) { throw "No matching asset found for pattern: $AssetPattern" }
$tmpZip = Join-Path $env:TEMP "$Name-$($release.tag_name).zip"
$tmpDir = Join-Path $env:TEMP "$Name-$($release.tag_name)"
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $tmpZip -UseBasicParsing
Expand-Archive -Path $tmpZip -DestinationPath $tmpDir -Force
$exe = Get-ChildItem -Recurse $tmpDir -Filter $BinaryName | Select-Object -First 1
Copy-Item $exe.FullName $BinPath
Remove-Item $tmpZip -Force
Remove-Item $tmpDir -Recurse -Force
Write-Host " done" -ForegroundColor Green
} catch {
Write-Host " FAILED: $_" -ForegroundColor Red
}
}
Write-Host ""
Write-Host "==> Installing manual (non-winget) packages..." -ForegroundColor Cyan
# watchexec — https://github.com/watchexec/watchexec
Install-GitHubBinary -Name "watchexec" -Repo "watchexec/watchexec" -AssetPattern "*x86_64-pc-windows-msvc.zip"