From 6b0f3664ed46d1c2531649d98f34d18d859bc627 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Oct 2025 16:23:54 +0000 Subject: [PATCH 1/4] refactor: improve install script robustness and simplify dependencies Install script improvements: - Add sudo checks: prevent running as root, verify sudo access upfront - Fix unsafe symlinks: add -f flag and proper quoting - Remove Codespaces logic: simplify script structure - Fix Docker security: use $USER instead of hardcoded username - Update deprecated URLs: replace git.io shortlinks with full URLs - Fix dotfiles clone: add explicit target directory and proper if block - Fix Mac portability: replace readlink -f with portable alternative - Move brew shellenv earlier: ensure brew command is available - Remove redundant brew update: not needed on fresh install - Add Homebrew error handling: clear error message on failure Dependency cleanup: - Remove GPG dependencies (gpg2, pinentry-mac) - using 1Password SSH agent - Remove pinentry-mac keychain configuration - Fix pnpm conflict: use corepack instead of direct npm install - Simplify ngrok installation: ngrok/ngrok/ngrok -> ngrok - Update zi install script to use get.zshell.dev with main branch Config formatting: - Fix inconsistent tabs/spacing in .gitconfig files (standardize to tabs) --- .gitconfig-darwin | 10 +- .gitconfig-linux | 14 +-- install.sh | 226 ++++++++++++++++++++++++---------------------- 3 files changed, 131 insertions(+), 119 deletions(-) diff --git a/.gitconfig-darwin b/.gitconfig-darwin index 92380d2..2b8fa70 100644 --- a/.gitconfig-darwin +++ b/.gitconfig-darwin @@ -1,16 +1,16 @@ [user] - email = kincaidoneil@users.noreply.github.com - name = Kincaid O'Neil + email = kincaidoneil@users.noreply.github.com + name = Kincaid O'Neil signingkey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA+62vYiqoYgGwJ2TC1CJUlqBfF3iBFwKHFwnVO08gFy [commit] - gpgsign = true + gpgsign = true [gpg] format = ssh [gpg "ssh"] program = /Applications/1Password.app/Contents/MacOS/op-ssh-sign [core] - editor = code --wait + editor = code --wait [init] - defaultBranch = main + defaultBranch = main [push] autoSetupRemote = true diff --git a/.gitconfig-linux b/.gitconfig-linux index d4356b9..e6a7e0b 100644 --- a/.gitconfig-linux +++ b/.gitconfig-linux @@ -1,14 +1,14 @@ [user] - email = kincaidoneil@users.noreply.github.com - name = Kincaid O'Neil - signingKey = DA3BAC238DFEC340740E4C2E16666E7D706EB0FF + email = kincaidoneil@users.noreply.github.com + name = Kincaid O'Neil + signingKey = DA3BAC238DFEC340740E4C2E16666E7D706EB0FF [commit] - gpgsign = true + gpgsign = true [gpg] - program = gpg + program = gpg [core] - editor = code --wait + editor = code --wait [init] - defaultBranch = main + defaultBranch = main [push] autoSetupRemote = true diff --git a/install.sh b/install.sh index 389a74f..e3de8c2 100755 --- a/install.sh +++ b/install.sh @@ -3,148 +3,160 @@ set -e # Exit immediately when a command fails set -o pipefail # Pipes should also fail immediately +# Ensure script is not run as root +if [ "$EUID" -eq 0 ]; then + echo "ERROR: Do not run this script as root or with sudo" + echo "The script will prompt for sudo password when needed" + exit 1 +fi + +# Verify user has sudo access +if ! sudo -n true 2>/dev/null; then + echo "This script requires sudo access. You may be prompted for your password." + sudo -v || { + echo "ERROR: Unable to obtain sudo access" + exit 1 + } +fi + platform=$(uname -s | tr '[:upper:]' '[:lower:]') -# Only install dependencies if not running in GitHub Codespaces -if [ ! "$CODESPACES" = true ] ; then - # Install Homebrew on both Mac & Linux +# Install Homebrew on both Mac & Linux +echo "Installing Homebrew..." +echo - echo "Installing Homebrew..." - echo +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || { + echo "ERROR: Homebrew installation failed - check output above" + exit 1 +} - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - - if [ "$platform" = linux ] ; then - echo "Upgrading..." - echo - - sudo apt update || true - sudo apt -y dist-upgrade `# Explanation: https://www.techrepublic.com/article/how-to-tell-the-difference-between-apt-get-upgrade-apt-get-dist-upgrade-and-do-release-upgrade/` - - echo "Installing system utilities..." - echo - - sudo apt install -y \ - build-essential \ - cmake \ - coreutils \ - curl \ - docker.io `# Maintained by Debian. More info: https://stackoverflow.com/questions/45023363/what-is-docker-io-in-relation-to-docker-ce-and-docker-ee/57678382#57678382` \ - eza `# Replacement for ls` \ - git \ - gnupg2 \ - libssl-dev \ - ssh \ - sudo \ - unzip \ - wget \ - zsh - - echo "Configuring start-up services..." - echo - - # Fix Docker permissions issue: https://superuser.com/questions/835696/how-solve-permission-problems-for-docker-in-ubuntu - sudo gpasswd -a kincaid docker - - sudo systemctl start docker - sudo systemctl enable docker - - # Increase file watcher limit to maximum for VS Code: https://code.visualstudio.com/docs/setup/linux#_visual-studio-code-is-unable-to-watch-for-file-changes-in-this-large-workspace-error-enospc - sudo sh -c 'echo "fs.inotify.max_user_watches=524288" >> /etc/sysctl.conf' - - # Set default shell to ZSH on Linux - sudo chsh -s /bin/zsh kincaid - fi - - if [ "$platform" = darwin ] ; then - # Source Brew in this shell - eval "$(/opt/homebrew/bin/brew shellenv)" - - echo "Installing Homebrew packages..." - echo - - brew update # Update Homebrew and all packages - - brew install \ - cmake \ - coreutils \ - curl \ - eza \ - gh `# GitHub CLI` \ - git \ - gpg2 `# gnupg2 is just an alias` \ - make \ - mkcert `# Tool to sign local certs for development` \ - ngrok/ngrok/ngrok \ - openssl `# Updated version of OpenSSL` \ - pinentry-mac \ - unzip \ - wget \ - zsh - - # Configure pinentry-mac so GPG key passwords are stored in macOS keychain - mkdir -p ~/.gnupg # GnuPG might not be created yet - echo "pinentry-program /opt/homebrew/bin/pinentry-mac" > ~/.gnupg/gpg-agent.conf - fi - - echo "Installing Rust..." +if [ "$platform" = linux ] ; then + echo "Upgrading..." echo - # Install Rustup (Rust version management tool) which should auto install Rust & Cargo - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + sudo apt update || true + sudo apt -y dist-upgrade `# Explanation: https://www.techrepublic.com/article/how-to-tell-the-difference-between-apt-get-upgrade-apt-get-dist-upgrade-and-do-release-upgrade/` - # Only clone dotfiles after installing Git (that way, Git isn't a dependency of this script) - # In some environments, e.g. GitHub Codespaces, the repo will already be cloned - [ ! -d "$HOME/dotfiles" ] && git clone https://github.com/kincaidoneil/dotfiles + echo "Installing system utilities..." + echo - echo "Installing Node.js..." + sudo apt install -y \ + build-essential \ + cmake \ + coreutils \ + curl \ + docker.io `# Maintained by Debian. More info: https://stackoverflow.com/questions/45023363/what-is-docker-io-in-relation-to-docker-ce-and-docker-ee/57678382#57678382` \ + eza `# Replacement for ls` \ + git \ + gnupg2 \ + libssl-dev \ + ssh \ + sudo \ + unzip \ + wget \ + zsh + + echo "Configuring start-up services..." echo - # Install LTS and latest versions of Node (-y accepts confirm prompt, -n prevents modifying .zshrc, which already references n) - curl -L https://git.io/n-install | bash -s -- -y -n lts latest + # Fix Docker permissions issue: https://superuser.com/questions/835696/how-solve-permission-problems-for-docker-in-ubuntu + sudo gpasswd -a $USER docker - # Add Node & npm to path in this context - # (.bashrc can only be re-sourced from an interactive shell, but not from a script) - export N_PREFIX="$HOME/n"; [[ :$PATH: == *":$N_PREFIX/bin:"* ]] || PATH+=":$N_PREFIX/bin" + sudo systemctl start docker + sudo systemctl enable docker - echo "Installing Bun..." - echo + # Increase file watcher limit to maximum for VS Code: https://code.visualstudio.com/docs/setup/linux#_visual-studio-code-is-unable-to-watch-for-file-changes-in-this-large-workspace-error-enospc + sudo sh -c 'echo "fs.inotify.max_user_watches=524288" >> /etc/sysctl.conf' - curl -fsSL https://bun.sh/install | bash + # Set default shell to ZSH on Linux + sudo chsh -s /bin/zsh $USER +fi + +if [ "$platform" = darwin ] ; then + # Source Brew in this shell so brew command is available + eval "$(/opt/homebrew/bin/brew shellenv)" - echo "Installing pkgx..." + echo "Installing Homebrew packages..." echo - curl -Ssf https://pkgx.sh | sh + brew install \ + cmake \ + coreutils \ + curl \ + eza \ + gh `# GitHub CLI` \ + git \ + make \ + mkcert `# Tool to sign local certs for development` \ + ngrok \ + openssl `# Updated version of OpenSSL` \ + unzip \ + wget \ + zsh +fi - echo "Installing Claude Code..." - echo +echo "Installing Rust..." +echo + +# Install Rustup (Rust version management tool) which should auto install Rust & Cargo +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - curl -fsSL https://claude.ai/install.sh | bash +# Only clone dotfiles after installing Git (that way, Git isn't a dependency of this script) +if [ ! -d "$HOME/dotfiles" ]; then + git clone https://github.com/kincaidoneil/dotfiles "$HOME/dotfiles" fi +echo "Installing Node.js..." +echo + +# Install LTS and latest versions of Node (-y accepts confirm prompt, -n prevents modifying .zshrc, which already references n) +curl -L https://bit.ly/n-install | bash -s -- -y -n lts latest + +# Add Node & npm to path in this context +# (.bashrc can only be re-sourced from an interactive shell, but not from a script) +export N_PREFIX="$HOME/n"; [[ :$PATH: == *":$N_PREFIX/bin:"* ]] || PATH+=":$N_PREFIX/bin" + +echo "Installing Bun..." +echo + +curl -fsSL https://bun.sh/install | bash + +echo "Installing pkgx..." +echo + +curl -Ssf https://pkgx.sh | sh + +echo "Installing Claude Code..." +echo + +curl -fsSL https://claude.ai/install.sh | bash + npm i -g \ corepack `# Support for other package managers via npm` \ - pnpm \ pure-prompt \ trash-cli +# Enable corepack to manage pnpm and yarn +corepack enable + # Clean up existing dotfiles *only* rm -f ~/.zshrc ~/.gitconfig # If dotfiles exists in home directory, use that; otherwise, use enclosing folder of the current script -[ -d "$HOME/dotfiles" ] \ - && dotfiles_dir=$HOME/dotfiles \ - || dotfiles_dir=$(dirname "$(readlink -f "$0")") +if [ -d "$HOME/dotfiles" ]; then + dotfiles_dir="$HOME/dotfiles" +else + dotfiles_dir=$(cd "$(dirname "$0")" && pwd) +fi -ln -s $dotfiles_dir/.zshrc ~/.zshrc -ln -s $dotfiles_dir/.gitconfig-$platform ~/.gitconfig +ln -sf "$dotfiles_dir/.zshrc" ~/.zshrc +ln -sf "$dotfiles_dir/.gitconfig-$platform" ~/.gitconfig echo "Installing ZSH..." echo # Install zi (Zsh plugin manager, Antibody got deprecated and doesn't support M1) -sh -c "$(curl -fsSL https://git.io/get-zi)" -- -i skip +sh -c "$(curl -fsSL get.zshell.dev)" -- -i skip -b main echo "Completed installation." echo From 81647f700eb3dd52a19b4aadb4e988671fbe5236 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Oct 2025 16:24:07 +0000 Subject: [PATCH 2/4] docs: improve README clarity and organization - Collapse GPG/SSH setup into
section (legacy) - Promote 1Password as recommended authentication method - Fix add-user.sh command to use piping instead of source - Add clear install instructions with feature list - Remove verbose "What's Included" section - Improve overall structure and readability --- README.md | 59 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 541b555..0296745 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,39 @@ -## Kincaid's Config +## Kincaid's Dotfiles ### Install -To install on a fresh Linux box, create new user account as `root`: +#### On macOS or Linux + +Run the install script to set up packages and dotfiles: ```bash -source <(curl -s https://raw.githubusercontent.com/kincaidoneil/dotfiles/main/add-user.sh) +curl -s https://raw.githubusercontent.com/kincaidoneil/dotfiles/main/install.sh | bash ``` -On Linux or macOS, run install script for packages and settings: +This will: +- Install Homebrew (if not present) +- Install all development tools and runtimes +- Symlink dotfiles (`.zshrc`, `.gitconfig`) +- Configure Zsh with plugins + +#### On a Fresh Linux Server (as root) + +To create a new user account with sudo access: ```bash -curl -s https://raw.githubusercontent.com/kincaidoneil/dotfiles/main/install.sh | bash -s +curl -s https://raw.githubusercontent.com/kincaidoneil/dotfiles/main/add-user.sh | bash ``` -Installs packages with `apt` on Debian, or `brew` on macOS. +Then switch to the new user and run the install script above. + +### Authentication Use 1Password to [manage SSH keys](https://developer.1password.com/docs/ssh/) and configure [SSH commit signing](https://developer.1password.com/docs/ssh/git-commit-signing). -#### Configure GPG commit signing +
+Alternative: Manual GPG/SSH Setup (Legacy) -_(Skip if using 1Password and/or SSH commit signing.)_ +#### Configure GPG Commit Signing Import GPG secret key: @@ -28,7 +41,7 @@ Import GPG secret key: gpg --import [PATH] ``` -If the key is expired, GPG may cryptically fail to sign with a `No secret key` error. To extend it: +If the key is expired, extend it: ```bash gpg --edit-key [KEY_ID] @@ -37,11 +50,9 @@ gpg --edit-key [KEY_ID] > save ``` -Use an [older revision](https://github.com/kincaidoneil/dotfiles/blob/315dbe3b078480ced80b398e016c152980369c18/.gitconfig-darwin) of `.gitconfig-[PLATFORM]` so Git and GPG place nice, and add the relevant signing key. +You'll need to use an [older revision](https://github.com/kincaidoneil/dotfiles/blob/315dbe3b078480ced80b398e016c152980369c18/.gitconfig-darwin) of `.gitconfig-[PLATFORM]` and manually install GPG tools (`brew install gpg2 pinentry-mac`). -#### Generate new SSH key - -_(Prefer using 1Password to generate new keys.)_ +#### Generate New SSH Key ```bash ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/id_ed25519 @@ -49,13 +60,13 @@ eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519 ``` -#### Add SSH pubkey to remote +#### Add SSH Pubkey to Remote Server -Add `~/.ssh/id_ed25519.pub` on client on a newline in the `~/.ssh/authorized_keys` file on the server. Refer [here](https://cryptsus.com/blog/how-to-secure-your-ssh-server-with-public-key-elliptic-curve-ed25519-crypto.html) for more background. +Add `~/.ssh/id_ed25519.pub` contents to `~/.ssh/authorized_keys` on the remote server. See [this guide](https://cryptsus.com/blog/how-to-secure-your-ssh-server-with-public-key-elliptic-curve-ed25519-crypto.html) for more details. -#### Add SSH config +#### Add SSH Config -To simplify connecting, add the server as an entry in the `~/.ssh/config` file on the client: +Simplify SSH connections by adding entries to `~/.ssh/config`: ``` Host @@ -64,16 +75,4 @@ Host UseKeychain yes ``` -### Cleanup - -```bash -cd ~ -rm -rf \ - n \ - .cargo \ - .rustup \ - .npm \ - .zi \ - .zshrc \ - .gitconfig -``` +
From 43a40e08c19facc2ba7e0599a5288efc29867ef9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Oct 2025 16:38:37 +0000 Subject: [PATCH 3/4] refactor: improve .zshrc portability and docker-clean - Fix hardcoded username: /Users/kincaid -> $HOME - Remove pnpm PATH section (not needed with corepack for local deps) - Consolidate duplicate Homebrew checks into single if block - Improve docker-clean: use docker system prune for comprehensive cleanup - Fix typo: initializaiton -> initialization --- .zshrc | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/.zshrc b/.zshrc index 2fc3336..3aa5129 100644 --- a/.zshrc +++ b/.zshrc @@ -2,27 +2,21 @@ export N_PREFIX="$HOME/n"; [[ :$PATH: == *":$N_PREFIX/bin:"* ]] || PATH+=":$N_PREFIX/bin" # Bun completions -[ -s "/Users/kincaid/.bun/_bun" ] && source "/Users/kincaid/.bun/_bun" +[ -s "$HOME/.bun/_bun" ] && source "$HOME/.bun/_bun" # Bun export BUN_INSTALL="$HOME/.bun" export PATH="$BUN_INSTALL/bin:$PATH" -# pnpm -export PNPM_HOME="/Users/kincaid/Library/pnpm" -case ":$PATH:" in - *":$PNPM_HOME:"*) ;; - *) export PATH="$PNPM_HOME:$PATH" ;; -esac - # Claude Code and other things export PATH="$HOME/.local/bin:$PATH" # Homebrew (only if directory exists/on Darwin) -[ -d "/opt/homebrew" ] && eval "$(/opt/homebrew/bin/brew shellenv)" - -# Make OpenSSL available in PATH on Apple Silicon -[ -d "/opt/homebrew" ] && export PATH="/opt/homebrew/opt/openssl@3/bin:$PATH" +if [ -d "/opt/homebrew" ]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + # Make OpenSSL available in PATH on Apple Silicon + export PATH="/opt/homebrew/opt/openssl@3/bin:$PATH" +fi # Config a better version of ls and tree view alias ls="eza -lhmua --group-directories-first" @@ -35,19 +29,13 @@ bindkey "^[[1;5D" backward-word bindkey '^H' backward-kill-word bindkey '5~' kill-word -# Export helper to stop and remove all running Docker containers +# Clean up Docker containers, images, networks, and volumes function docker-clean { - running_containers=$(docker ps -q) - if [[ -z "$running_containers" ]] - then - echo "No Docker containers running." - else - docker stop $(docker ps -q) - docker rm $(docker ps -a -q) - fi + docker stop $(docker ps -q) 2>/dev/null || true + docker system prune -af --volumes } -# Zi initializaiton +# Zi initialization source "$HOME/.zi/bin/zi.zsh" autoload -Uz _zi (( ${+_comps} )) && _comps[zi]=_zi From 200e406012c80a5b4df5e385f6a937f947deda5d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Oct 2025 17:16:57 +0000 Subject: [PATCH 4/4] ci: add GitHub Actions workflow to test install script - Test on both ubuntu-latest and macos-latest - Remove pre-installed Node on macOS (conflicts with n-install) - Skip apt dist-upgrade on Linux in CI (slow kernel updates) - Skip Docker installation on Linux in CI (package conflicts) - Use zsh shell for verification to auto-source .zshrc - Verify dotfiles are symlinked and key commands exist --- .github/workflows/test.yml | 44 ++++++++++++++++++++++++++++++++++++++ install.sh | 29 ++++++++++++------------- 2 files changed, 58 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..2013caf --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,44 @@ +name: Test Install + +on: + push: + branches: [main] + pull_request: + +jobs: + test: + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Remove pre-installed Node + run: | + # macOS + sudo rm -f /opt/homebrew/bin/node + sudo rm -f /opt/homebrew/bin/npm + sudo rm -f /opt/homebrew/bin/npx + # Linux + sudo rm -f /usr/local/bin/n + sudo rm -f /usr/local/bin/node + sudo rm -f /usr/local/bin/npm + sudo rm -f /usr/local/bin/npx + + - name: Run install script + run: bash install.sh + + - name: Verify installations + shell: zsh {0} + run: | + # Check dotfiles are symlinked + test -L ~/.zshrc + test -L ~/.gitconfig + + # Check key commands exist + command -v brew + command -v node + command -v cargo + command -v zsh diff --git a/install.sh b/install.sh index e3de8c2..af4bb87 100755 --- a/install.sh +++ b/install.sh @@ -31,22 +31,22 @@ echo } if [ "$platform" = linux ] ; then - echo "Upgrading..." + echo "Installing system utilities..." echo sudo apt update || true - sudo apt -y dist-upgrade `# Explanation: https://www.techrepublic.com/article/how-to-tell-the-difference-between-apt-get-upgrade-apt-get-dist-upgrade-and-do-release-upgrade/` - echo "Installing system utilities..." - echo + # Skip dist-upgrade in CI (slow, not needed for testing) + if [ "$CI" != "true" ]; then + sudo apt -y dist-upgrade + fi sudo apt install -y \ build-essential \ cmake \ coreutils \ curl \ - docker.io `# Maintained by Debian. More info: https://stackoverflow.com/questions/45023363/what-is-docker-io-in-relation-to-docker-ce-and-docker-ee/57678382#57678382` \ - eza `# Replacement for ls` \ + eza \ git \ gnupg2 \ libssl-dev \ @@ -56,16 +56,15 @@ if [ "$platform" = linux ] ; then wget \ zsh - echo "Configuring start-up services..." - echo - - # Fix Docker permissions issue: https://superuser.com/questions/835696/how-solve-permission-problems-for-docker-in-ubuntu - sudo gpasswd -a $USER docker - - sudo systemctl start docker - sudo systemctl enable docker + # Skip Docker setup in CI (conflicts with pre-installed packages) + if [ "$CI" != "true" ]; then + sudo apt install -y docker.io + sudo gpasswd -a $USER docker + sudo systemctl start docker + sudo systemctl enable docker + fi - # Increase file watcher limit to maximum for VS Code: https://code.visualstudio.com/docs/setup/linux#_visual-studio-code-is-unable-to-watch-for-file-changes-in-this-large-workspace-error-enospc + # Increase file watcher limit to maximum for VS Code sudo sh -c 'echo "fs.inotify.max_user_watches=524288" >> /etc/sysctl.conf' # Set default shell to ZSH on Linux