/
/
/
My dotfile repo. It sets aliases and installs generic applications for my user.
1#!/bin/bash
2
3export DEBIAN_FRONTEND=noninteractive
4
5home_dir="${HOME:-/home/$(id -un)}"
6temp_dir="${home_dir}/tmp"
7code_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)/.."
8
9local_bin_dir="${home_dir}/.local/bin"
10local_app_dir="${home_dir}/.local/share/applications"
11local_icon_dir="${home_dir}/.local/share/icons/hicolor/apps"
12local_lib_dir="${home_dir}/.local/lib"
13local_opt_dir="${home_dir}/.local/opt"
14font_dir="${home_dir}/.fonts"
15
16mkdir -p "${code_dir}"
17mkdir -p "${local_bin_dir}"
18mkdir -p "${local_app_dir}"
19mkdir -p "${local_icon_dir}"
20mkdir -p "${local_lib_dir}"
21mkdir -p "${temp_dir}"
22mkdir -p "${font_dir}"
23mkdir -p "${local_opt_dir}"
24
25export PATH="${local_bin_dir}:${PATH}"
26
27wget -nc -O "${temp_dir}/nerdfont.zip" \
28 "https://github.com/ryanoasis/nerd-fonts/releases/download/v3.2.1/FiraCode.zip"
29
30# Install / reinstall nvim latest stable based on architecture
31arch="$(uname -m)"
32case "$arch" in
33 x86_64)
34 echo "[nvim] Installing latest x86_64 binary..."
35 nvim_asset="nvim-linux-x86_64"
36 ;;
37 aarch64)
38 echo "[nvim] Installing latest ARM64 binary..."
39 nvim_asset="nvim-linux-arm64"
40 ;;
41 *)
42 echo "[nvim] Unsupported architecture: $arch"
43 exit 1
44 ;;
45esac
46
47nvim_tar="${nvim_asset}.tar.gz"
48nvim_url="https://github.com/neovim/neovim/releases/latest/download/${nvim_tar}"
49
50rm -rf "${local_opt_dir}/nvim-linux64"
51rm -rf "${local_opt_dir}/nvim-linux-x86_64"
52rm -rf "${local_opt_dir}/nvim-linux-arm64"
53
54curl -fL -o "${temp_dir}/${nvim_tar}" "${nvim_url}"
55tar xzf "${temp_dir}/${nvim_tar}" -C "${local_opt_dir}/"
56
57ln -sf "${local_opt_dir}/${nvim_asset}/bin/nvim" "${local_bin_dir}/nvim"
58
59"${local_bin_dir}/nvim" --version | head -n 1
60
61unzip -nq "${temp_dir}/nerdfont.zip" -d "${font_dir}"
62fc-cache -f -v
63
64# Install superfile locally, without sudo/password prompts
65arch="$(uname -m)"
66case "$arch" in
67 x86_64) spf_arch="amd64" ;;
68 aarch64) spf_arch="arm64" ;;
69 *)
70 echo "[superfile] Unsupported architecture: $arch"
71 exit 1
72 ;;
73esac
74
75SPF_VERSION="$(
76 curl -fsSL "https://api.github.com/repos/yorukot/superfile/releases/latest" \
77 | grep '"tag_name"' \
78 | cut -d'"' -f4 \
79 | sed 's/^v//'
80)"
81
82spf_tar="superfile-linux-v${SPF_VERSION}-${spf_arch}.tar.gz"
83spf_url="https://github.com/yorukot/superfile/releases/download/v${SPF_VERSION}/${spf_tar}"
84
85echo "[superfile] downloading ${spf_url}"
86rm -rf "${temp_dir}/superfile"
87mkdir -p "${temp_dir}/superfile"
88curl -fsSL -o "${temp_dir}/${spf_tar}" "${spf_url}"
89tar -xzf "${temp_dir}/${spf_tar}" -C "${temp_dir}/superfile"
90
91spf_bin="$(find "${temp_dir}/superfile" -type f -name spf | head -n 1)"
92if [ -z "${spf_bin}" ]; then
93 echo "[superfile] Could not find extracted spf binary"
94 exit 1
95fi
96
97install -m 0755 "${spf_bin}" "${local_bin_dir}/spf"
98"${local_bin_dir}/spf" --version || true
99
100# Install lazygit locally
101arch="$(uname -m)"
102case "$arch" in
103 x86_64) lazygit_arch="x86_64" ;;
104 aarch64) lazygit_arch="arm64" ;;
105 *)
106 echo "[lazygit] Unsupported architecture: $arch"
107 exit 1
108 ;;
109esac
110
111LAZYGIT_VERSION="$(
112 curl -fsSL "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" \
113 | grep '"tag_name"' \
114 | cut -d'"' -f4 \
115 | sed 's/^v//'
116)"
117
118lazygit_tar="lazygit_${LAZYGIT_VERSION}_Linux_${lazygit_arch}.tar.gz"
119lazygit_url="https://github.com/jesseduffield/lazygit/releases/download/v${LAZYGIT_VERSION}/${lazygit_tar}"
120
121echo "[lazygit] downloading ${lazygit_url}"
122rm -rf "${temp_dir}/lazygit"
123mkdir -p "${temp_dir}/lazygit"
124curl -fsSL -o "${temp_dir}/${lazygit_tar}" "${lazygit_url}"
125tar -xzf "${temp_dir}/${lazygit_tar}" -C "${temp_dir}/lazygit" lazygit
126
127install -m 0755 "${temp_dir}/lazygit/lazygit" "${local_bin_dir}/lazygit"
128"${local_bin_dir}/lazygit" --version
129
130# Clone / refresh source-based tools
131rm -rf "${code_dir}/fzf"
132rm -rf "${code_dir}/powerlevel10k"
133rm -rf "${code_dir}/fri"
134rm -rf "${code_dir}/diff-so-fancy"
135rm -rf "${code_dir}/zoxide"
136rm -rf "${home_dir}/.tmux/plugins/tpm"
137
138git clone --depth=1 -b 0.50.0 https://github.com/junegunn/fzf.git "${code_dir}/fzf"
139git clone --depth=1 -b v1.20.0 https://github.com/romkatv/powerlevel10k.git "${code_dir}/powerlevel10k"
140git clone --depth=1 https://github.com/xuyuan/fri.git "${code_dir}/fri"
141git clone --depth=1 -b v1.4.4 https://github.com/so-fancy/diff-so-fancy.git "${code_dir}/diff-so-fancy"
142git clone --depth=1 -b v0.9.4 https://github.com/ajeetdsouza/zoxide.git "${code_dir}/zoxide"
143git clone --depth=1 https://github.com/tmux-plugins/tpm "${home_dir}/.tmux/plugins/tpm"
144
145# Rust toolchain is only needed for explicit cargo fallback.
146if [ "${ALLOW_CARGO_FALLBACK:-0}" = "1" ] && ! command -v cargo >/dev/null 2>&1; then
147 echo "[setup] Installing Rust toolchain because ALLOW_CARGO_FALLBACK=1 ..."
148 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path
149 . "${home_dir}/.cargo/env"
150fi
151
152# Architecture detection for Rust-tool binary downloads
153arch="$(uname -m)"
154case "$arch" in
155 x86_64) rust_arch="x86_64-unknown-linux-gnu" ;;
156 aarch64) rust_arch="aarch64-unknown-linux-gnu" ;;
157 *)
158 echo "[rust-tools] Unsupported architecture: $arch, skipping rust tools installation"
159 rust_arch=""
160 ;;
161esac
162
163if [ -n "${rust_arch}" ]; then
164 echo "[rust-tools] Installing pre-compiled binaries for ${arch} ..."
165
166 # eza
167 echo "[eza] Downloading binary..."
168 rm -rf "${temp_dir}/eza"
169 mkdir -p "${temp_dir}/eza"
170
171 if curl -fsSL "https://github.com/eza-community/eza/releases/download/v0.23.0/eza_${rust_arch}.tar.gz" \
172 | tar xz -C "${temp_dir}/eza" \
173 && [ -f "${temp_dir}/eza/eza" ]; then
174
175 cp "${temp_dir}/eza/eza" "${local_bin_dir}/eza"
176 chmod +x "${local_bin_dir}/eza"
177 echo "[eza] Binary installed successfully"
178 else
179 echo "[eza] Binary download failed"
180 if [ "${ALLOW_CARGO_FALLBACK:-0}" = "1" ] && command -v cargo >/dev/null 2>&1; then
181 cargo install --locked --root "${home_dir}/.local" eza
182 else
183 echo "[eza] Skipping cargo fallback"
184 fi
185 fi
186
187 # bat
188 echo "[bat] Downloading binary..."
189 rm -rf "${temp_dir}/bat"
190 mkdir -p "${temp_dir}/bat"
191
192 if curl -fsSL "https://github.com/sharkdp/bat/releases/download/v0.24.0/bat-v0.24.0-${rust_arch}.tar.gz" \
193 | tar xz --strip-components=1 -C "${temp_dir}/bat" \
194 && [ -f "${temp_dir}/bat/bat" ]; then
195
196 cp "${temp_dir}/bat/bat" "${local_bin_dir}/bat"
197 chmod +x "${local_bin_dir}/bat"
198 echo "[bat] Binary installed successfully"
199 else
200 echo "[bat] Binary download failed"
201 if [ "${ALLOW_CARGO_FALLBACK:-0}" = "1" ] && command -v cargo >/dev/null 2>&1; then
202 cargo install --locked --root "${home_dir}/.local" bat
203 else
204 echo "[bat] Skipping cargo fallback"
205 fi
206 fi
207
208 # ripgrep
209 echo "[ripgrep] Downloading binary..."
210 rm -rf "${temp_dir}/ripgrep"
211 mkdir -p "${temp_dir}/ripgrep"
212
213 if curl -fsSL "https://github.com/BurntSushi/ripgrep/releases/download/14.1.1/ripgrep-14.1.1-${rust_arch}.tar.gz" \
214 | tar xz --strip-components=1 -C "${temp_dir}/ripgrep" \
215 && [ -f "${temp_dir}/ripgrep/rg" ]; then
216
217 cp "${temp_dir}/ripgrep/rg" "${local_bin_dir}/rg"
218 chmod +x "${local_bin_dir}/rg"
219 echo "[ripgrep] Binary installed successfully"
220 else
221 echo "[ripgrep] Binary download failed"
222 if [ "${ALLOW_CARGO_FALLBACK:-0}" = "1" ] && command -v cargo >/dev/null 2>&1; then
223 cargo install --locked --root "${home_dir}/.local" ripgrep
224 else
225 echo "[ripgrep] Skipping cargo fallback"
226 fi
227 fi
228
229 # fd
230 echo "[fd] Downloading binary..."
231 rm -rf "${temp_dir}/fd"
232 mkdir -p "${temp_dir}/fd"
233
234 if curl -fsSL "https://github.com/sharkdp/fd/releases/download/v10.2.0/fd-v10.2.0-${rust_arch}.tar.gz" \
235 | tar xz --strip-components=1 -C "${temp_dir}/fd" \
236 && [ -f "${temp_dir}/fd/fd" ]; then
237
238 cp "${temp_dir}/fd/fd" "${local_bin_dir}/fd"
239 chmod +x "${local_bin_dir}/fd"
240 echo "[fd] Binary installed successfully"
241 else
242 echo "[fd] Binary download failed"
243 if [ "${ALLOW_CARGO_FALLBACK:-0}" = "1" ] && command -v cargo >/dev/null 2>&1; then
244 cargo install --locked --root "${home_dir}/.local" fd-find
245 else
246 echo "[fd] Skipping cargo fallback"
247 fi
248 fi
249fi
250
251# Node.js user-local install
252NODE_VERSION="${NODE_VERSION:-v20.17.0}"
253
254arch="$(uname -m)"
255case "$arch" in
256 x86_64) node_arch="x64" ;;
257 aarch64) node_arch="arm64" ;;
258 *)
259 echo "[node] Unsupported architecture: $arch"
260 exit 1
261 ;;
262esac
263
264node_base="node-${NODE_VERSION}-linux-${node_arch}"
265node_tar="${node_base}.tar.xz"
266node_url="https://nodejs.org/dist/${NODE_VERSION}/${node_tar}"
267
268if [ ! -x "${local_opt_dir}/${node_base}/bin/node" ]; then
269 echo "[node] downloading ${node_url}"
270 curl -fsSL -o "${temp_dir}/${node_tar}" "${node_url}"
271 echo "[node] installing to ${local_opt_dir}/${node_base}"
272 tar -xJf "${temp_dir}/${node_tar}" -C "${local_opt_dir}/"
273fi
274
275ln -sf "${local_opt_dir}/${node_base}/bin/node" "${local_bin_dir}/node"
276ln -sf "${local_opt_dir}/${node_base}/bin/npm" "${local_bin_dir}/npm"
277ln -sf "${local_opt_dir}/${node_base}/bin/npx" "${local_bin_dir}/npx"
278
279# Claude Code local install
280rm -f "${home_dir}/.claude"
281ln -s "${code_dir}/dotfiles/.claude" "${home_dir}/.claude"
282
283mkdir -p "${home_dir}/.claude/local"
284"${local_bin_dir}/npm" install --prefix "${home_dir}/.claude/local" @anthropic-ai/claude-code
285
286cat > "${local_bin_dir}/claude" << 'EOF'
287#!/bin/bash
288exec "${HOME}/.local/bin/node" "${HOME}/.claude/local/node_modules/.bin/claude" "$@"
289EOF
290chmod +x "${local_bin_dir}/claude"
291
292# fzf and zoxide
293yes | "${code_dir}/fzf/install" --all
294"${code_dir}/zoxide/install.sh" --bin-dir "${local_bin_dir}"
295
296rm -f "${local_bin_dir}/fri"
297ln -s "${code_dir}/fri/fri" "${local_bin_dir}/fri"
298
299rm -f "${home_dir}/.dircolors"
300ln -s "${code_dir}/dotfiles/.dircolors" "${home_dir}/.dircolors"
301
302# Build diff-so-fancy, handle missing dependency gracefully
303cd "${code_dir}/diff-so-fancy/third_party/build_fatpack"
304if perl build.pl --output "${local_bin_dir}/diff-so-fancy" 2>/dev/null; then
305 echo "[diff-so-fancy] Built successfully"
306else
307 echo "[diff-so-fancy] Build failed - missing App::FatPacker, using direct script"
308 ln -sf "${code_dir}/diff-so-fancy/diff-so-fancy" "${local_bin_dir}/diff-so-fancy"
309 chmod +x "${local_bin_dir}/diff-so-fancy"
310fi
311cd "${home_dir}"
312
313# Dotfile symlinks
314rm -f "${home_dir}/.zshrc"
315ln -s "${code_dir}/dotfiles/.zshrc" "${home_dir}/.zshrc"
316
317rm -f "${home_dir}/.p10k.zsh"
318ln -s "${code_dir}/dotfiles/.p10k.zsh" "${home_dir}/.p10k.zsh"
319
320rm -f "${home_dir}/.powerlevel10k.zsh-theme"
321ln -s "${code_dir}/powerlevel10k/powerlevel10k.zsh-theme" "${home_dir}/.powerlevel10k.zsh-theme"
322
323rm -f "${home_dir}/.aliases"
324ln -s "${code_dir}/dotfiles/.aliases" "${home_dir}/.aliases"
325
326rm -rf "${home_dir}/.config/git"
327mkdir -p "${home_dir}/.config/git"
328ln -s "${code_dir}/dotfiles/.config/git/config" "${home_dir}/.config/git/config"
329
330rm -rf "${home_dir}/.config/tmux"
331ln -s "${code_dir}/dotfiles/.config/tmux" "${home_dir}/.config/"
332
333rm -rf "${home_dir}/.config/nvim"
334ln -s "${code_dir}/dotfiles/.config/nvim" "${home_dir}/.config/nvim"
335
336rm -rf "${home_dir}/.config/lazygit"
337ln -s "${code_dir}/dotfiles/.config/lazygit" "${home_dir}/.config/lazygit"
338
339# Install tmux plugins
340"${home_dir}/.tmux/plugins/tpm/bin/install_plugins"
341
342# Install nvim plugins
343"${local_bin_dir}/nvim" --headless "+Lazy! sync" +qa
344