diff --git a/.github/workflows/shell-quality.yml b/.github/workflows/shell-quality.yml index a8e38e0..2a75d0d 100644 --- a/.github/workflows/shell-quality.yml +++ b/.github/workflows/shell-quality.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - develop pull_request: jobs: @@ -13,8 +14,8 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Install ShellCheck - run: sudo apt-get update && sudo apt-get install -y shellcheck + - name: Install shell tools + run: sudo apt-get update && sudo apt-get install -y shellcheck zsh - name: Set up Go uses: actions/setup-go@v5 diff --git a/README.md b/README.md index 339010d..f31cf9b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Ubuntu 24.04 Developer Bootstrap (WSL2 + native) +# Ubuntu 26.04 Developer Bootstrap -Reproducible, idempotent bootstrap for Ubuntu 24.04 that installs a full developer distro and applies dotfiles via chezmoi. +Idempotent developer bootstrap for Ubuntu 26.04 LTS on bare-metal/VM installations and WSL2. The installer detects WSL automatically; Windows integration is never configured on native Ubuntu. ## One-liner install @@ -8,9 +8,17 @@ Reproducible, idempotent bootstrap for Ubuntu 24.04 that installs a full develop git clone https://github.com/w0rldx/dotfiles.git ~/.dotfiles && bash ~/.dotfiles/bootstrap/install.sh ``` -## WSL2 systemd requirement (mandatory for Podman rootless) +## Platform-specific behavior -Podman rootless socket activation uses `systemctl --user`, which requires systemd. If running in WSL2: +On native Ubuntu, the bootstrap uses the normal Linux PATH and optionally installs native VS Code. Under WSL it additionally: + +- removes conflicting Windows Node.js, pnpm, and PowerShell entries from PATH; +- skips the native VS Code package and prints WSL extension guidance; +- prints WSL-specific systemd guidance only when systemd is unavailable. + +## WSL2 systemd (optional) + +Rootless Docker uses systemd user services when available. If systemd is not running, the bootstrap falls back to starting `dockerd-rootless.sh` in the user session and zsh auto-starts it on login. If running in WSL2 and you want systemd: 1. Edit `/etc/wsl.conf`: @@ -29,6 +37,13 @@ wsl.exe --shutdown Microsoft Learn: https://learn.microsoft.com/windows/wsl/systemd +## Docker (rootless) + +- Installed from Docker's official Ubuntu apt repo (docker-ce, docker-ce-rootless-extras, etc.). +- `DOCKER_HOST` is set to `unix://$XDG_RUNTIME_DIR/docker.sock` with a fallback to `~/.docker/run` when systemd is unavailable. +- With systemd: user service `docker.service` is enabled and linger is turned on. +- Without systemd: `~/.config/docker/rootless-session.sh` starts `dockerd-rootless.sh` on login if it's not already running. + ## VS Code on WSL (recommended) - Install VS Code on Windows (not inside WSL). @@ -44,25 +59,9 @@ Native Linux VS Code is opt-in. Set the environment variable to install via the INSTALL_VSCODE_LINUX=1 ./bootstrap/install.sh ``` -## Apt PPAs - -- The bootstrap adds `ppa:zhangsongcui3371/fastfetch` before apt installs. -- Add additional PPAs in `bootstrap/steps/05-apt-ppa.sh`. +## Mise-managed developer toolchain -## Neovim (tarball) + LazyVim Starter - -- Neovim is installed from the official tarball into `/opt/nvim-linux-x86_64` (x86_64 only; other archs will error out). -- PATH is set via zsh env: `export PATH="$PATH:/opt/nvim-linux-x86_64/bin"` (managed in `chezmoi/dot_zshenv`). -- LazyVim Starter setup: - - If `~/.config/nvim` already looks like LazyVim (and `.git` is removed), the step is a NOOP. - - Existing Neovim dirs are backed up with `.bak` (timestamped if needed). - - Starter is cloned to `~/.config/nvim` and `.git` is removed. - - Overlay dotfiles from `chezmoi/dot_config/nvim/` are applied via `rsync -a` (no deletions). - -## Python + uv - -- Python is installed via mise (`python = "latest"` in `mise/mise.toml`). -- uv is installed by `bootstrap/steps/31-python-uv.sh`. +Language runtimes and user-level CLI tools are declared in `mise/mise.toml` and installed together by `bootstrap/steps/30-mise.sh`. Run `mise outdated` to inspect available updates and `mise upgrade` to update the toolchain. ## Dotfiles with chezmoi @@ -72,26 +71,19 @@ INSTALL_VSCODE_LINUX=1 ./bootstrap/install.sh - `chezmoi diff` - `chezmoi apply` - `chezmoi update` -- Neovim overlay files live in `chezmoi/dot_config/nvim/` and are layered on top of the LazyVim Starter base. - -## Global package lists - -- NPM globals live in `bootstrap/packages/npm.txt` and are installed by `bootstrap/steps/36-npm-packages.sh`. -- Cargo globals live in `bootstrap/packages/cargo.txt` and are installed by `bootstrap/steps/35-cargo-packages.sh`. ## Shell quality - Lint: `./scripts/lint-shell.sh` - Format: `./scripts/format-shell.sh` - Prereqs: - - ShellCheck is installed by the bootstrap (or via `sudo apt-get install -y shellcheck`). - - shfmt via Go: `go install mvdan.cc/sh/v3/cmd/shfmt@latest` + - ShellCheck and shfmt are managed by mise; zsh is installed through apt. ## What this installs -- Apt packages: zsh, git, curl, ca-certificates, fzf, ripgrep, fd-find (+ fd symlink), eza, zoxide, rsync, podman, podman-compose -- From official sources/releases: GitHub CLI (apt repo), Oh-My-Zsh, Powerlevel10k theme, zsh-autosuggestions, zsh-syntax-highlighting, mise, lazygit, lazydocker, try, Neovim (tarball to /opt) -- Toolchains via mise: Rust (cargo), Go, .NET SDK, Node.js LTS, Bun +- Apt packages: zsh, git, curl, ca-certificates, build-essential, unzip, rsync +- System/custom sources: Docker Engine, Oh-My-Zsh, Powerlevel10k, zsh-autosuggestions, zsh-syntax-highlighting, mise, try, and optional native VS Code +- Mise toolchain: Node.js LTS, pnpm, Go, Rust, .NET SDK, Bun, Python, uv, Codex, GitHub CLI, lazygit, lazydocker, chezmoi, fzf, ripgrep, fd, eza, fastfetch, ShellCheck, and shfmt ## Usage @@ -116,9 +108,8 @@ INSTALL_VSCODE_LINUX=1 ./bootstrap/install.sh ./bootstrap/install.sh ``` - Targeted updates: + - `mise upgrade` - `chezmoi apply` - - `./bootstrap/steps/36-npm-packages.sh` - - `./bootstrap/steps/35-cargo-packages.sh` If the installer is not executable, either run: diff --git a/bootstrap/install.sh b/bootstrap/install.sh old mode 100644 new mode 100755 index bbb53b1..69f64e9 --- a/bootstrap/install.sh +++ b/bootstrap/install.sh @@ -12,17 +12,11 @@ log "Starting bootstrap" steps=( "00-preflight.sh" - "05-apt-ppa.sh" "10-apt.sh" "20-zsh-ohmyzsh.sh" "30-mise.sh" - "31-python-uv.sh" - "35-cargo-packages.sh" - "36-npm-packages.sh" - "40-podman.sh" - "50-tools-github.sh" - "50-neovim.sh" - "55-lazyvim.sh" + "40-docker.sh" + "50-try.sh" "60-dotfiles.sh" "70-vscode.sh" "90-doctor.sh" diff --git a/bootstrap/lib/common.sh b/bootstrap/lib/common.sh old mode 100644 new mode 100755 index e5ea512..6b01425 --- a/bootstrap/lib/common.sh +++ b/bootstrap/lib/common.sh @@ -24,7 +24,24 @@ need_cmd() { } is_wsl() { - grep -qi microsoft /proc/version 2>/dev/null || [ -n "${WSL_INTEROP:-}" ] || [ -n "${WSL_DISTRO_NAME:-}" ] + [ -n "${WSL_INTEROP:-}" ] || + [ -n "${WSL_DISTRO_NAME:-}" ] || + { + grep -qi microsoft /proc/sys/kernel/osrelease 2>/dev/null && + { + [ -d /run/WSL ] || + [ -e /proc/sys/fs/binfmt_misc/WSLInterop ] || + [ -d /mnt/c/Windows ] + } + } +} + +platform_name() { + if is_wsl; then + printf 'WSL' + else + printf 'native Ubuntu' + fi } is_ubuntu() { @@ -55,8 +72,14 @@ link_file() { src="$1" dest="$2" - if [ -L "${dest}" ] && [ "$(readlink "${dest}")" = "${src}" ]; then - log "Link exists: ${dest}" + if [ -L "${dest}" ]; then + if [ "$(readlink "${dest}")" = "${src}" ]; then + log "Link exists: ${dest}" + return 0 + fi + + ln -sfn "${src}" "${dest}" + log "Updated link: ${dest} -> ${src}" return 0 fi diff --git a/bootstrap/packages/cargo.txt b/bootstrap/packages/cargo.txt deleted file mode 100644 index 4b88910..0000000 --- a/bootstrap/packages/cargo.txt +++ /dev/null @@ -1,3 +0,0 @@ -# One crate per line. Lines starting with # are ignored. -# Example: -# ripgrep diff --git a/bootstrap/packages/npm.txt b/bootstrap/packages/npm.txt deleted file mode 100644 index 0dea701..0000000 --- a/bootstrap/packages/npm.txt +++ /dev/null @@ -1,3 +0,0 @@ -# One package per line. Lines starting with # are ignored. -@openai/codex -@charmland/crush diff --git a/bootstrap/steps/00-preflight.sh b/bootstrap/steps/00-preflight.sh old mode 100644 new mode 100755 index fd47398..f07d84e --- a/bootstrap/steps/00-preflight.sh +++ b/bootstrap/steps/00-preflight.sh @@ -17,15 +17,17 @@ if ! is_ubuntu; then fi version_id="$(ubuntu_version_id)" -if [ "${version_id}" != "24.04" ]; then - die "This installer targets Ubuntu 24.04. Detected: ${version_id}." +if [ "${version_id}" != "26.04" ]; then + die "This installer targets Ubuntu 26.04. Detected: ${version_id}." fi +log "Detected $(platform_name) on Ubuntu ${version_id}" + if is_wsl; then if [ ! -d /run/systemd/system ]; then + warn "WSL detected, but systemd is not running. Using rootless Docker session fallback." cat >&2 <<'EOM' -[ERROR] WSL detected, but systemd is not running. -Enable systemd in /etc/wsl.conf: +[WARN] For systemd user services (recommended), enable systemd in /etc/wsl.conf: [boot] systemd=true @@ -35,8 +37,11 @@ Then run from Windows: Re-open your WSL distro and rerun the installer. EOM - exit 1 + else + log "WSL systemd is available" fi +elif [ ! -d /run/systemd/system ]; then + warn "Native Ubuntu detected without systemd; service setup will use available fallbacks." fi log "Preflight OK" diff --git a/bootstrap/steps/05-apt-ppa.sh b/bootstrap/steps/05-apt-ppa.sh deleted file mode 100644 index bcaddf6..0000000 --- a/bootstrap/steps/05-apt-ppa.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' - -BOOTSTRAP_ROOT="${BOOTSTRAP_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" -# shellcheck source=bootstrap/lib/common.sh -. "${BOOTSTRAP_ROOT}/bootstrap/lib/common.sh" - -ppa_slug="zhangsongcui3371/fastfetch" -ppa="ppa:${ppa_slug}" -ppa_pattern="ppa.launchpad.net/${ppa_slug}/ubuntu" - -list_files=(/etc/apt/sources.list /etc/apt/sources.list.d/*.list) -if grep -Rqs "${ppa_pattern}" "${list_files[@]}" 2>/dev/null; then - log "PPA already present: ${ppa}" - exit 0 -fi - -if ! have_cmd add-apt-repository; then - log "Installing software-properties-common for add-apt-repository" - sudo apt-get update - sudo apt-get install -y software-properties-common -fi - -log "Adding PPA: ${ppa}" -sudo add-apt-repository -y --no-update "${ppa}" diff --git a/bootstrap/steps/10-apt.sh b/bootstrap/steps/10-apt.sh old mode 100644 new mode 100755 index a193f34..ccddf11 --- a/bootstrap/steps/10-apt.sh +++ b/bootstrap/steps/10-apt.sh @@ -11,17 +11,9 @@ packages=( git curl ca-certificates - gcc - fzf - ripgrep - fd-find - eza - zoxide + build-essential + unzip rsync - shellcheck - fastfetch - podman - podman-compose ) log "Updating apt metadata" @@ -29,9 +21,3 @@ sudo apt-get update log "Installing apt packages" sudo apt-get install -y "${packages[@]}" - -if have_cmd fdfind && ! have_cmd fd; then - mkdir -p "${HOME}/.local/bin" - ln -sf "$(command -v fdfind)" "${HOME}/.local/bin/fd" - log "Linked fd -> fdfind in ~/.local/bin" -fi diff --git a/bootstrap/steps/20-zsh-ohmyzsh.sh b/bootstrap/steps/20-zsh-ohmyzsh.sh old mode 100644 new mode 100755 diff --git a/bootstrap/steps/30-mise.sh b/bootstrap/steps/30-mise.sh old mode 100644 new mode 100755 index 81b1cba..0d1fbff --- a/bootstrap/steps/30-mise.sh +++ b/bootstrap/steps/30-mise.sh @@ -34,5 +34,59 @@ else yes | mise trust "${config_dest}" fi -log "Installing mise toolchains" +log "Installing mise toolchains and CLI tools" MISE_CONFIG_FILE="${config_src}" mise install + +retired_mise_tools=( + neovim + zoxide +) + +for tool in "${retired_mise_tools[@]}"; do + if mise ls --installed --no-header "${tool}" 2>/dev/null | grep -q "^${tool}[[:space:]]"; then + log "Removing retired mise tool: ${tool}" + mise uninstall --all --yes "${tool}" + fi +done + +legacy_apt_packages=( + fzf + ripgrep + fd-find + eza + zoxide + shellcheck + shfmt + fastfetch + neovim + gh +) + +installed_legacy_packages=() +for package in "${legacy_apt_packages[@]}"; do + if dpkg -s "${package}" >/dev/null 2>&1; then + installed_legacy_packages+=("${package}") + fi +done + +if [ "${#installed_legacy_packages[@]}" -gt 0 ]; then + log "Removing obsolete apt tool copies: ${installed_legacy_packages[*]}" + sudo apt-get remove -y "${installed_legacy_packages[@]}" +fi + +sudo rm -f \ + /etc/apt/sources.list.d/github-cli.list \ + /usr/share/keyrings/githubcli-archive-keyring.gpg + +for binary in chezmoi lazygit lazydocker uv uvx; do + legacy_binary="${HOME}/.local/bin/${binary}" + if [ -e "${legacy_binary}" ] || [ -L "${legacy_binary}" ]; then + rm -f "${legacy_binary}" + log "Removed legacy binary: ${legacy_binary}" + fi +done + +if MISE_CONFIG_FILE="${config_src}" mise exec -- pnpm list -g --depth=0 @openai/codex >/dev/null 2>&1; then + log "Removing pnpm copy of @openai/codex" + MISE_CONFIG_FILE="${config_src}" mise exec -- pnpm remove -g @openai/codex +fi diff --git a/bootstrap/steps/31-python-uv.sh b/bootstrap/steps/31-python-uv.sh deleted file mode 100644 index c8b6a10..0000000 --- a/bootstrap/steps/31-python-uv.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' - -BOOTSTRAP_ROOT="${BOOTSTRAP_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" -# shellcheck source=bootstrap/lib/common.sh -. "${BOOTSTRAP_ROOT}/bootstrap/lib/common.sh" - -need_cmd curl - -if have_cmd uv; then - log "uv already installed" - exit 0 -fi - -install_url="https://astral.sh/uv/install.sh" -log "Installing uv via ${install_url}" -tmpfile="$(download_to_tmp "${install_url}")" -sh "${tmpfile}" -rm -f "${tmpfile}" diff --git a/bootstrap/steps/35-cargo-packages.sh b/bootstrap/steps/35-cargo-packages.sh deleted file mode 100644 index 654f587..0000000 --- a/bootstrap/steps/35-cargo-packages.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' - -BOOTSTRAP_ROOT="${BOOTSTRAP_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" -# shellcheck source=bootstrap/lib/common.sh -. "${BOOTSTRAP_ROOT}/bootstrap/lib/common.sh" - -packages_file="${BOOTSTRAP_ROOT}/bootstrap/packages/cargo.txt" - -trim() { - local s - s="$1" - s="${s#"${s%%[![:space:]]*}"}" - s="${s%"${s##*[![:space:]]}"}" - printf '%s' "${s}" -} - -if ! have_cmd cargo; then - warn "cargo not found; skipping cargo packages" - exit 0 -fi - -if [ ! -f "${packages_file}" ]; then - warn "Cargo package list not found: ${packages_file}" - exit 0 -fi - -installed_list="$(cargo install --list 2>/dev/null || true)" - -count=0 -while IFS= read -r line || [ -n "${line}" ]; do - line="${line%%#*}" - line="$(trim "${line}")" - if [ -z "${line}" ]; then - continue - fi - - count=$((count + 1)) - if printf '%s\n' "${installed_list}" | grep -q "^${line} v"; then - log "cargo package already installed: ${line}" - else - log "Installing cargo package: ${line}" - cargo install "${line}" - fi -done <"${packages_file}" - -if [ "${count}" -eq 0 ]; then - log "No cargo packages configured" -fi diff --git a/bootstrap/steps/36-npm-packages.sh b/bootstrap/steps/36-npm-packages.sh deleted file mode 100644 index b7171a7..0000000 --- a/bootstrap/steps/36-npm-packages.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' - -BOOTSTRAP_ROOT="${BOOTSTRAP_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" -# shellcheck source=bootstrap/lib/common.sh -. "${BOOTSTRAP_ROOT}/bootstrap/lib/common.sh" - -packages_file="${BOOTSTRAP_ROOT}/bootstrap/packages/npm.txt" - -trim() { - local s - s="$1" - s="${s#"${s%%[![:space:]]*}"}" - s="${s%"${s##*[![:space:]]}"}" - printf '%s' "${s}" -} - -if ! have_cmd npm; then - warn "npm not found; skipping npm packages" - exit 0 -fi - -if [ ! -f "${packages_file}" ]; then - warn "NPM package list not found: ${packages_file}" - exit 0 -fi - -count=0 -while IFS= read -r line || [ -n "${line}" ]; do - line="${line%%#*}" - line="$(trim "${line}")" - if [ -z "${line}" ]; then - continue - fi - - count=$((count + 1)) - if npm list -g --depth=0 "${line}" >/dev/null 2>&1; then - log "npm package already installed: ${line}" - else - log "Installing npm package: ${line}" - npm install -g "${line}" - fi -done <"${packages_file}" - -if [ "${count}" -eq 0 ]; then - log "No npm packages configured" -fi diff --git a/bootstrap/steps/40-docker.sh b/bootstrap/steps/40-docker.sh new file mode 100755 index 0000000..16867ba --- /dev/null +++ b/bootstrap/steps/40-docker.sh @@ -0,0 +1,168 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' + +BOOTSTRAP_ROOT="${BOOTSTRAP_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" +# shellcheck source=bootstrap/lib/common.sh +. "${BOOTSTRAP_ROOT}/bootstrap/lib/common.sh" + +need_cmd sudo +need_cmd curl + +target_user="${SUDO_USER:-${USER:-$(id -un)}}" +if [ -z "${target_user}" ]; then + die "Unable to determine target user." +fi +if [ "${target_user}" = "root" ]; then + die "Run this step as a regular user (not root)." +fi + +target_uid="$(id -u "${target_user}")" +target_home="$(getent passwd "${target_user}" | cut -d: -f6)" +if [ -z "${target_home}" ]; then + target_home="${HOME}" +fi + +run_as_user() { + if [ "$(id -un)" = "${target_user}" ]; then + "$@" + else + sudo -u "${target_user}" -H "$@" + fi +} + +conflicting_packages=( + docker.io + docker-doc + docker-compose + docker-compose-v2 + podman-docker + containerd + runc +) + +remove_packages=() +for pkg in "${conflicting_packages[@]}"; do + if dpkg -s "${pkg}" >/dev/null 2>&1; then + remove_packages+=("${pkg}") + fi +done + +if [ "${#remove_packages[@]}" -gt 0 ]; then + log "Removing conflicting container packages: ${remove_packages[*]}" + sudo apt-get remove -y "${remove_packages[@]}" +else + log "No conflicting container packages installed" +fi + +log "Adding Docker apt repository" +sudo install -m 0755 -d /etc/apt/keyrings +docker_key="$(download_to_tmp "https://download.docker.com/linux/ubuntu/gpg")" +sudo install -m 0644 "${docker_key}" /etc/apt/keyrings/docker.asc +rm -f "${docker_key}" + +arch="$(dpkg --print-architecture)" +# shellcheck disable=SC1091 +codename="$(. /etc/os-release && printf '%s' "${VERSION_CODENAME}")" +sudo rm -f /etc/apt/sources.list.d/docker.list +sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null </dev/null 2>&1 || true +fi + +ensure_subid() { + local file flag + file="$1" + flag="$2" + if ! grep -q "^${target_user}:" "${file}" 2>/dev/null; then + log "Adding subid range in ${file} for ${target_user}" + sudo usermod "${flag}" "100000-65536" "${target_user}" + fi +} + +ensure_subid /etc/subuid --add-subuids +ensure_subid /etc/subgid --add-subgids + +need_cmd dockerd-rootless-setuptool.sh +need_cmd dockerd-rootless.sh + +systemd_user_available=0 +if have_cmd systemctl && [ -d /run/systemd/system ]; then + if run_as_user systemctl --user show-environment >/dev/null 2>&1; then + systemd_user_available=1 + fi +fi + +runtime_dir="/run/user/${target_uid}" +if [ "${systemd_user_available}" -eq 0 ]; then + runtime_dir="${target_home}/.docker/run" +fi + +docker_host="unix://${runtime_dir}/docker.sock" + +log "Configuring rootless Docker for ${target_user}" +if [ "${systemd_user_available}" -eq 0 ]; then + run_as_user mkdir -p "${runtime_dir}" + run_as_user chmod 700 "${runtime_dir}" 2>/dev/null || true + run_as_user env XDG_RUNTIME_DIR="${runtime_dir}" dockerd-rootless-setuptool.sh install +else + run_as_user dockerd-rootless-setuptool.sh install +fi + +if [ "${systemd_user_available}" -eq 1 ]; then + if have_cmd loginctl; then + sudo loginctl enable-linger "${target_user}" >/dev/null 2>&1 || true + fi + run_as_user systemctl --user daemon-reload + if ! run_as_user systemctl --user enable --now docker; then + warn "Failed to enable docker user service; falling back to manual start." + systemd_user_available=0 + fi +fi + +if [ "${systemd_user_available}" -eq 0 ]; then + if ! run_as_user env DOCKER_HOST="${docker_host}" docker info >/dev/null 2>&1; then + log "Starting rootless dockerd in the user session" + run_as_user env XDG_RUNTIME_DIR="${runtime_dir}" DOCKER_HOST="${docker_host}" sh -c \ + "nohup dockerd-rootless.sh --host=\"${docker_host}\" >\"${runtime_dir}/dockerd-rootless.log\" 2>&1 &" + fi +fi + +docker_ready=0 +for _ in 1 2 3 4 5; do + if run_as_user env DOCKER_HOST="${docker_host}" docker info >/dev/null 2>&1; then + docker_ready=1 + break + fi + sleep 1 +done + +if [ "${docker_ready}" -eq 1 ]; then + log "Docker rootless is running" + if run_as_user env DOCKER_HOST="${docker_host}" docker run --rm hello-world >/dev/null 2>&1; then + log "OK: docker run hello-world" + else + warn "docker run hello-world failed (network/image access?)" + fi +else + warn "Docker rootless is not responding yet (check dockerd-rootless logs)." +fi diff --git a/bootstrap/steps/40-podman.sh b/bootstrap/steps/40-podman.sh deleted file mode 100644 index e2762fc..0000000 --- a/bootstrap/steps/40-podman.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' - -BOOTSTRAP_ROOT="${BOOTSTRAP_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" -# shellcheck source=bootstrap/lib/common.sh -. "${BOOTSTRAP_ROOT}/bootstrap/lib/common.sh" - -need_cmd podman -need_cmd systemctl - -missing_deps=() - -if ! have_cmd newuidmap || ! have_cmd newgidmap; then - missing_deps+=(uidmap) -fi - -if ! have_cmd slirp4netns; then - missing_deps+=(slirp4netns) -fi - -if ! have_cmd fuse-overlayfs; then - missing_deps+=(fuse-overlayfs) -fi - -if [ "${#missing_deps[@]}" -gt 0 ]; then - log "Installing Podman rootless dependencies: ${missing_deps[*]}" - sudo apt-get install -y "${missing_deps[@]}" -fi - -log "Enabling Podman user socket" -if ! systemctl --user enable --now podman.socket; then - die "Failed to enable podman.socket (systemd user session required)." -fi - -log "Podman rootless socket enabled" diff --git a/bootstrap/steps/50-neovim.sh b/bootstrap/steps/50-neovim.sh deleted file mode 100644 index 969af45..0000000 --- a/bootstrap/steps/50-neovim.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' - -BOOTSTRAP_ROOT="${BOOTSTRAP_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" -# shellcheck source=bootstrap/lib/common.sh -. "${BOOTSTRAP_ROOT}/bootstrap/lib/common.sh" - -need_cmd curl -need_cmd tar - -arch="$(uname -m)" -if [ "${arch}" != "x86_64" ]; then - die "Unsupported architecture for Neovim tarball install: ${arch}. Expected x86_64." -fi - -tmpdir="$(mktemp -d)" -cleanup() { - rm -rf "${tmpdir}" -} -trap cleanup EXIT - -log "Downloading Neovim tarball from https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.tar.gz" -( - cd "${tmpdir}" - curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.tar.gz - sudo rm -rf /opt/nvim-linux-x86_64 - sudo tar -C /opt -xzf nvim-linux-x86_64.tar.gz -) diff --git a/bootstrap/steps/50-tools-github.sh b/bootstrap/steps/50-tools-github.sh deleted file mode 100644 index b9ed43e..0000000 --- a/bootstrap/steps/50-tools-github.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' - -BOOTSTRAP_ROOT="${BOOTSTRAP_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" -# shellcheck source=bootstrap/lib/common.sh -. "${BOOTSTRAP_ROOT}/bootstrap/lib/common.sh" - -need_cmd curl -need_cmd tar - -arch="$(uname -m)" -case "${arch}" in - x86_64) - lazygit_arch="x86_64" - ;; - aarch64 | arm64) - lazygit_arch="arm64" - ;; - *) - die "Unsupported architecture: ${arch}" - ;; -esac - -install_gh() { - if have_cmd gh; then - log "gh already installed" - return 0 - fi - - log "Setting up GitHub CLI apt repository" - sudo mkdir -p /usr/share/keyrings - - if [ ! -f /usr/share/keyrings/githubcli-archive-keyring.gpg ]; then - key_url="https://cli.github.com/packages/githubcli-archive-keyring.gpg" - tmpfile="$(download_to_tmp "${key_url}")" - sudo install -m 0644 "${tmpfile}" /usr/share/keyrings/githubcli-archive-keyring.gpg - rm -f "${tmpfile}" - fi - - list_file="/etc/apt/sources.list.d/github-cli.list" - repo_line="deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" - - if [ ! -f "${list_file}" ] || ! grep -q "cli.github.com/packages" "${list_file}"; then - echo "${repo_line}" | sudo tee "${list_file}" >/dev/null - fi - - sudo apt-get update - sudo apt-get install -y gh -} - -latest_github_tag() { - local repo tag - repo="$1" - tag="$(curl -fsSLI "https://github.com/${repo}/releases/latest" | tr -d '\r' | awk -F'/' '/^location:/I {print $NF}')" - if [ -z "${tag}" ]; then - return 1 - fi - printf '%s' "${tag}" -} - -install_lazygit() { - if have_cmd lazygit; then - log "lazygit already installed" - return 0 - fi - - tag="$(latest_github_tag "jesseduffield/lazygit")" || die "Unable to resolve lazygit release tag" - if [ -z "${tag}" ]; then - die "Unable to resolve lazygit release tag" - fi - version="${tag#v}" - asset="lazygit_${version}_Linux_${lazygit_arch}.tar.gz" - url="https://github.com/jesseduffield/lazygit/releases/download/${tag}/${asset}" - - tmpfile="$(download_to_tmp "${url}")" - tmpdir="$(mktemp -d)" - tar -xzf "${tmpfile}" -C "${tmpdir}" - mkdir -p "${HOME}/.local/bin" - install -m 0755 "${tmpdir}/lazygit" "${HOME}/.local/bin/lazygit" - rm -rf "${tmpfile}" "${tmpdir}" -} - -install_lazydocker() { - if have_cmd lazydocker; then - log "lazydocker already installed" - return 0 - fi - - script_url="https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh" - tmpfile="$(download_to_tmp "${script_url}")" - bash "${tmpfile}" - rm -f "${tmpfile}" -} - -install_try() { - if [ -x "${HOME}/.local/bin/try" ]; then - log "try already installed" - return 0 - fi - - script_url="https://raw.githubusercontent.com/binpash/try/master/try" - tmpfile="$(download_to_tmp "${script_url}")" - mkdir -p "${HOME}/.local/bin" - install -m 0755 "${tmpfile}" "${HOME}/.local/bin/try" - rm -f "${tmpfile}" -} - -install_gh -install_lazygit -install_lazydocker -install_try diff --git a/bootstrap/steps/50-try.sh b/bootstrap/steps/50-try.sh new file mode 100755 index 0000000..2be2aa3 --- /dev/null +++ b/bootstrap/steps/50-try.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' + +BOOTSTRAP_ROOT="${BOOTSTRAP_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" +# shellcheck source=bootstrap/lib/common.sh +. "${BOOTSTRAP_ROOT}/bootstrap/lib/common.sh" + +if [ -x "${HOME}/.local/bin/try" ]; then + log "try already installed" + exit 0 +fi + +script_url="https://raw.githubusercontent.com/binpash/try/master/try" +tmpfile="$(download_to_tmp "${script_url}")" +mkdir -p "${HOME}/.local/bin" +install -m 0755 "${tmpfile}" "${HOME}/.local/bin/try" +rm -f "${tmpfile}" +log "try installed" diff --git a/bootstrap/steps/55-lazyvim.sh b/bootstrap/steps/55-lazyvim.sh deleted file mode 100644 index e6eb27c..0000000 --- a/bootstrap/steps/55-lazyvim.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' - -BOOTSTRAP_ROOT="${BOOTSTRAP_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" -# shellcheck source=bootstrap/lib/common.sh -. "${BOOTSTRAP_ROOT}/bootstrap/lib/common.sh" - -need_cmd git - -nvim_config="${HOME}/.config/nvim" - -is_lazyvim_starter() { - [ -d "${nvim_config}" ] && - [ ! -d "${nvim_config}/.git" ] && - [ -f "${nvim_config}/lua/config/lazy.lua" ] -} - -backup_path() { - local path backup - path="$1" - - if [ ! -e "${path}" ]; then - return 0 - fi - - backup="${path}.bak" - if [ -e "${backup}" ]; then - backup="${path}.bak-$(date +%Y%m%d%H%M%S)" - fi - - mv "${path}" "${backup}" - log "Backed up ${path} -> ${backup}" -} - -if is_lazyvim_starter; then - log "LazyVim starter already present; skipping setup" - exit 0 -fi - -backup_path "${nvim_config}" -backup_path "${HOME}/.local/share/nvim" -backup_path "${HOME}/.local/state/nvim" -backup_path "${HOME}/.cache/nvim" - -log "Cloning LazyVim starter" -git clone https://github.com/LazyVim/starter "${nvim_config}" -rm -rf "${nvim_config}/.git" - -overlay_dir="${BOOTSTRAP_ROOT}/chezmoi/dot_config/nvim" -if [ -d "${overlay_dir}" ]; then - need_cmd rsync - log "Overlaying Neovim dotfiles from ${overlay_dir}" - rsync -a "${overlay_dir}/" "${nvim_config}/" -else - warn "Overlay directory not found: ${overlay_dir}" -fi diff --git a/bootstrap/steps/60-dotfiles.sh b/bootstrap/steps/60-dotfiles.sh old mode 100644 new mode 100755 index 785cbe3..f49d3ed --- a/bootstrap/steps/60-dotfiles.sh +++ b/bootstrap/steps/60-dotfiles.sh @@ -6,27 +6,16 @@ BOOTSTRAP_ROOT="${BOOTSTRAP_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && # shellcheck source=bootstrap/lib/common.sh . "${BOOTSTRAP_ROOT}/bootstrap/lib/common.sh" -if ! have_cmd chezmoi; then - install_url="https://get.chezmoi.io" - tmpfile="$(download_to_tmp "${install_url}")" - mkdir -p "${HOME}/.local/bin" - if ! sh "${tmpfile}" -- -b "${HOME}/.local/bin"; then - warn "chezmoi installer failed with -- -b, retrying with -b" - sh "${tmpfile}" -b "${HOME}/.local/bin" - fi - rm -f "${tmpfile}" -fi +need_cmd mise -export PATH="${HOME}/.local/bin:${PATH}" -if ! have_cmd chezmoi; then - die "chezmoi installation failed" -fi +mise_config="${BOOTSTRAP_ROOT}/mise/mise.toml" +chezmoi=(env MISE_CONFIG_FILE="${mise_config}" mise exec -- chezmoi) chezmoi_state_dir="${HOME}/.local/share/chezmoi" if [ -d "${chezmoi_state_dir}" ]; then log "Applying chezmoi dotfiles" - chezmoi apply + "${chezmoi[@]}" apply else log "Initializing chezmoi" - chezmoi init --apply "${BOOTSTRAP_ROOT}" + "${chezmoi[@]}" init --apply "${BOOTSTRAP_ROOT}" fi diff --git a/bootstrap/steps/70-vscode.sh b/bootstrap/steps/70-vscode.sh old mode 100644 new mode 100755 index 47866be..d062a55 --- a/bootstrap/steps/70-vscode.sh +++ b/bootstrap/steps/70-vscode.sh @@ -7,10 +7,8 @@ BOOTSTRAP_ROOT="${BOOTSTRAP_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && . "${BOOTSTRAP_ROOT}/bootstrap/lib/common.sh" if is_wsl; then - log "WSL detected; ensuring VS Code Server dependencies" - sudo apt-get update - sudo apt-get install -y wget ca-certificates - log "WSL VS Code Server dependencies installed" + log "WSL detected; skipping native VS Code installation" + log "Use VS Code on Windows with the WSL extension, then run: code ." exit 0 fi diff --git a/bootstrap/steps/90-doctor.sh b/bootstrap/steps/90-doctor.sh old mode 100644 new mode 100755 index dc420a3..ff450fe --- a/bootstrap/steps/90-doctor.sh +++ b/bootstrap/steps/90-doctor.sh @@ -28,48 +28,51 @@ check_cmd fzf check_cmd rg check_cmd fd check_cmd eza -check_cmd zoxide -check_cmd podman -check_cmd podman-compose +check_cmd fastfetch +check_cmd docker check_cmd gh check_cmd lazygit check_cmd lazydocker check_cmd try -check_cmd nvim check_cmd mise check_cmd chezmoi check_cmd shellcheck check_cmd shfmt -if have_cmd nvim; then - if nvim --version >/dev/null 2>&1; then - log "OK: nvim --version" - else - warn "nvim --version failed" +if have_cmd systemctl; then + if [ -d /run/systemd/system ]; then + if systemctl --user is-active --quiet docker; then + log "OK: docker.service (user)" + else + warn "MISSING: docker.service (user)" + fi fi fi -if [ -d "${HOME}/.config/nvim/lua" ]; then - log "OK: LazyVim config detected (run :LazyHealth in Neovim)" +runtime_dir="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" +socket="${runtime_dir}/docker.sock" +if [ -S "${socket}" ]; then + log "OK: docker.sock (${socket})" else - warn "MISSING: LazyVim config (~/.config/nvim/lua not found)" - warn "Hint: run Neovim and execute :LazyHealth" + warn "MISSING: docker.sock (${socket})" fi -if have_cmd systemctl; then - if systemctl --user is-active --quiet podman.socket; then - log "OK: podman.socket (user)" +if have_cmd docker; then + if docker compose version >/dev/null 2>&1; then + log "OK: docker compose" else - warn "MISSING: podman.socket (user)" + warn "docker compose unavailable" fi -fi -if have_cmd podman; then - if podman info --format '{{.Host.Security.Rootless}}' >/dev/null 2>&1; then - rootless="$(podman info --format '{{.Host.Security.Rootless}}')" - log "Podman rootless: ${rootless}" + security_opts="$(docker info --format '{{json .SecurityOptions}}' 2>/dev/null || true)" + if [ -n "${security_opts}" ]; then + if printf '%s' "${security_opts}" | grep -q 'rootless'; then + log "Docker rootless: true" + else + warn "Docker rootless: false" + fi else - warn "Podman info unavailable" + warn "Docker info unavailable" fi fi @@ -86,4 +89,6 @@ Then in WSL run: code . This will download/install the VS Code Server automatically. EOM fi +else + log "Native Ubuntu detected; WSL integration checks skipped" fi diff --git a/chezmoi/dot_config/docker/rootless-session.sh b/chezmoi/dot_config/docker/rootless-session.sh new file mode 100644 index 0000000..b003618 --- /dev/null +++ b/chezmoi/dot_config/docker/rootless-session.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' + +if command -v systemctl >/dev/null 2>&1 && [ -d /run/systemd/system ]; then + if systemctl --user is-active --quiet docker 2>/dev/null; then + exit 0 + fi +fi + +if ! command -v dockerd-rootless.sh >/dev/null 2>&1; then + exit 0 +fi + +runtime_dir="${XDG_RUNTIME_DIR:-$HOME/.docker/run}" +if [ ! -d "${runtime_dir}" ]; then + mkdir -p "${runtime_dir}" + chmod 700 "${runtime_dir}" 2>/dev/null || true +fi + +socket="${runtime_dir}/docker.sock" +if [ -S "${socket}" ]; then + if DOCKER_HOST="unix://${socket}" docker info >/dev/null 2>&1; then + exit 0 + fi +fi + +if command -v pgrep >/dev/null 2>&1; then + if pgrep -u "$(id -u)" -f "dockerd-rootless.sh" >/dev/null 2>&1; then + exit 0 + fi +fi + +nohup env XDG_RUNTIME_DIR="${runtime_dir}" dockerd-rootless.sh --host="unix://${socket}" \ + >"${runtime_dir}/dockerd-rootless.log" 2>&1 & diff --git a/chezmoi/dot_config/lazydocker/config.yml b/chezmoi/dot_config/lazydocker/config.yml index b8e86dc..1971443 100644 --- a/chezmoi/dot_config/lazydocker/config.yml +++ b/chezmoi/dot_config/lazydocker/config.yml @@ -1 +1 @@ -# Lazydocker uses DOCKER_HOST from the shell environment (Podman socket). +# Lazydocker uses DOCKER_HOST from the shell environment (rootless Docker socket). diff --git a/chezmoi/dot_config/nvim/lua/config/options.lua b/chezmoi/dot_config/nvim/lua/config/options.lua deleted file mode 100644 index 1417249..0000000 --- a/chezmoi/dot_config/nvim/lua/config/options.lua +++ /dev/null @@ -1,12 +0,0 @@ --- ~/.config/nvim/lua/config/options.lua -vim.opt.clipboard = "unnamedplus" - -vim.g.clipboard = { - name = "wsl-clipboard", - copy = { ["+"] = "clip.exe", ["*"] = "clip.exe" }, - paste = { - ["+"] = "powershell.exe -NoProfile -Command Get-Clipboard", - ["*"] = "powershell.exe -NoProfile -Command Get-Clipboard", - }, - cache_enabled = 0, -} diff --git a/chezmoi/dot_config/nvim/lua/user/init.lua b/chezmoi/dot_config/nvim/lua/user/init.lua deleted file mode 100644 index 3a4ccbe..0000000 --- a/chezmoi/dot_config/nvim/lua/user/init.lua +++ /dev/null @@ -1,3 +0,0 @@ -return { - -- Minimal user layer for LazyVim. -} diff --git a/chezmoi/dot_gitconfig b/chezmoi/dot_gitconfig index 7c5e187..9349438 100644 --- a/chezmoi/dot_gitconfig +++ b/chezmoi/dot_gitconfig @@ -1,2 +1,4 @@ [color] ui = auto +[init] + defaultBranch = develop diff --git a/chezmoi/dot_zshenv b/chezmoi/dot_zshenv index 3fb4d5c..ab5137f 100644 --- a/chezmoi/dot_zshenv +++ b/chezmoi/dot_zshenv @@ -1,8 +1,9 @@ export PATH="$HOME/.local/bin:$PATH" -export PATH="$PATH:/opt/nvim-linux-x86_64/bin" -export DOCKER_HOST="unix://${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/podman/podman.sock" -export PODMAN_COMPOSE_PROVIDER="podman-compose" +if [ -z "${XDG_RUNTIME_DIR:-}" ] || [ ! -d "${XDG_RUNTIME_DIR:-}" ]; then + export XDG_RUNTIME_DIR="$HOME/.docker/run" +fi +export DOCKER_HOST="unix://${XDG_RUNTIME_DIR}/docker.sock" if command -v mise >/dev/null 2>&1; then eval "$(mise activate zsh)" diff --git a/chezmoi/dot_zshrc b/chezmoi/dot_zshrc index 23780b4..7bf1c98 100644 --- a/chezmoi/dot_zshrc +++ b/chezmoi/dot_zshrc @@ -7,14 +7,44 @@ if [ -f "$ZSH/oh-my-zsh.sh" ]; then source "$ZSH/oh-my-zsh.sh" fi -if command -v zoxide >/dev/null 2>&1; then - eval "$(zoxide init zsh)" -fi - if command -v fdfind >/dev/null 2>&1 && ! command -v fd >/dev/null 2>&1; then alias fd='fdfind' fi if [ -f "$HOME/.config/zsh/aliases.zsh" ]; then source "$HOME/.config/zsh/aliases.zsh" -fi \ No newline at end of file +fi + +if [ -f "$HOME/.config/docker/rootless-session.sh" ]; then + bash "$HOME/.config/docker/rootless-session.sh" >/dev/null 2>&1 || true +fi + +# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh. +[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh + +# opencode +export PATH="$HOME/.opencode/bin:$PATH" + +# Added by get-aspire-cli.sh +export PATH="$HOME/.aspire/bin:$PATH" + +# Added by dotnet tools +export PATH="$PATH:$HOME/.dotnet/tools" + +if [ -n "${WSL_INTEROP:-}" ] || [ -n "${WSL_DISTRO_NAME:-}" ] || { \ + grep -qi microsoft /proc/sys/kernel/osrelease 2>/dev/null && \ + { [ -d /run/WSL ] || [ -e /proc/sys/fs/binfmt_misc/WSLInterop ] || [ -d /mnt/c/Windows ]; }; \ +}; then + export PATH="$(printf '%s' "$PATH" | tr ':' '\n' \ + | grep -Evi '/mnt/c/Users/.*/AppData/Local/pnpm|/mnt/c/Program Files/nodejs|WindowsPowerShell' \ + | paste -sd ':' -)" + hash -r +fi + +# pnpm +export PNPM_HOME="$HOME/.local/share/pnpm" +case ":$PATH:" in + *":$PNPM_HOME/bin:"*) ;; + *) export PATH="$PNPM_HOME/bin:$PATH" ;; +esac +# pnpm end diff --git a/mise/mise.toml b/mise/mise.toml index 61e9666..06a9982 100644 --- a/mise/mise.toml +++ b/mise/mise.toml @@ -1,7 +1,24 @@ +[tool_alias] +codex = "aqua:openai/codex" + [tools] node = "lts" +pnpm = "latest" go = "latest" rust = "latest" dotnet = "latest" bun = "latest" python = "latest" +uv = "latest" +codex = "latest" +github-cli = "latest" +lazygit = "latest" +lazydocker = "latest" +chezmoi = "latest" +fzf = "latest" +ripgrep = "latest" +fd = "latest" +eza = "latest" +fastfetch = "latest" +shellcheck = "latest" +shfmt = "latest" diff --git a/scripts/format-shell.sh b/scripts/format-shell.sh old mode 100644 new mode 100755 index 6fa0e4c..d85880d --- a/scripts/format-shell.sh +++ b/scripts/format-shell.sh @@ -3,15 +3,15 @@ set -euo pipefail IFS=$'\n\t' if ! command -v shfmt >/dev/null 2>&1; then - echo "shfmt not found. Install with: go install mvdan.cc/sh/v3/cmd/shfmt@latest" >&2 + echo "shfmt not found. Install the mise toolchain with: ./bootstrap/steps/30-mise.sh" >&2 exit 1 fi files=() if command -v rg >/dev/null 2>&1; then - mapfile -t files < <(rg --files -g '*.sh' bootstrap scripts) + mapfile -t files < <(rg --files -g '*.sh') else - mapfile -t files < <(find bootstrap scripts -type f -name '*.sh') + mapfile -t files < <(find . -type f -name '*.sh' -not -path './.git/*') fi if [ "${#files[@]}" -eq 0 ]; then @@ -19,4 +19,10 @@ if [ "${#files[@]}" -eq 0 ]; then exit 0 fi +# 1) Format shfmt -w -i 2 -ci "${files[@]}" + +# 2) Normalize EOL to LF (convert CRLF -> LF, also lone CR -> LF) +# -pi: in-place +# -e : program +perl -pi -e 's/\r\n?/\n/g' "${files[@]}" diff --git a/scripts/lint-shell.sh b/scripts/lint-shell.sh old mode 100644 new mode 100755 index f9306c2..4af08f9 --- a/scripts/lint-shell.sh +++ b/scripts/lint-shell.sh @@ -3,20 +3,25 @@ set -euo pipefail IFS=$'\n\t' if ! command -v shellcheck >/dev/null 2>&1; then - echo "shellcheck not found. Install via apt (e.g. sudo apt-get install -y shellcheck)." >&2 + echo "shellcheck not found. Install the mise toolchain with: ./bootstrap/steps/30-mise.sh" >&2 exit 1 fi if ! command -v shfmt >/dev/null 2>&1; then - echo "shfmt not found. Install with: go install mvdan.cc/sh/v3/cmd/shfmt@latest" >&2 + echo "shfmt not found. Install the mise toolchain with: ./bootstrap/steps/30-mise.sh" >&2 + exit 1 +fi + +if ! command -v zsh >/dev/null 2>&1; then + echo "zsh not found. Install via apt (e.g. sudo apt-get install -y zsh)." >&2 exit 1 fi files=() if command -v rg >/dev/null 2>&1; then - mapfile -t files < <(rg --files -g '*.sh' bootstrap scripts) + mapfile -t files < <(rg --files -g '*.sh') else - mapfile -t files < <(find bootstrap scripts -type f -name '*.sh') + mapfile -t files < <(find . -type f -name '*.sh' -not -path './.git/*') fi if [ "${#files[@]}" -eq 0 ]; then @@ -26,3 +31,4 @@ fi shellcheck "${files[@]}" shfmt -d -i 2 -ci "${files[@]}" +zsh -n chezmoi/dot_zshenv chezmoi/dot_zshrc chezmoi/dot_config/zsh/aliases.zsh