Created
July 2, 2026 10:11
-
-
Save JesperDramsch/c82d8be11e665e0f514d2d5a79b1b6d0 to your computer and use it in GitHub Desktop.
Setup scripts for chezmoi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #Requires -Version 5.1 | |
| <# | |
| .SYNOPSIS | |
| Bootstrap shim — the only public entry point. | |
| .DESCRIPTION | |
| The dotfiles repo is private, so the real bootstrap script can't be | |
| served from raw.githubusercontent.com. | |
| What it does: | |
| 1. Sets ExecutionPolicy for CurrentUser | |
| 2. Installs scoop, then gh via scoop (gh is scoop-managed in this repo) | |
| 3. Signs in to GitHub via the browser device flow (no PAT to type) | |
| 4. Fetches bootstrap/bootstrap.ps1 from the private repo via the | |
| GitHub API and runs it | |
| The real bootstrap never leaves the private repo. | |
| #> | |
| $ErrorActionPreference = 'Stop' | |
| $owner = 'jesperdramsch' | |
| Write-Host "`n=== dotfiles stage-0 bootstrap ===" -ForegroundColor Cyan | |
| # The repo name stays out of the published shim — ask for it instead. | |
| # A bare name is prefixed with the owner; a full owner/name slug passes through. | |
| $repoInput = (Read-Host "Dotfiles repo (name under $owner/, or full owner/name slug)").Trim() | |
| if (-not $repoInput) { | |
| Write-Error "No repo given" | |
| exit 1 | |
| } | |
| $repo = if ($repoInput -match '/') { $repoInput } else { "$owner/$repoInput" } | |
| # --- 1. ExecutionPolicy (scoop's installer needs RemoteSigned) --- | |
| $policy = Get-ExecutionPolicy -Scope CurrentUser | |
| if ($policy -eq 'Undefined' -or $policy -eq 'Restricted') { | |
| Write-Host "[1/4] Setting ExecutionPolicy to RemoteSigned..." | |
| Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force | |
| } else { | |
| Write-Host "[1/4] ExecutionPolicy already set: $policy" | |
| } | |
| # --- 2. Scoop + gh --- | |
| if (-not (Get-Command scoop -ErrorAction SilentlyContinue)) { | |
| Write-Host "[2/4] Installing scoop..." | |
| Invoke-RestMethod get.scoop.sh | Invoke-Expression | |
| } else { | |
| Write-Host "[2/4] Scoop already installed" | |
| } | |
| if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { | |
| Write-Host " Installing gh via scoop..." | |
| scoop install gh | |
| if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { | |
| Write-Error "gh not found on PATH after scoop install" | |
| exit 1 | |
| } | |
| } | |
| # --- 3. GitHub auth (browser device flow) --- | |
| gh auth status --hostname github.com 2>&1 | Out-Null | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Host "[3/4] Signing in to GitHub (browser will open, enter the one-time code)..." -ForegroundColor Yellow | |
| # https so the token also covers the private-repo clone during bootstrap; | |
| # post-bootstrap docs cover switching to the SSH alias. | |
| gh auth login --hostname github.com --git-protocol https --web | |
| gh auth status --hostname github.com 2>&1 | Out-Null | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Error "GitHub authentication failed or was cancelled" | |
| exit 1 | |
| } | |
| } else { | |
| Write-Host "[3/4] Already signed in to GitHub" | |
| } | |
| # --- 4. Fetch and run the real bootstrap from the private repo --- | |
| Write-Host "[4/4] Fetching bootstrap.ps1 from $repo..." | |
| $script = gh api -H 'Accept: application/vnd.github.raw' "repos/$repo/contents/bootstrap/bootstrap.ps1" | |
| if ($LASTEXITCODE -ne 0 -or -not $script) { | |
| Write-Error "Failed to fetch bootstrap.ps1 from $repo (check repo access for the signed-in account)" | |
| exit 1 | |
| } | |
| Invoke-Expression ($script -join "`n") | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Bootstrap shim (Linux / WSL2 / macOS / HPC) — the only public entry point. | |
| # | |
| # The dotfiles repo is private, so the real bootstrap script can't be served | |
| # from raw.githubusercontent.com. | |
| # | |
| # Run with (process substitution keeps stdin on the terminal, which the | |
| # interactive gh/bw prompts need — do NOT use `curl | bash`): | |
| # | |
| # bash <(curl -fsLS https://gist.githubusercontent.com/JesperDramsch/GIST_ID/raw/bootstrap-stage0.sh) | |
| # | |
| # What it does: | |
| # 1. Ensures gh exists (official release binary into ~/.local/bin — rootless, | |
| # no sudo/apt, HPC-safe; skipped where gh is already present) | |
| # 2. Signs in to GitHub via the browser device flow (no PAT to type; | |
| # on headless hosts gh prints the URL + one-time code instead) | |
| # 3. Fetches bootstrap/bootstrap.sh from the private repo via the GitHub | |
| # API and runs it | |
| set -euo pipefail | |
| OWNER="jesperdramsch" | |
| echo "" | |
| echo "=== dotfiles stage-0 bootstrap ===" | |
| echo "" | |
| # gh auth login and the downstream bw prompts need a real terminal. | |
| if [ ! -t 0 ]; then | |
| echo "ERROR: stdin is not a terminal (did you use 'curl | bash'?)." >&2 | |
| echo "Run via process substitution instead:" >&2 | |
| echo " bash <(curl -fsLS <shim-url>)" >&2 | |
| exit 1 | |
| fi | |
| # The repo name stays out of the published shim — ask for it instead. | |
| # A bare name is prefixed with the owner; a full owner/name slug passes through. | |
| read -r -p "Dotfiles repo (name under $OWNER/, or full owner/name slug): " repo_input | |
| repo_input="$(echo "$repo_input" | tr -d '[:space:]')" | |
| if [ -z "$repo_input" ]; then | |
| echo "ERROR: no repo given" >&2 | |
| exit 1 | |
| fi | |
| case "$repo_input" in | |
| */*) REPO="$repo_input" ;; | |
| *) REPO="$OWNER/$repo_input" ;; | |
| esac | |
| # git is needed by gh auth setup-git and the chezmoi clone later. | |
| if ! command -v git >/dev/null 2>&1; then | |
| if [ "$(uname -s)" = "Darwin" ]; then | |
| echo "git not found — triggering the Xcode Command Line Tools install." | |
| echo "Re-run this shim once it finishes." | |
| xcode-select --install | |
| else | |
| echo "ERROR: git not found. Install it first (e.g. 'sudo apt install git' on WSL2)." >&2 | |
| fi | |
| exit 1 | |
| fi | |
| export PATH="$HOME/.local/bin:$PATH" | |
| tmpdir="$(mktemp -d)" | |
| trap 'rm -rf "$tmpdir"' EXIT | |
| # --- 1. gh (user-local, rootless — no sudo, no /usr/local) --- | |
| if command -v gh >/dev/null 2>&1; then | |
| echo "[1/3] gh already installed: $(gh --version | head -n1)" | |
| else | |
| echo "[1/3] Installing gh to ~/.local/bin..." | |
| case "$(uname -m)" in | |
| x86_64) arch="amd64" ;; | |
| aarch64 | arm64) arch="arm64" ;; | |
| *) echo "ERROR: unsupported architecture: $(uname -m)" >&2; exit 1 ;; | |
| esac | |
| # Resolve the latest release tag from the redirect — no JSON parsing, no auth. | |
| tag="$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/cli/cli/releases/latest)" | |
| tag="${tag##*/}" | |
| ver="${tag#v}" | |
| mkdir -p "$HOME/.local/bin" | |
| if [ "$(uname -s)" = "Darwin" ]; then | |
| curl -fsSL -o "$tmpdir/gh.zip" \ | |
| "https://github.com/cli/cli/releases/download/${tag}/gh_${ver}_macOS_${arch}.zip" | |
| unzip -q "$tmpdir/gh.zip" -d "$tmpdir" | |
| cp "$tmpdir/gh_${ver}_macOS_${arch}/bin/gh" "$HOME/.local/bin/gh" | |
| else | |
| curl -fsSL "https://github.com/cli/cli/releases/download/${tag}/gh_${ver}_linux_${arch}.tar.gz" | | |
| tar -xz -C "$tmpdir" | |
| cp "$tmpdir/gh_${ver}_linux_${arch}/bin/gh" "$HOME/.local/bin/gh" | |
| fi | |
| chmod +x "$HOME/.local/bin/gh" | |
| echo " Installed: $(gh --version | head -n1)" | |
| fi | |
| # --- 2. GitHub auth (browser device flow) --- | |
| if gh auth status --hostname github.com >/dev/null 2>&1; then | |
| echo "[2/3] Already signed in to GitHub" | |
| else | |
| echo "[2/3] Signing in to GitHub (follow the one-time-code prompt)..." | |
| # https so the token also covers the private-repo clone during bootstrap; | |
| # post-bootstrap docs cover switching to the SSH alias. | |
| gh auth login --hostname github.com --git-protocol https --web | |
| if ! gh auth status --hostname github.com >/dev/null 2>&1; then | |
| echo "ERROR: GitHub authentication failed or was cancelled" >&2 | |
| exit 1 | |
| fi | |
| fi | |
| # --- 3. Fetch and run the real bootstrap from the private repo --- | |
| echo "[3/3] Fetching bootstrap.sh from $REPO..." | |
| script="$(gh api -H 'Accept: application/vnd.github.raw' "repos/$REPO/contents/bootstrap/bootstrap.sh")" || { | |
| echo "ERROR: failed to fetch bootstrap.sh from $REPO (check repo access for the signed-in account)" >&2 | |
| exit 1 | |
| } | |
| # Run from a file (not a pipe) so the real bootstrap's prompts keep the terminal. | |
| printf '%s\n' "$script" > "$tmpdir/bootstrap.sh" | |
| bash "$tmpdir/bootstrap.sh" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment