feat(git): deploy user-scope ~/.gitconfig from the repo template

Git never expands $VARS in its config, so gitconfig carries @USER@ and
@EMAIL@ placeholders that install.sh fills from the deployed bashrc's
USER/EMAIL exports. A differing ~/.gitconfig is kept as
~/.gitconfig.backup-<date>; an identical one is left alone. A repo's
.git/config still overrides it. excludesfile uses ~ (git expands it,
not $HOME).

The rc files now override $USER, so the installer takes the login name
from id -un for dscl and the code-server unit.
This commit is contained in:
Bastien CHANOT
2026-10-06 10:55:24 +02:00
parent 3b53213af4
commit 51521e7ca4
5 changed files with 97 additions and 7 deletions
+9
View File
@@ -90,3 +90,12 @@ Same branch. Prompt at start of Darwin block (MACOS_SHELL=bash|zsh env overrides
- [x] install.sh: use_brew_bash_login_shell → set_login_shell <path>, called at end with chosen shell
- [x] README + CLAUDE.md
- [x] shellcheck/bash -n/zsh -n; runtime: theme in zsh (prompt render, timer, git bits), dtach-router sourced in zsh; harness both choices
## Feature — user-scope ~/.gitconfig from repo template, VIUSER/VIMAIL → USER/EMAIL (2026-10-06)
- [x] rc files (bashrc-linux, bashrc-osx, zshrc-osx): `VIUSER`/`VIMAIL` → `USER`/`EMAIL`
- [x] `gitconfig` template: git never expands `$VAR` → `@USER@`/`@EMAIL@` placeholders, `excludesfile = ~/.gitignore`
- [x] install.sh `deploy_gitconfig`: values read from deployed bashrc, rendered → ~/.gitconfig, differing old one → ~/.gitconfig.backup-<date>
- [x] install.sh: `$USER` → `$(id -un)` (dscl, code-server unit): rc now overrides USER with identity
- [x] README + CLAUDE.md layout
- [x] Verify: shellcheck, bash -n, render to temp HOME, `git config --file` reads values
- [x] `git-delta` added to apt + brew lists (gitconfig pager = delta)
+2 -1
View File
@@ -20,7 +20,8 @@ install.sh one-shot installer (OS auto-detected)
vim/vimrc vim config (pathogen, molokai, syntastic, NERDTree)
vim/autoload/ pathogen loader (committed)
vim/colors/ molokai colorscheme (committed)
bash/bashrc-{linux,osx} OS-detected bashrc
bash/bashrc-{linux,osx} OS-detected bashrc (exports USER/EMAIL identity)
gitconfig user-scope ~/.gitconfig template, @USER@/@EMAIL@ filled at install
zsh/{zshrc-osx,bchanot.zsh-theme} macOS zsh option: oh-my-zsh zshrc + theme porting the bash prompt
bin/{dt,dtach-router,claude-provider} CLI scripts deployed to ~/.local/bin
etc/profile.d/disk-usage-warning.sh login-time low-disk warning → /etc/profile.d (Linux only)
+3 -2
View File
@@ -27,6 +27,7 @@ curl -fsSL https://git.bchanot.fr/bchanot/config/raw/branch/master/remote-instal
| `vim/vimrc` | Vim config: pathogen, molokai, syntastic (C with `-Wall -Werror -Wextra`), NERDTree, 42-style canonical class generators (`:ClassH`, `:ClassC`). |
| `vim/autoload/` | `pathogen.vim` plugin loader (committed). |
| `vim/colors/` | `molokai.vim` colorscheme (committed). |
| `gitconfig` | Template of the user-scope `~/.gitconfig`. `@USER@` and `@EMAIL@` are filled at install with the `USER` and `EMAIL` exported by the bashrc (git never expands `$VARS` itself). |
| `bash/bashrc-linux` | bashrc for desktop Linux (git-aware prompt + command timer). |
| `bash/bashrc-osx` | bashrc for macOS: `bashrc-linux` adapted (Homebrew on `PATH`, BSD `ls -G`, bash 5 clock for the timer, `cc` without `systemd-run`). |
| `zsh/zshrc-osx` | zshrc for macOS when zsh is chosen: oh-my-zsh + the same env, aliases and dtach menu as `bashrc-osx`. Loads `~/.zshrc.local` for machine-specific lines. |
@@ -64,11 +65,11 @@ What it does:
3. Moves any existing `~/.vim`, `~/.vimrc`, `~/.bashrc`, `~/.Sublivim` to `~/Oldconfig`.
4. Clones the `syntastic` and `nerdtree` vim plugins into `~/.vim/bundle/`.
5. Copies the tracked vim files into `~/.vim` and symlinks `~/.vimrc`.
6. Picks the bashrc by OS: macOS → `bashrc-osx` (falls back to `bashrc-linux` if missing), everything else → `bashrc-linux`. Copies it to `~/.bashrc`.
6. Picks the bashrc by OS: macOS → `bashrc-osx` (falls back to `bashrc-linux` if missing), everything else → `bashrc-linux`. Copies it to `~/.bashrc`. Then renders `gitconfig` into `~/.gitconfig` with that bashrc's `USER` / `EMAIL`. A different existing `~/.gitconfig` is saved as `~/.gitconfig.backup-<date>`; an identical one is left alone. It is the global level only: a repo's own `.git/config` still overrides it. `core.excludesfile` points at `~/.gitignore`, ignored by git when the file does not exist.
7. Installs Python CLIs via `pipx` (`PyMuPDF` → `pymupdf`, `Markdown` → `markdown_py`) — skipped if `pipx` is absent.
8. Copies the `bin/` scripts (`dt`, `dtach-router`, `claude-provider`) into `~/.local/bin`. The dtach session-resume menu ships in the deployed bashrc (both OSes), so every interactive shell offers it — including VS Code Remote-SSH terminals, which are non-login and never read `~/.profile`. The installer also strips any older dtach block left in `~/.profile` so a plain SSH login doesn't prompt twice.
9. On Linux, installs `etc/profile.d/disk-usage-warning.sh` to `/etc/profile.d/` (needs `sudo`) so each login warns when `/` or `/home` cross 85% usage.
10. On Linux, installs **code-server** (VS Code in the browser) via its vendor script — skipped if already present — and enables the `code-server@$USER` systemd service.
10. On Linux, installs **code-server** (VS Code in the browser) via its vendor script — skipped if already present — and enables the `code-server@<login>` systemd service (login from `id -un`: the bashrc overrides `$USER`).
11. On Linux, installs **`ubuntu-desktop-minimal`** (GDM + GNOME Shell, ~1.5 GB): the RDP remote login below hands out a GNOME session, which a bare server install does not have. Then sets up **RDP remote login** via `gnome-remote-desktop` (Wayland-native): installs the daemon + `openssl`, generates a self-signed TLS cert once, and prompts interactively for shared "gate" credentials (skipped when no terminal is attached, or already set). Disables `xrdp` if present; opens UFW port `3389` only when UFW is already active. Finally, when `lspci` sees an NVIDIA GPU, runs `ubuntu-drivers install` to put on the driver the distro recommends for the card (no version pinned; loads at the next reboot). Skipped on machines without an NVIDIA GPU.
12. On Linux, installs the **`cloudpex`** NAS mount helper to `/usr/local/bin` via `cloudpex/install.sh`, which prompts for the NAS host, share name, SMB user, mount point and SMB version and writes them to `/etc/cloudpex.conf` (root, `0600`; an existing config is shown and kept unless you say `n`; skipped when no terminal is attached). Nothing is mounted, no password stored, see [`cloudpex/README.md`](cloudpex/README.md).
13. On Linux, installs the **security baseline**, always, no prompt: **fail2ban** (+ `nftables`) with `etc/fail2ban/jail.d/local.conf` (sshd jail reading the journal, bans the offending IP on every port so the SSH port does not matter, 5 failures in 10 min → 1 h ban, loopback and private LAN ranges never banned); **unattended-upgrades** enabled through `etc/apt/apt.conf.d/20auto-upgrades`; and the **sshd drop-in** `etc/ssh/sshd_config.d/20-hardening.conf` (`PermitRootLogin no`, `MaxAuthTries 3`, `LoginGraceTime 20`), checked with `sshd -t` and removed again if sshd rejects it, then `reload ssh`. Authentication methods, port and user lists are left as they are.
+31
View File
@@ -0,0 +1,31 @@
# Template for the user-scope ~/.gitconfig, rendered by install.sh.
# Git never expands $VARS: @USER@ and @EMAIL@ are replaced at install
# time with the USER and EMAIL exported by the deployed bashrc.
# A repo .git/config still overrides these values for that repo.
[user]
name = @USER@
email = @EMAIL@
[push]
default = current
[color]
ui = auto
[pull]
rebase = true
[core]
editor = vim
pager = delta
excludesfile = ~/.gitignore
[advice]
detachedHead = false
[merge]
tool = vimdiff
conflictStyle = zdiff3
[pager]
branch = false
[url "git@salsa.debian.org:installer-team/"]
pushInsteadOf = https://salsa.debian.org/installer-team/
[interactive]
diffFilter = delta --color-only
[delta]
navigate = true # use n and N to move between diff sections
dark = true # or light = true, or omit for auto-detection
+52 -4
View File
@@ -364,7 +364,8 @@ set_login_shell() {
if ! grep -qxF "$target" /etc/shells; then
echo "$target" | sudo tee -a /etc/shells >/dev/null
fi
current="$(dscl . -read "/Users/$USER" UserShell | awk '{ print $2 }')"
# id -un, not $USER: the deployed rc sets USER to the git/vim identity.
current="$(dscl . -read "/Users/$(id -un)" UserShell | awk '{ print $2 }')"
if [ "$current" = "$target" ]; then
echo "Login shell already $target — skipping"
return 0
@@ -429,6 +430,50 @@ wire_bash_profile() {
"$line" >> "$profile"
}
# Value of `export NAME=value` ($1) in the rc file $2, quotes stripped. The last
# match wins, as when the shell sources it. Empty when absent.
rc_export_value() {
sed -n "s/^export $1=//p" "$2" | tail -n 1 | tr -d "\"'"
}
# Print the repo gitconfig template with @USER@ and @EMAIL@ replaced by $1 and
# $2. Bash substitution, so the values need no sed escaping.
render_gitconfig() {
local name="$1" email="$2" line
while IFS= read -r line || [ -n "$line" ]; do
line="${line//@USER@/$name}"
line="${line//@EMAIL@/$email}"
printf '%s\n' "$line"
done < "$SCRIPT_DIR/gitconfig"
}
# Install the user-scope ~/.gitconfig (a repo's own .git/config still wins).
# The identity is read from the USER/EMAIL exports of the deployed rc ($1), so
# git and the shell agree. A ~/.gitconfig that differs is kept as
# ~/.gitconfig.backup-<date>, outside ~/Oldconfig which every run wipes.
# Idempotent: an identical ~/.gitconfig is left alone.
deploy_gitconfig() {
local rc="$1" name email rendered backup
name="$(rc_export_value USER "$rc")"
email="$(rc_export_value EMAIL "$rc")"
if [ -z "$name" ] || [ -z "$email" ]; then
echo "USER/EMAIL not exported by $rc — skipping ~/.gitconfig" >&2
return 0
fi
rendered="$(render_gitconfig "$name" "$email")"
if printf '%s\n' "$rendered" | cmp -s - "$HOME/.gitconfig"; then
echo "$HOME/.gitconfig already up to date — skipping"
return 0
fi
if [ -e "$HOME/.gitconfig" ]; then
backup="$HOME/.gitconfig.backup-$(date +%Y%m%d-%H%M%S)"
echo "Saving the current ~/.gitconfig to $backup"
mv "$HOME/.gitconfig" "$backup"
fi
echo "Deploying gitconfig to ~/.gitconfig ($name <$email>)"
printf '%s\n' "$rendered" > "$HOME/.gitconfig"
}
# What the Linux install sets up that this macOS run did not, and why.
print_macos_gaps() {
cat <<'EOF'
@@ -463,8 +508,7 @@ if command -v apt-get >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get upgrade -y
# Build + version control + C dev tooling (gitleaks backs the pre-commit hook).
# Web stack: MariaDB + PHP modules for local WordPress/LAMP work; the php-* metapackages
# Build + version control + C dev tooling (gitleaks backs the pre-commit hook). # Web stack: MariaDB + PHP modules for local WordPress/LAMP work; the php-* metapackages
# follow the distro's PHP version instead of pinning php8.x-*.
sudo apt-get install -y \
vim git git-lfs git-filter-repo gitleaks gcc make pkg-config dkms valgrind shellcheck \
@@ -483,7 +527,8 @@ if command -v apt-get >/dev/null 2>&1; then
if ! command -v code-server >/dev/null 2>&1; then
curl -fsSL https://code-server.dev/install.sh | sh
fi
sudo systemctl enable --now "code-server@$USER"
# id -un, not $USER: the deployed bashrc sets USER to the git/vim identity.
sudo systemctl enable --now "code-server@$(id -un)"
# GNOME desktop (GDM + Shell): the RDP remote login below needs a GNOME session
# to hand out; a bare server install has none. Ubuntu-only metapackage.
@@ -558,6 +603,9 @@ fi
echo "Deploying $bashrc"
cp "$SCRIPT_DIR/$bashrc" "$HOME/.bashrc"
# User-scope git config, identity taken from the bashrc just deployed.
deploy_gitconfig "$SCRIPT_DIR/$bashrc"
# Python CLIs via pipx (run as the user, never sudo). Skipped if pipx is absent.
if command -v pipx >/dev/null 2>&1; then
echo "Installing pipx CLIs (PyMuPDF -> pymupdf, Markdown -> markdown_py)"