-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathget.sh
More file actions
executable file
·255 lines (228 loc) · 11.1 KB
/
Copy pathget.sh
File metadata and controls
executable file
·255 lines (228 loc) · 11.1 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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#!/usr/bin/env bash
# YA Dotfiles - one-line bootstrap
#
# Usage (pick a flavor):
#
# workstation (full setup - no wizard when piped):
# curl -fsSL https://raw.githubusercontent.com/YASoftwareDev/dotfiles/master/get.sh | bash -s -- workstation
#
# minimal (zsh + tmux + git config only):
# curl -fsSL https://raw.githubusercontent.com/YASoftwareDev/dotfiles/master/get.sh | bash -s -- minimal
#
# docker (headless, CI-friendly, no shell change):
# curl -fsSL https://raw.githubusercontent.com/YASoftwareDev/dotfiles/master/get.sh | bash -s -- docker
#
# force user-local installs (no apt, ~/.local/bin only) - useful on shared machines:
# curl -fsSL https://raw.githubusercontent.com/YASoftwareDev/dotfiles/master/get.sh | NOSUDO=1 bash -s -- workstation
# (or, if you have the file locally: NOSUDO=1 bash get.sh workstation)
#
# Or inspect first, then run (also gives you the interactive wizard):
# curl -fsSL https://raw.githubusercontent.com/YASoftwareDev/dotfiles/master/get.sh -o get.sh
# bash get.sh [minimal|workstation|docker]
#
# Docker (clean Ubuntu container):
# Option A - single copy-paste command (installs curl, then runs get.sh):
# apt-get update -qq && apt-get install -yq curl && \
# curl -fsSL https://raw.githubusercontent.com/YASoftwareDev/dotfiles/master/get.sh | bash -s -- workstation
#
# Option B - if you already have get.sh locally (auto-installs git+curl inside container):
# docker cp get.sh <container>:/get.sh
# docker exec <container> bash /get.sh workstation
#
# Note: the interactive profile wizard requires a real terminal (stdin = tty).
# When piping through bash, a profile argument is required.
#
set -euo pipefail
REPO="https://github.com/YASoftwareDev/dotfiles.git"
DEST="${DOTFILES_DIR:-$HOME/.dotfiles}"
# Parse args: [--nosudo] [profile]
PROFILE=""
for _arg in "$@"; do
case "$_arg" in
--nosudo) export NOSUDO=1 ;;
*) PROFILE="$_arg" ;;
esac
done
unset _arg
# ── Colour helpers ─────────────────────────────────────────────────────────────
BOLD='\033[1m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
_info() { echo -e "${BOLD} →${NC} $*"; }
_ok() { echo -e "${GREEN} ✓${NC} $*"; }
_warn() { echo -e "${YELLOW} !${NC} $*" >&2; }
_die() { echo -e "${RED} ✗${NC} $*" >&2; exit 1; }
echo ""
echo -e "${BOLD}╔══════════════════════════════════════════╗"
echo -e "║ YA Dotfiles - Bootstrap ║"
echo -e "╚══════════════════════════════════════════╝${NC}"
echo ""
# ── Enforce explicit profile when piped (no tty = no wizard) ──────────────────
if [ -z "$PROFILE" ] && [ ! -t 0 ]; then
_die "Profile required when piping. Use: bash -s -- <minimal|workstation|docker>"
fi
# ── Pre-flight checks ──────────────────────────────────────────────────────────
echo -e "${BOLD}── Pre-flight checks ──${NC}"
_preflight_ok=true
# package-manager hint for error messages (apt on Debian family, dnf/yum on RHEL family)
if command -v apt-get &>/dev/null; then _PKG_HINT="sudo apt install"
elif command -v dnf &>/dev/null; then _PKG_HINT="sudo dnf install"
elif command -v yum &>/dev/null; then _PKG_HINT="sudo yum install"
else _PKG_HINT="your package manager:"
fi
# auto-bootstrap missing tools when running as root (apt or dnf/yum systems)
_apt_updated=false
_pkg_bootstrap() {
local pkg="$1"
[ "$(id -u)" -eq 0 ] || return 1
if command -v apt-get &>/dev/null; then
if ! $_apt_updated; then
apt-get update -qq >/dev/null
_apt_updated=true
fi
DEBIAN_FRONTEND=noninteractive apt-get install -yq "$pkg" >/dev/null
elif command -v dnf &>/dev/null; then
dnf -yq install "$pkg" >/dev/null 2>&1
elif command -v yum &>/dev/null; then
yum -yq install "$pkg" >/dev/null 2>&1
else
return 1
fi
}
# git (hard requirement - needed to clone)
if command -v git &>/dev/null; then
_ok "git $(git --version | awk '{print $3}')"
elif _pkg_bootstrap git; then
_ok "git $(git --version | awk '{print $3}') (just installed)"
else
echo -e "${RED} ✗${NC} git - not found → ${_PKG_HINT} git" >&2
_preflight_ok=false
fi
# transfer tool (curl or wget - needed by install.sh to fetch binaries)
if command -v curl &>/dev/null; then
_ok "curl $(curl --version | awk 'NR==1{print $2}')"
elif command -v wget &>/dev/null; then
_ok "wget $(wget --version 2>&1 | awk 'NR==1{print $3}')"
elif _pkg_bootstrap curl; then
_ok "curl $(curl --version | awk 'NR==1{print $2}') (just installed)"
else
echo -e "${RED} ✗${NC} curl/wget - neither found → ${_PKG_HINT} curl" >&2
_preflight_ok=false
fi
# sudo (soft - not needed when already root)
if [ -n "${NOSUDO:-}" ]; then
_warn "NOSUDO set - sudo checks skipped (user-local install)"
elif [ "$(id -u)" -eq 0 ]; then
_ok "running as root (sudo not required)"
elif command -v sudo &>/dev/null && sudo -n true 2>/dev/null; then
_ok "sudo (passwordless)"
elif command -v sudo &>/dev/null; then
_warn "sudo present but requires a password - you will be prompted when packages install"
else
_warn "sudo not found - system package installs will be skipped"
fi
# OS (soft - Debian family is fully supported; RHEL family installs tools
# as user-local binaries and needs prerequisites preinstalled via dnf)
if [ -f /etc/os-release ]; then
# shellcheck source=/dev/null
. /etc/os-release
case "${ID:-}" in
ubuntu) _ok "OS: Ubuntu ${VERSION_ID:-}" ;;
debian) _warn "OS: Debian ${VERSION_ID:-} - supported but not tested" ;;
almalinux|rocky|rhel|centos|fedora)
_ok "OS: ${PRETTY_NAME:-$ID} (RHEL family)"
_warn "No apt: tools install to ~/.local/bin; prerequisites (git curl zsh tmux python3 tar) need dnf" ;;
*) _warn "OS: ${PRETTY_NAME:-unknown} - scripts are written for Ubuntu; proceed with caution" ;;
esac
else
_warn "OS: unknown - /etc/os-release not found"
fi
# disk space (2 GB required in $HOME)
_avail_mb=$(df -m "$HOME" 2>/dev/null | awk 'NR==2{print $4}')
if [ -n "$_avail_mb" ]; then
if [ "$_avail_mb" -ge 2048 ]; then
_ok "Disk: ${_avail_mb} MB free in \$HOME"
else
_warn "Disk: only ${_avail_mb} MB free in \$HOME (2048 MB recommended)"
fi
fi
# network
if command -v curl &>/dev/null; then
_net_ok=$(curl -sSf --max-time 5 https://github.com &>/dev/null && echo yes || echo no)
elif command -v wget &>/dev/null; then
_net_ok=$(wget -q --spider --timeout=5 https://github.com &>/dev/null && echo yes || echo no)
else
_net_ok=no
fi
if [ "$_net_ok" = "yes" ]; then
_ok "Network: github.com reachable"
else
_warn "Network: github.com unreachable - install may fail on download steps"
fi
echo ""
if ! $_preflight_ok; then
_die "Fix the errors above and re-run."
fi
# ── Clone or update ────────────────────────────────────────────────────────────
if [ -d "$DEST/.git" ]; then
_info "Dotfiles found at $DEST - pulling latest..."
# Stale lock from a previously interrupted git operation
if [ -f "$DEST/.git/index.lock" ]; then
_die "Git index is locked at $DEST/.git/index.lock\n If no git process is running: rm '$DEST/.git/index.lock'\n Then: bash '$DEST/install.sh' ${PROFILE:-workstation}"
fi
# Unfinished merge or rebase
if [ -f "$DEST/.git/MERGE_HEAD" ] || [ -d "$DEST/.git/rebase-merge" ] || [ -d "$DEST/.git/rebase-apply" ]; then
_die "Repo at $DEST is in an unfinished merge/rebase state.\n Resolve it first: git -C '$DEST' status\n Then: bash '$DEST/install.sh' ${PROFILE:-workstation}"
fi
# Local modifications would be overwritten by pull - auto-stash, pull, restore
if ! git -C "$DEST" diff --quiet HEAD 2>/dev/null; then
_warn "Local modifications detected - auto-stashing before pull:"
git -C "$DEST" diff --name-only HEAD 2>/dev/null | while IFS= read -r f; do _warn " $f"; done
if ! git -C "$DEST" stash 2>&1; then
_die "git stash failed - resolve manually, then:\n bash '$DEST/install.sh' ${PROFILE:-workstation}"
fi
_ok "Local changes stashed"
if ! git -C "$DEST" pull --ff-only 2>&1; then
git -C "$DEST" stash pop 2>/dev/null || true
_die "Pull failed (stash restored) - resolve manually:\n git -C '$DEST' pull\n bash '$DEST/install.sh' ${PROFILE:-workstation}"
fi
_ok "Pulled latest"
if ! git -C "$DEST" stash pop 2>&1; then
_warn "Stash pop has conflicts - your local changes conflict with what was pulled."
_warn " See conflicts: git -C '$DEST' status"
_warn " Accept upstream: git -C '$DEST' checkout -- <file>"
_warn " Drop the stash: git -C '$DEST' stash drop"
_warn " Then install: bash '$DEST/install.sh' ${PROFILE:-workstation}"
_warn " If shell is broken: exec bash"
_die "Resolve stash pop conflicts manually."
fi
_ok "Local changes restored after pull"
fi
# Local commits ahead of upstream (clean tree but diverged)
if git -C "$DEST" rev-parse '@{u}' &>/dev/null; then
_ahead=$(git -C "$DEST" rev-list '@{u}..HEAD' --count 2>/dev/null || echo 0)
if [ "${_ahead:-0}" -gt 0 ]; then
_warn "Local commits on this clone (${_ahead} ahead of upstream):"
git -C "$DEST" log '@{u}..HEAD' --oneline >&2
_die "Resolve manually:\n git -C '$DEST' fetch\n git -C '$DEST' reset --hard '@{u}' # WARNING: discards those commits\n bash '$DEST/install.sh' ${PROFILE:-workstation}"
fi
fi
# Pull (network failure: warn and continue on existing clone)
if git -C "$DEST" pull --ff-only 2>&1; then
_ok "Updated to $(git -C "$DEST" describe --tags --always 2>/dev/null || git -C "$DEST" rev-parse --short HEAD)"
else
_warn "git pull failed - continuing with existing clone (proceeding with installed version)."
_warn "To apply latest changes later: git -C '$DEST' pull && bash '$DEST/install.sh' ${PROFILE:-workstation}"
fi
elif [ -e "$DEST" ]; then
_die "$DEST already exists but is not a dotfiles repo.\n Remove it or set DOTFILES_DIR to a different path:\n DOTFILES_DIR=~/my-dotfiles bash get.sh ${PROFILE:-}"
else
_info "Cloning dotfiles to $DEST..."
git clone "$REPO" "$DEST"
_ok "Cloned"
fi
# ── Hand off to the real installer ────────────────────────────────────────────
echo ""
if [ -n "$PROFILE" ]; then
exec bash "$DEST/install.sh" "$PROFILE"
else
exec bash "$DEST/install.sh"
fi