Repository navigation
Expand file tree
/
Copy pathsetup.sh
More file actions
executable file
·184 lines (168 loc) · 7.48 KB
/
Copy pathsetup.sh
File metadata and controls
executable file
·184 lines (168 loc) · 7.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/usr/bin/env bash
# Guest-side dotfiles installer. Runs INSIDE a VM or container.
#
# 1. Installs git + curl + mise (minimum bootstrap prerequisites).
# 2. Clones the dotfiles repo to ~/.dotfiles and seeds ~/.miserc.toml plus the
# global mise config (and the macOS platform config) so they are readable.
# 3. Runs 'mise bootstrap --yes' from $HOME, which reads the global config
# and handles the rest:
# a. [bootstrap.packages] — brew formulae and casks, apt/pacman/dnf
# b. [dotfiles] apply — symlinks and Cursor settings template
# c. [bootstrap.macos.*] — macOS defaults
# d. [bootstrap.user] — login shell (chsh)
# e. mise install [tools] — tools from global config
# f. [tasks.bootstrap] — atuin hooks
#
# Usage: ./setup.sh [--repo <url>] [--branch <branch>]
#
# Flags:
# --repo <url> Dotfiles git repo (default: AllySummers/dotfiles).
# --branch <branch> Branch to check out (default: repo's default branch).
#
# Environment:
# DOTFILES_SOURCE If set to an existing directory, symlinks it as the
# dotfiles root instead of cloning --repo. Lets you test
# local uncommitted changes inside a VM or container.
set -euo pipefail
DOTFILES_REPO="${DOTFILES_REPO:-https://github.com/AllySummers/dotfiles.git}"
DOTFILES_BRANCH="${DOTFILES_BRANCH:-}"
while [ "$#" -gt 0 ]; do
case "$1" in
--repo) DOTFILES_REPO="$2"; shift 2 ;;
--branch) DOTFILES_BRANCH="$2"; shift 2 ;;
*) echo "Unknown argument: $1" >&2; exit 1 ;;
esac
done
# ── Pretty logging (no zsh dependency; works in a bare container) ─────────────
if [ -t 1 ]; then
C_BLUE=$'\033[34m'; C_GREEN=$'\033[32m'; C_YELLOW=$'\033[33m'; C_BOLD=$'\033[1m'; C_RESET=$'\033[0m'
else
C_BLUE=''; C_GREEN=''; C_YELLOW=''; C_BOLD=''; C_RESET=''
fi
log() { printf '%s==>%s %s%s%s\n' "$C_BLUE" "$C_RESET" "$C_BOLD" "$*" "$C_RESET"; }
ok() { printf '%s ✓%s %s\n' "$C_GREEN" "$C_RESET" "$*"; }
warn() { printf '%s ⚠%s %s\n' "$C_YELLOW" "$C_RESET" "$*"; }
# ── sudo only when needed (containers usually run as root) ────────────────────
if [ "$(id -u)" -ne 0 ] && command -v sudo >/dev/null 2>&1; then
SUDO="sudo"
else
SUDO=""
fi
# ── Detect platform ───────────────────────────────────────────────────────────
PLATFORM=""
case "$(uname -s)" in
Darwin) PLATFORM="macos" ;;
Linux)
# /etc/os-release is a symlink to /usr/lib/os-release on Arch; some
# minimal containers only have the latter.
_os_release=""
[ -r /etc/os-release ] && _os_release=/etc/os-release
[ -z "$_os_release" ] && [ -r /usr/lib/os-release ] && _os_release=/usr/lib/os-release
if [ -n "$_os_release" ]; then
# shellcheck disable=SC1090
. "$_os_release"
case "${ID:-}" in
arch) PLATFORM="arch" ;;
ubuntu) PLATFORM="ubuntu" ;;
debian) PLATFORM="debian" ;;
*)
case " ${ID_LIKE:-} " in
*arch*) PLATFORM="arch" ;;
*debian*|*ubuntu*) PLATFORM="debian" ;;
*) echo "Unsupported Linux distro: ${ID:-unknown}" >&2; exit 1 ;;
esac
;;
esac
else
echo "Cannot detect Linux distro (no /etc/os-release)" >&2; exit 1
fi
;;
*) echo "Unsupported OS: $(uname -s)" >&2; exit 1 ;;
esac
log "Detected platform: $PLATFORM"
# ── Ensure ~/.local/bin is on PATH for installers below ───────────────────────
export PATH="$HOME/.local/bin:$PATH"
# ── Install git + curl (minimum to bootstrap mise) ───────────────────────────
# The full package set is declared in [bootstrap.packages] in the global config
# and installed by 'mise bootstrap' after dotfiles are seeded.
install_prereqs() {
case "$PLATFORM" in
macos)
# curl and git ship with macOS; nothing extra needed here.
;;
arch)
# Pacman 7's downloader runs in a seccomp + landlock sandbox that the kernel
# can't apply inside Docker (especially under QEMU/Rosetta emulation).
# Disable it when running inside a container (/.dockerenv exists in Docker).
pac_flags=()
[ -f /.dockerenv ] && pac_flags+=(--disable-sandbox)
$SUDO pacman -Sy --needed --noconfirm "${pac_flags[@]}" git curl ca-certificates
;;
ubuntu|debian)
$SUDO apt-get update -y
DEBIAN_FRONTEND=noninteractive $SUDO apt-get install -y git curl ca-certificates
;;
esac
}
# ── Install mise ──────────────────────────────────────────────────────────────
install_mise() {
if command -v mise >/dev/null 2>&1; then
ok "mise already installed"
else
log "Installing mise (mise.run)"
curl -fsSL https://mise.run | sh
fi
command -v mise >/dev/null 2>&1 && ok "mise $(mise --version)"
}
# ── Clone/link dotfiles repo ──────────────────────────────────────────────────
# Seeds ~/.dotfiles and copies the global mise config so that 'mise bootstrap'
# can read [bootstrap.*] and [dotfiles] in the next step. bootstrap's dotfiles
# apply step will then replace the copy with a proper symlink (content-identical
# regular files are converged to symlinks without --force since mise v2026.6.7).
apply_dotfiles() {
DOTFILES_DIR="$HOME/.dotfiles"
if [ -n "${DOTFILES_SOURCE:-}" ] && [ -d "$DOTFILES_SOURCE" ]; then
log "Linking dotfiles from local source: $DOTFILES_SOURCE"
ln -snf "$DOTFILES_SOURCE" "$DOTFILES_DIR"
else
if [ -d "$DOTFILES_DIR/.git" ]; then
log "Updating dotfiles repo at $DOTFILES_DIR"
git -C "$DOTFILES_DIR" pull --ff-only
else
log "Cloning dotfiles repo: $DOTFILES_REPO${DOTFILES_BRANCH:+ (branch: $DOTFILES_BRANCH)}"
git clone ${DOTFILES_BRANCH:+--branch "$DOTFILES_BRANCH"} "$DOTFILES_REPO" "$DOTFILES_DIR"
fi
fi
# Seed the early-init miserc and the global mise config (plus the platform
# config, if any) so mise bootstrap can read them before the dotfiles phase.
mkdir -p "$HOME/.config/mise"
seed_file "$DOTFILES_DIR/home/.miserc.toml" "$HOME/.miserc.toml"
seed_file "$DOTFILES_DIR/home/.config/mise/config.toml" "$HOME/.config/mise/config.toml"
if [ "$PLATFORM" = "macos" ]; then
seed_file "$DOTFILES_DIR/home/.config/mise/config.macos.toml" "$HOME/.config/mise/config.macos.toml"
fi
ok "Dotfiles seeded"
}
# Copy src to dest only if dest is absent or already identical (never overwrite
# a differing file), then trust it. The dotfiles phase converges identical
# copies into symlinks.
seed_file() {
local src="$1" dest="$2"
if [ ! -e "$dest" ]; then
cp "$src" "$dest"
elif ! cmp -s "$src" "$dest"; then
warn "Keeping existing $dest (differs from dotfiles)"
fi
case "$dest" in
*/.config/mise/*) mise trust --yes "$dest" ;;
esac
}
install_prereqs
install_mise
apply_dotfiles
# ── Run bootstrap from $HOME ──────────────────────────────────────────────────
# Reads [bootstrap.*] and [dotfiles] from the now-seeded global config.
log "Running mise bootstrap"
cd ~
mise bootstrap --yes
log "Done! Open a new zsh session to use the configured shell."