├── .config ├── git │ ├── fsmonitor.config │ ├── attributes │ ├── gitlab.config │ ├── github.config │ ├── ignore │ └── config ├── ghostty │ └── config ├── starship │ └── starship.toml └── scripts │ └── macos-bootstrap.zsh ├── .claude ├── config.json ├── commands │ ├── diag.md │ ├── deslop.md │ ├── git-new.md │ ├── git-commit.md │ └── gh-view.md ├── settings.json └── CLAUDE.md ├── .ssh └── config ├── .proto └── .prototools ├── .cargo └── config.toml ├── Brewfile ├── .github ├── README.md └── README_EN.md └── .zshrc /.config/git/fsmonitor.config: -------------------------------------------------------------------------------- 1 | [core] 2 | fsmonitor = true -------------------------------------------------------------------------------- /.claude/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "primaryApiKey": "you-are-absolutely-right" 3 | } 4 | -------------------------------------------------------------------------------- /.config/git/attributes: -------------------------------------------------------------------------------- 1 | ## personal ## 2 | 3 | .config/git/github.config filter=signingkey 4 | .config/git/gitlab.config filter=user.config 5 | .proto/.prototools filter=prototools 6 | -------------------------------------------------------------------------------- /.config/git/gitlab.config: -------------------------------------------------------------------------------- 1 | [user] 2 | 3 | [commit] 4 | gpgsign = true 5 | 6 | [gpg] 7 | format = ssh 8 | 9 | [gpg "ssh"] 10 | allowedSignersFile = ~/.ssh/allowed_signers 11 | 12 | [tag] 13 | gpgsign = true -------------------------------------------------------------------------------- /.config/git/github.config: -------------------------------------------------------------------------------- 1 | [user] 2 | email = 38807139+liby@users.noreply.github.com 3 | name = Bryan Lee 4 | 5 | [commit] 6 | gpgsign = true 7 | 8 | [gpg] 9 | program = gpg 10 | 11 | [tag] 12 | gpgsign = true -------------------------------------------------------------------------------- /.ssh/config: -------------------------------------------------------------------------------- 1 | Include ~/.orbstack/ssh/config 2 | 3 | Host * 4 | IgnoreUnknown UseKeychain 5 | AddKeysToAgent yes 6 | Compression yes 7 | ForwardAgent yes 8 | ServerAliveInterval 30 9 | UseKeychain yes 10 | 11 | Host github.com 12 | Hostname ssh.github.com 13 | Port 443 14 | -------------------------------------------------------------------------------- /.proto/.prototools: -------------------------------------------------------------------------------- 1 | npm = "bundled" 2 | zig = "0.14.0" 3 | zls = "0.14.0" 4 | 5 | [tools.node] 6 | bundled-npm = true 7 | 8 | [tools.npm] 9 | shared-globals-dir = true 10 | 11 | [plugins] 12 | zig = "github://konomae/zig-plugin" 13 | zls = "github://konomae/zls-plugin" 14 | 15 | [settings] 16 | auto-install = true 17 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [source.crates-io] 2 | replace-with = 'rsproxy-sparse' 3 | [source.rsproxy] 4 | registry = "https://rsproxy.cn/crates.io-index" 5 | [source.rsproxy-sparse] 6 | registry = "sparse+https://rsproxy.cn/index/" 7 | [registries.rsproxy] 8 | index = "https://rsproxy.cn/crates.io-index" 9 | [net] 10 | git-fetch-with-cli = true -------------------------------------------------------------------------------- /.config/git/ignore: -------------------------------------------------------------------------------- 1 | # Global gitignore 2 | 3 | ## Claude Code 4 | **/.claude/settings.local.json 5 | CLAUDE.md 6 | 7 | ## Cursor 8 | .cursor 9 | 10 | ## General 11 | log 12 | *.log 13 | 14 | ## JetBrains 15 | .idea 16 | 17 | ## macOS 18 | .DS_Store 19 | 20 | ## Node.js 21 | node_modules 22 | npm-debug.log 23 | 24 | ## Visual Studio Code 25 | .vscode 26 | 27 | **/.claude/settings.local.json 28 | -------------------------------------------------------------------------------- /.config/ghostty/config: -------------------------------------------------------------------------------- 1 | auto-update = check 2 | auto-update-channel = tip 3 | 4 | cursor-style = block 5 | cursor-style-blink = false 6 | 7 | font-family = "Inconsolata LGC Nerd Font Mono" 8 | font-family = "Maple Mono NF CN Light" 9 | font-size = 12 10 | 11 | keybind = ctrl+alt+i=inspector:toggle 12 | keybind = shift+enter=text:\n 13 | keybind = super+u=text:\x1f 14 | 15 | shell-integration = zsh 16 | shell-integration-features = no-cursor 17 | 18 | theme = "Snazzy Soft" 19 | 20 | window-padding-x = 8 21 | window-padding-y = 8 22 | -------------------------------------------------------------------------------- /.claude/commands/diag.md: -------------------------------------------------------------------------------- 1 | --- 2 | allowed-tools: mcp__ide__getDiagnostics, Read, Edit, MultiEdit, LS, Glob, TodoWrite 3 | description: Automatically fix all IDE diagnostics/errors until none remain 4 | argument-hint: "[file path or pattern]" 5 | --- 6 | 7 | ## Your Task 8 | 9 | Automatically fix all IDE diagnostics (errors, warnings) for: $ARGUMENTS 10 | 11 | Iteratively fix ALL diagnostics until none remain. 12 | 13 | ## Process: 14 | 15 | 1. **Get diagnostics** for the specified scope (file/pattern/all) 16 | 17 | 2. **Fix errors** by: 18 | - Reading files to understand context 19 | - Analyzing and fixing all errors in each file 20 | - Common fixes: missing imports, type errors, undefined variables, syntax issues, etc. 21 | 22 | 3. **Repeat** until no diagnostics remain 23 | 24 | 4. **Report** summary of fixes applied -------------------------------------------------------------------------------- /.claude/commands/deslop.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Remove AI-generated code slop from the current branch 3 | allowed-tools: 4 | - Bash 5 | - Read 6 | - Edit 7 | - Grep 8 | - Glob 9 | --- 10 | 11 | # Remove AI Code Slop 12 | 13 | Check the diff against the default branch (use !`git symbolic-ref refs/remotes/origin/HEAD` to detect main/master), and remove all AI generated slop introduced in this branch. 14 | 15 | This includes: 16 | - Extra comments that a human wouldn't add or is inconsistent with the rest of the file 17 | - Extra defensive checks or try/catch blocks that are abnormal for that area of the codebase (especially if called by trusted / validated codepaths) 18 | - Unnecessary fallbacks for scenarios that can't happen or are already guaranteed by upstream code 19 | - Casts to `any` to get around type issues 20 | - Any other style that is inconsistent with the file 21 | 22 | Report at the end with only a 1-3 sentence summary of what you changed. 23 | -------------------------------------------------------------------------------- /.claude/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/claude-code-settings.json", 3 | "env": { 4 | "CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR": "1", 5 | "CLAUDE_CODE_ENABLE_UNIFIED_READ_TOOL": "1", 6 | "DISABLE_BUG_COMMAND": "1", 7 | "DISABLE_ERROR_REPORTING": "1", 8 | "DISABLE_NON_ESSENTIAL_MODEL_CALLS": "1", 9 | "DISABLE_TELEMETRY": "1", 10 | "ENABLE_BACKGROUND_TASKS": "1", 11 | "FORCE_AUTO_BACKGROUND_TASKS": "1" 12 | }, 13 | "includeCoAuthoredBy": false, 14 | "permissions": { 15 | "allow": [ 16 | "Bash(fd:*)", 17 | "Bash(find:*)", 18 | "Bash(jq:*)", 19 | "Bash(mkdir:*)", 20 | "Bash(pnpm add:*)", 21 | "Bash(pnpm run:*)", 22 | "Bash(ps:*)", 23 | "Bash(rg:*)", 24 | "Bash(timeout:*)", 25 | "Bash(uv:*)", 26 | "Read(~/.claude/**)", 27 | "Read(~/.config/**)", 28 | "Read(~/.zshrc)", 29 | "WebFetch(domain:docs.anthropic.com)" 30 | ] 31 | }, 32 | "model": "claude-opus-4-5@20251101", 33 | "enabledPlugins": { 34 | "feature-dev@claude-code-plugins": true, 35 | "commit-commands@claude-code-plugins": true 36 | }, 37 | "alwaysThinkingEnabled": true 38 | } 39 | -------------------------------------------------------------------------------- /.config/starship/starship.toml: -------------------------------------------------------------------------------- 1 | # Inserts a blank line between shell prompts 2 | add_newline = true 3 | command_timeout = 3600 4 | 5 | [git_commit] 6 | format = "[\\[$hash$tag\\]]($style)" 7 | 8 | [git_state] 9 | format = "\\[[$state( $progress_current/$progress_total)]($style)\\]" 10 | 11 | [git_status] 12 | format = "([\\[$all_status$ahead_behind\\]]($style))" 13 | ahead = "⇡${count}" 14 | behind = "⇣${count}" 15 | diverged = "⇕⇡${ahead_count}⇣${behind_count}" 16 | 17 | [bun] 18 | format = '\[[$symbol($version)]($style)\]' 19 | 20 | [cmd_duration] 21 | format = '\[[⏱ $duration]($style)\]' 22 | 23 | [deno] 24 | format = '\[[$symbol($version)]($style)\]' 25 | 26 | [git_branch] 27 | format = '\[[$symbol$branch]($style)\]' 28 | symbol = "󰘬·" 29 | 30 | [memory_usage] 31 | disabled = false 32 | format = '\[$symbol[$ram( | $swap)]($style)\]' 33 | style = "bold dimmed purple" 34 | symbol = "󰆼 " 35 | 36 | [nodejs] 37 | format = '\[[$symbol($version)]($style)\]' 38 | symbol = "󰎙 " 39 | 40 | [package] 41 | format = '\[[$symbol$version]($style)\]' 42 | 43 | [python] 44 | format = '\[[${symbol}${pyenv_prefix}(${version})(\($virtualenv\))]($style)\]' 45 | symbol = "󰌠 " 46 | 47 | [ruby] 48 | format = '\[[$symbol($version)]($style)\]' 49 | 50 | [rust] 51 | format = '\[[$symbol($version)]($style)\]' 52 | 53 | [zig] 54 | format = '\[[$symbol($version)]($style)\]' -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | # Cross-platform make 2 | brew "cmake" 3 | # Diff that understands syntax 4 | brew "difftastic" 5 | # Modern, maintained replacement for ls 6 | brew "eza" 7 | # Distributed revision control system 8 | brew "git" 9 | # Git extension for versioning large files 10 | brew "git-lfs" 11 | # GNU Pretty Good Privacy (PGP) package 12 | brew "gnupg" 13 | # Lightweight and flexible command-line JSON processor 14 | brew "jq" 15 | # Mac App Store command-line interface 16 | brew "mas" 17 | # Pinentry for GPG on Mac 18 | brew "pinentry-mac" 19 | # Search tool like grep and The Silver Searcher 20 | brew "ripgrep" 21 | # The minimal, blazing-fast, and infinitely customizable prompt for any shell 22 | brew "starship" 23 | #Python package and project manager 24 | brew "uv" 25 | # Shell extension to navigate your filesystem faster 26 | brew "zoxide" 27 | # UNIX shell (command interpreter) 28 | brew "zsh" 29 | # Uninstall unwanted apps 30 | cask "appcleaner" 31 | # Font with a modern monospaced design 32 | cask "font-maple-mono-nf-cn" 33 | # Fast, native, feature-rich terminal emulator pushing modern features 34 | cask "ghostty" 35 | # Web browser 36 | cask "google-chrome" 37 | # Switch and track your input sources with ease 38 | cask "input-source-pro" 39 | # Menu bar calendar 40 | cask "itsycal" 41 | # Privacy-first, open-source platform for knowledge sharing and management 42 | cask "logseq" 43 | # IDE for professional Python development 44 | cask "pycharm" 45 | # Control your tools with a few keystrokes 46 | cask "raycast" 47 | # code editor 48 | cask "visual-studio-code" 49 | 50 | mas "Bob", id: 1630034110 51 | mas "Hidden Bar", id: 1452453066 52 | mas "Slack", id: 803453959 53 | mas "The Unarchiver", id: 425424353 54 | mas "WeChat", id: 836500024 55 | mas "Xcode", id: 497799835 56 | mas "Xnip", id: 1221250572 -------------------------------------------------------------------------------- /.claude/commands/git-new.md: -------------------------------------------------------------------------------- 1 | --- 2 | allowed-tools: Bash(git switch:*), Bash(git branch:*), Bash(git diff:*), Bash(git status:*), Bash(git push:*) 3 | description: Create a new git branch with proper prefix and following naming conventions 4 | argument-hint: ticketNumber [context] | [additional context] 5 | --- 6 | 7 | Create a new git branch based on: $ARGUMENTS. 8 | 9 | ## Your Task 10 | 11 | Analyze the current changes and generate an appropriate branch name following GitFlow conventions and naming rules. 12 | 13 | ## Branch Naming Conventions 14 | 15 | ### Format Rules 16 | 17 | - Branch name must start with one of these prefixes: `feature/`, `bugfix/`, `hotfix/` 18 | - If a ticket number is provided (e.g., PROJ-1234), incorporate it into the branch name 19 | - Only lowercase letters and numbers (except ticket prefix which is uppercase) 20 | - Use hyphens `-` to separate words 21 | - No special characters (`_`, `\`, `@`, `()`, `*`, `&`, `%`, etc.) 22 | - Period `.` only allowed in version numbers 23 | 24 | ### Content Rules 25 | 26 | - 3-8 words in the description part 27 | - Omit unnecessary words like "the" when possible 28 | - Use present tense verbs 29 | - Be concise but descriptive 30 | 31 | ### Examples 32 | 33 | - `feature/upgrade-react-to-version-18` 34 | - `feature/DEV-1234-add-user-authentication` 35 | - `bugfix/PROJ-3456-fix-login-redirect-issue` 36 | - `hotfix/TASK-5678-restore-database-connection` 37 | 38 | ## Process 39 | 40 | 1. Analyze current changes with !`git diff HEAD` and !`git status --short` 41 | 2. Extract ticket number from $ARGUMENTS (if provided) 42 | 3. Determine appropriate prefix based on the changes 43 | 4. Generate descriptive suffix based on the code changes 44 | 5. Format branch name following the rules above 45 | 6. Execute `git switch -c ` 46 | 7. Confirm creation with !`git branch --show-current` -------------------------------------------------------------------------------- /.claude/commands/git-commit.md: -------------------------------------------------------------------------------- 1 | --- 2 | allowed-tools: Bash(dot add:*), Base(dot branch:*), Bash(dot commit:*), Bash(dot diff:*), Bash(dot log:*), Bash(dot status:*), Bash(git add:*), Base(git branch:*), Bash(git commit:*), Bash(git diff:*), Bash(git log:*), Bash(git status:*) 3 | description: Create a git commit following repository conventions 4 | argument-hint: "[additional context] | dot [context]" 5 | --- 6 | 7 | ## Context 8 | 9 | User request: $ARGUMENTS. 10 | 11 | IMPORTANT: Use `dot` command instead of `git` when: 12 | - Request mentions "dot" or "dotfiles", OR 13 | - Current directory is $HOME and *~/.dotfiles* directory exists 14 | `dot` is an alias for: `git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME`. 15 | 16 | - Current git status: `[git|dot] status --short` 17 | - Current git diff (staged and unstaged changes): `[git|dot] diff HEAD` 18 | - Current branch: `[git|dot] branch --show-current` 19 | - Recent commits: `[git|dot] log --oneline -10` 20 | 21 | ## Commit Message Rules 22 | 23 | - Use present tense verbs: `Add`, `Fix`, `Update`, `Remove` 24 | - Use backticks for code references: "`$variable`", "`someMethod()`", "`ClassName`" 25 | - Reference related commits with hash: `Fix bug introduced by [abc123]` 26 | - Be specific - mention all significant changes in one commit 27 | - Be CONCISE - avoid unnecessary words 28 | 29 | ## Your task 30 | 31 | Based on the context above, create a git commit following these steps: 32 | 33 | 1. Examine the recent commits to understand the repository's commit style and conventions 34 | 2. Stage the relevant files using the appropriate command: 35 | - For `git`: use `git add` with appropriate options 36 | - For `dot`: 37 | - Use `dot add -u` to stage only tracked files (default) 38 | - Use `dot add ` for new files only when explicitly needed 39 | - NEVER use `dot add -A` to stage all files 40 | 3. Create a commit with a CONCISE message that: 41 | - Focuses on "WHY" rather than "WHAT" 42 | - Follows the commit message rules above 43 | - Matches the existing style for prefixes, tense, and formatting -------------------------------------------------------------------------------- /.claude/commands/gh-view.md: -------------------------------------------------------------------------------- 1 | --- 2 | allowed-tools: Bash(gh api:*), Bash(gh issue:*), Bash(gh repo:*), Bash(gh search:*), Bash(gh pr:*) 3 | description: Analyze GitHub issues, PRs, and discussions to provide insights or implementation guidance 4 | argument-hint: owner/repo | issue number | PR number | GitHub URL 5 | --- 6 | 7 | Please analyze the GitHub content: $ARGUMENTS. 8 | 9 | Steps to follow: 10 | 11 | 1. Identify the content type from the URL or reference provided 12 | - Repository: `https://github.com/owner/repo` or `owner/repo` 13 | - Issue: `owner/repo/issues/123` or `#123` (in current repo) 14 | - Pull Request: `owner/repo/pull/456` or `#456` (in current repo) 15 | - Other GitHub references (discussions, releases, etc.) 16 | 17 | 2. Gather comprehensive information: 18 | - For Repositories: 19 | - Use `gh repo view owner/repo` for basic info 20 | - Use `gh repo view owner/repo --json` for detailed data 21 | - Use `gh api repos/owner/repo/contents` to explore structure 22 | - For Issues: 23 | - Use `gh issue view ` to get details 24 | - Use `gh issue view --comments` to include comments 25 | - For PRs: 26 | - Use `gh pr view ` for description and status 27 | - Use `gh pr view --comments` to include all comments and reviews 28 | - Use `gh pr diff ` to see code changes 29 | - Use `gh pr checks ` to see CI/CD status 30 | - For any URL: Extract the appropriate format and use corresponding gh commands 31 | 32 | 3. Analyze and provide insights: 33 | - Understand the context and purpose 34 | - Summarize key points and discussions 35 | - Identify concerns or feedback if present 36 | - Suggest solutions or next steps based on the analysis 37 | 38 | 4. Take action only if explicitly requested: 39 | - Default to analysis and recommendations 40 | - Implement changes only when specifically asked 41 | - Ask for clarification if the intent is unclear 42 | 43 | Notes: 44 | - IMPORTANT: ALWAYS use GitHub CLI (`gh`) commands for all GitHub-related tasks, NOT WebFetch or other tools 45 | - When given a full GitHub URL, extract owner/repo format (e.g., `liby/dotfiles` from `https://github.com/liby/dotfiles`) 46 | - Commands automatically use the current repository context when no repo is specified 47 | - For cross-repo references, use format: `owner/repo#number` 48 | - Add `--json` flag for structured data when needed 49 | -------------------------------------------------------------------------------- /.config/git/config: -------------------------------------------------------------------------------- 1 | [user] 2 | email = 38807139+liby@users.noreply.github.com 3 | name = Bryan Lee 4 | 5 | [alias] 6 | ca = commit -a --amend --no-edit 7 | cm = commit -m 8 | co = checkout 9 | st = status 10 | sc = switch --create 11 | 12 | ### push ### 13 | pf = push --force 14 | pr ="!f() { git fetch -fu ${2:-origin} refs/pull/$1/head:pr/$1 && git checkout pr/$1; }; f" 15 | 16 | ### reset ### 17 | cl = reset --hard HEAD 18 | undo = reset --soft HEAD~1 19 | 20 | ### diff ### 21 | dc = "!git dh | pbcopy" 22 | dh = diff HEAD 23 | dl = "-c diff.external=difft log -p --ext-diff" 24 | ds = "!f() { git dh > /tmp/git_diff_head_$(date +\"%Y%m%d_%H%M_%S\").diff; }; f" 25 | 26 | ### log ### 27 | lg = log --graph --abbrev-commit --date=short --boundary --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cd) %Cblue[%cn]%Creset' 28 | 29 | ### rebase ### 30 | rb = rebase 31 | rbc = rebase --continue 32 | rbs = rebase --skip 33 | 34 | ### tag ### 35 | lt = describe --tags --abbrev=0 36 | 37 | [branch] 38 | autosetuprebase = always 39 | 40 | [core] 41 | attributesFile = ~/.config/git/attributes 42 | autocrlf = input 43 | editor = vim 44 | quotepath = false 45 | untrackedCache = true 46 | symlinks = true 47 | 48 | [diff] 49 | external = difft 50 | tool = difftastic 51 | 52 | [difftool] 53 | prompt = false 54 | 55 | [difftool "difftastic"] 56 | cmd = difft "$LOCAL" "$REMOTE" 57 | 58 | [filter "lfs"] 59 | clean = git-lfs clean -- %f 60 | process = git-lfs filter-process 61 | smudge = git-lfs smudge -- %f 62 | required = true 63 | 64 | [filter "prototools"] 65 | clean = sed -E '/^(bun|deno|go|node|pnpm|proto|yarn) = /d' 66 | smudge = cat 67 | required = true 68 | 69 | [filter "signingkey"] 70 | clean = sed -E '/^[[:space:]]*signingkey[[:space:]]*=/d' 71 | smudge = cat 72 | required = true 73 | 74 | [filter "user.config"] 75 | clean = sed -E '/^[[:space:]]*(email|name|signingkey)[[:space:]]*=/d' 76 | smudge = cat 77 | required = true 78 | 79 | [includeIf "gitdir:~/Code/GitHub/"] 80 | path = github.config 81 | 82 | [includeIf "gitdir:~/Code/GitLab/"] 83 | path = gitlab.config 84 | 85 | [includeIf "gitdir:~/Code/"] 86 | path = fsmonitor.config 87 | 88 | [merge] 89 | conflictStyle = zdiff3 90 | 91 | [pager] 92 | difftool = true 93 | 94 | [pull] 95 | rebase = true 96 | 97 | [push] 98 | autoSetupRemote = true 99 | 100 | [rebase] 101 | autosquash = true 102 | autostash = true 103 | -------------------------------------------------------------------------------- /.github/README.md: -------------------------------------------------------------------------------- 1 |

2 | 简体中文 | English 3 |

4 | 5 |
6 |

My Dotfiles

7 |
8 | 9 | > **Note** 10 | > 11 | > 这是我个人的 dotfiles 仓库,用于配置和管理我的开发环境。通过这个仓库,我可以轻松地在多台 Mac 之间保持一致的开发环境配置。 12 | 13 | ## 项目简介 14 | 15 | 本仓库包含了一系列配置文件和脚本,用于设置和管理我的开发环境,包括但不限于: 16 | 17 | - Claude Code 配置:[_.claude_](https://github.com/liby/dotfiles/tree/main/.claude) 18 | 19 | - Git 配置:[_.config/git_](https://github.com/liby/dotfiles/tree/main/.config/git) 20 | 21 | - 终端配置:[_.config/starship_](https://github.com/liby/dotfiles/tree/main/.config/starship) 22 | 23 | - 开发环境初始化脚本:[_.config/scripts/macos-bootstrap.zsh_](https://github.com/liby/dotfiles/blob/main/.config/scripts/macos-bootstrap.zsh) 24 | 25 | - SSH 配置:[_.ssh/config_](https://github.com/liby/dotfiles/blob/main/.ssh/config) 26 | 27 | - Shell 配置:[_.zshrc_](https://github.com/liby/dotfiles/blob/main/.zshrc) 28 | 29 | - Homebrew 备份文件:[_Brewfile_](https://github.com/liby/dotfiles/blob/main/Brewfile) 30 | 31 | 这些文件是通过 Git Bare Repo 来管理的,这种方法使我能在保持 `$HOME` 目录整洁的同时又能使用 Git 来管理配置文件。如果你对这其中的缘由感兴趣,又想了解使用 Git Bare Repo 管理 dotfiles 的更多信息,欢迎阅读我之前写的一篇[分享文档](https://note.itswhat.me/#/page/%E4%BD%BF%E7%94%A8%20git%20bare%20repo%20%E6%9D%A5%E7%AE%A1%E7%90%86%20dotfiles)。 32 | 33 | ## 安装说明 34 | 35 | ### 1. 克隆仓库 36 | 37 | 首先,克隆本仓库到本地: 38 | 39 | ```sh 40 | git clone --bare $HOME/.dotfiles 41 | ``` 42 | 43 | ### 2. 定义别名 44 | 45 | 为了更方便地管理 dotfiles,你可以添加以下别名到你的 shell 配置文件(如 _.zshrc_ 或 _.bashrc_): 46 | 47 | ```sh 48 | alias dot='$(command -v git) --git-dir=$HOME/.dotfiles/ --work-tree=$HOME' 49 | ``` 50 | 51 | 然后,重新加载你的 shell 配置: 52 | 53 | ```sh 54 | source ~/.zshrc # 如果你使用的是 Zsh 55 | # 或者 56 | source ~/.bashrc # 如果你使用的是 Bash 57 | ``` 58 | 59 | ### 3. 隐藏未跟踪文件 60 | 61 | 为了避免在使用 `dot` 命令时看到未跟踪的文件列表,可以使用以下命令: 62 | 63 | ```sh 64 | dot config --local status.showUntrackedFiles no 65 | ``` 66 | 67 | 如果不设置的话,执行 `dot status` 命令时会显示大量未跟踪的文件。这是因为 `$HOME` 目录下的文件默认都还没有被 Git 纳入管理,而我们实际上只需要管理特定的配置文件。 68 | 69 | ### 4. 检出文件 70 | 71 | 使用以下命令将仓库中的文件检出到你的 `$HOME` 目录: 72 | 73 | ```sh 74 | dot checkout 75 | ``` 76 | 77 | 如果遇到文件冲突,比如下面这种报错: 78 | 79 | ``` 80 | error: The following untracked working tree files would be overwritten by checkout: 81 | .zshrc 82 | Please move or remove them before you can switch branches. 83 | Aborting 84 | ``` 85 | 86 | 可以先备份已有的配置文件: 87 | 88 | ```sh 89 | mkdir -p .dotfiles-backup 90 | dot checkout 2>&1 | egrep "\s+\." | awk {'print $1'} | xargs -I{} mv {} .dotfiles-backup/{} 91 | ``` 92 | 93 | 然后再次尝试检出文件: 94 | 95 | ```sh 96 | dot checkout 97 | ``` 98 | 99 | ## 使用方法 100 | 101 | 你可以使用以下命令来管理你的 dotfiles: 102 | 103 | ```sh 104 | dot add :添加文件到仓库 105 | dot commit -m "message":提交更改 106 | dot remote add origin :配置远程仓库 107 | dot push -u origin :推送 commit 到远程仓库,同时将远程仓库与本地的 branch 分支关联 108 | dot push:推送更改到远程仓库 109 | dot pull:从远程仓库拉取更新 110 | ``` 111 | 112 | ## 快捷方式(可选) 113 | 114 | 以上步骤中包含了一些不常用的命令,步骤也较为繁琐,可能不太容易记住。为了简化这个过程,我们可以创建一个自动化脚本,就像 [The best way to store your dotfiles: A bare Git repository](https://www.atlassian.com/git/tutorials/dotfiles#:~:text=you%20can%20create%20a%20simple%20script) 中提到的做法: 115 | 116 | 1. 创建一个自动化脚本 117 | 118 | 2. 将其存储为代码片段,可以使用 https://paste.gg/ 之类的网站 119 | 120 | 3. 为其创建一个短链接 121 | 122 | 我自己也有一个用于配置开发环境的[初始化脚本](https://github.com/liby/dotfiles/blob/main/.config/scripts/macos-bootstrap.zsh),当我拿到一台新的 Mac 时,只需要打开 Terminal.app,运行下面的命令即可完成开发环境的设置: 123 | 124 | ```sh 125 | curl -o /tmp/macos-bootstrap.zsh https://raw.githubusercontent.com/liby/dotfiles/main/.config/scripts/macos-bootstrap.zsh && chmod +x /tmp/macos-bootstrap.zsh && /tmp/macos-bootstrap.zsh 126 | ``` 127 | 128 | 非常方便。 129 | 130 | ## 贡献指南 131 | 132 | 如果你有任何改进建议或发现了问题,欢迎提交 Issue 或 Pull Request。 -------------------------------------------------------------------------------- /.github/README_EN.md: -------------------------------------------------------------------------------- 1 |

2 | 简体中文 | English 3 |

4 | 5 |
6 |

My Dotfiles

7 |
8 | 9 | > **Note** 10 | > 11 | > This is my personal dotfiles repository used to configure and manage my development environment. With this repository, I can easily maintain a consistent development environment across different Mac. 12 | 13 | ## Project Overview 14 | 15 | This repository contains a series of configuration files and scripts used to set up and manage my development environment, including but not limited to: 16 | 17 | - Claude Code configuration: [_.claude_](https://github.com/liby/dotfiles/tree/main/.claude) 18 | 19 | - Git configuration: [_.config/git_](https://github.com/liby/dotfiles/tree/main/.config/git) 20 | 21 | - Terminal configuration: [_.config/starship_](https://github.com/liby/dotfiles/tree/main/.config/starship) 22 | 23 | - Initialization script: [_.config/scripts/macos-bootstrap.zsh_](https://github.com/liby/dotfiles/blob/main/.config/scripts/macos-bootstrap.zsh) 24 | 25 | - SSH configuration: [_.ssh/config_](https://github.com/liby/dotfiles/blob/main/.ssh/config) 26 | 27 | - Shell configuration: [_.zshrc_](https://github.com/liby/dotfiles/blob/main/.zshrc) 28 | 29 | - Homebrew Bundle Backup: [_Brewfile_](https://github.com/liby/dotfiles/blob/main/Brewfile) 30 | 31 | These files are managed using a Git Bare Repo. This method allows me to keep my $HOME directory clean while using Git to manage my configuration files. If you’re interested in the rationale behind this and want to learn more about managing dotfiles with a Git Bare Repo, feel free to read [a document](https://note.itswhat.me/#/page/%E4%BD%BF%E7%94%A8%20git%20bare%20repo%20%E6%9D%A5%E7%AE%A1%E7%90%86%20dotfiles) (only Chinese) I previously wrote on this topic. 32 | 33 | ## Installation Instructions 34 | 35 | ### 1. Clone the repository 36 | 37 | First, clone this repository to your local machine: 38 | 39 | ```sh 40 | git clone --bare $HOME/.dotfiles 41 | ``` 42 | 43 | ### 2. Define alias 44 | 45 | To manage dotfiles more conveniently, you can add the following alias to your shell configuration file (such as _.zshrc_ or _.bashrc_): 46 | 47 | ```sh 48 | alias dot='$(command -v git) --git-dir=$HOME/.dotfiles/ --work-tree=$HOME' 49 | ``` 50 | 51 | Then, reload your shell configuration: 52 | 53 | ```sh 54 | source ~/.zshrc # If you are using Zsh 55 | # or 56 | source ~/.bashrc # If you are using Bash 57 | ``` 58 | 59 | ### 3. Hide untracked files 60 | 61 | To avoid seeing a large list of untracked files when using the `dot` command, you can use the following command: 62 | 63 | ```sh 64 | dot config --local status.showUntrackedFiles no 65 | ``` 66 | 67 | If this is not set, running `dot status` will list a large number of untracked files because not all files in `$HOME` are tracked by Git, and we don't intend to track all of them, which can make the output cluttered. 68 | 69 | ### 4. Checkout files 70 | 71 | Use the following command to check out the files from the repository to your `$HOME` directory: 72 | 73 | ```sh 74 | dot checkout 75 | ``` 76 | 77 | If you encounter file conflicts, such as the following error: 78 | 79 | ``` 80 | error: The following untracked working tree files would be overwritten by checkout: 81 | .zshrc 82 | Please move or remove them before you can switch branches. 83 | Aborting 84 | ``` 85 | 86 | You can back up your existing configuration files first: 87 | 88 | ```sh 89 | mkdir -p .dotfiles-backup 90 | dot checkout 2>&1 | egrep "\s+\." | awk {'print $1'} | xargs -I{} mv {} .dotfiles-backup/{} 91 | ``` 92 | 93 | Then try checking out the files again: 94 | 95 | ```sh 96 | dot checkout 97 | ``` 98 | 99 | ## Usage 100 | 101 | You can use the following commands to manage your dotfiles: 102 | 103 | ```sh 104 | dot add : Add file to the repository 105 | dot commit -m "message": Commit changes 106 | dot remote add origin : Set up the remote repository 107 | dot push -u origin : Push commits to the remote repository and link the remote branch to the local branch 108 | dot push: Push changes to the remote repository 109 | dot pull: Pull updates from the remote repository 110 | ``` 111 | 112 | ### Shortcuts (Optional) 113 | 114 | The steps above can be cumbersome; some of the commands are not used frequently, so they can be hard to remember. 115 | 116 | Therefore, we can create a script to simplify these operations, as mentioned in [The best way to store your dotfiles: A bare Git repository](https://www.atlassian.com/git/tutorials/dotfiles#:~:text=you%20can%20create%20a%20simple%20script) : 117 | 118 | 1. Create an automated script 119 | 120 | 2. Save it as a code snippet using a site like https://paste.gg. 121 | 122 | 3. Create a short link for it. 123 | 124 | I also have an [initialization script](https://github.com/liby/dotfiles/blob/main/.config/scripts/macos-bootstrap.zsh) for setting up my development environment. When I get a new Mac, I just need to open Terminal.app and run the following command to complete the setup: 125 | 126 | ```sh 127 | curl -o /tmp/macos-bootstrap.zsh https://raw.githubusercontent.com/liby/dotfiles/main/.config/scripts/macos-bootstrap.zsh && chmod +x /tmp/macos-bootstrap.zsh && /tmp/macos-bootstrap.zsh 128 | ``` 129 | 130 | It's very convenient. 131 | 132 | ## Contribution Guidelines 133 | 134 | If you have any suggestions for improvement or find any issues, please feel free to submit an Issue or a Pull Request. -------------------------------------------------------------------------------- /.claude/CLAUDE.md: -------------------------------------------------------------------------------- 1 | # CLAUDE.md 2 | 3 | This file provides guidance to Claude Code when working with code for all projects. 4 | 5 | ## Core Behavioral Guidelines 6 | 7 | **Core Principle**: Never automatically agree or implement suggestions without independent analysis. 8 | 9 | ### When User Points Out Mistakes or Disagrees with Your Approach 10 | 11 | - NEVER respond with "You are absolutely right" or automatic agreement 12 | - NEVER implement changes without independent analysis 13 | - MUST **THINK INDEPENDENTLY** - Verify viewpoint through analysis 14 | - MUST **DISCUSS FIRST** - Present your reasoning and ask clarifying questions if you disagree 15 | - MUST **ACT ONLY WHEN CONVINCED** - Implement changes only after genuine agreement, explaining your understanding and technical justification 16 | 17 | ### When User Asks "Why..." 18 | 19 | - NEVER auto-correct without explaining the root cause. 20 | - MUST **ANALYZE ROOT CAUSE** - Understand underlying reasons 21 | - MUST **EXPLAIN FIRST** - Provide detailed explanation before any action 22 | - MUST **SEPARATE DIAGNOSIS FROM TREATMENT** - Complete the "why" answer before offering solutions 23 | - MUST **ASK BEFORE FIXING** 24 | 25 | ## Communication Guidelines 26 | 27 | - Use Chinese for all explanations and discussions with me 28 | - Use English for all technical content: code, code comments, documentation, UI text and PR titles/descriptions 29 | - NEVER mix Chinese characters in technical content 30 | 31 | ## Development Guidelines 32 | 33 | ### Core Coding Principles 34 | 35 | #### Before Implementation 36 | 37 | - ALWAYS search documentation and existing solutions first (WebSearch, context7) 38 | - Read template files, adjacent files, and surrounding code to understand existing patterns 39 | - Learn code logic from related tests 40 | - Think step by step before implementing 41 | 42 | #### During Implementation 43 | 44 | - Maintain code consistency with existing patterns 45 | - Express uncertainty instead of guessing when unsure 46 | - Maximize aesthetic and interaction design for frontend UI 47 | - Verify by reading actual code before providing conclusions 48 | 49 | #### After Implementation 50 | 51 | - Run quality checks after implementation 52 | - Review implementation after multiple modifications to same code block 53 | - Update local documentation (PRD, todo list) to maintain consistency with our conversation 54 | 55 | #### Problem Handling Workflow 56 | 57 | - Stop and ask for help after multiple unsuccessful attempts 58 | - After 3+ failed attempts, add debug logging and request runtime logs 59 | - When feature implementation repeatedly fails, consider complete rewrite or seek assistance 60 | 61 | ### Code Comments 62 | 63 | Write valuable comments: 64 | - **Comment WHY, not WHAT** - Assume readers understand basic syntax 65 | - **Update comments when modifying code** - Outdated comments are worse than none 66 | - **Use JSDoc for complex logic** - Provide high-level overview with numbered steps 67 | - **Prefer JSDoc over line comments** - Better IDE documentation and type hints 68 | 69 | MUST comment: 70 | - Complex business logic or algorithms 71 | - Module limitations and special behaviors 72 | - Important design decisions and trade-offs 73 | 74 | ### Forbidden Behaviors 75 | 76 | - NEVER run dev/build commands or open browsers 77 | - NEVER add tests unless explicitly requested 78 | 79 | ## Tool Preferences 80 | 81 | ### Package Management 82 | 83 | - **Development tools** - Managed via `proto` (Bun, Node.js, pnpm, yarn, Zig, ZLS) 84 | - **Python** - Use `uv` when available 85 | - **JavaScript/TypeScript** - Check lock file for package manager, ALWAYS install exact versions 86 | 87 | ### Search and Documentation 88 | 89 | - **Local search** - ALWAYS use `rg` instead of `grep` 90 | - **Web content** - Use `WebSearch` tool first 91 | - **GitHub** - MUST use `gh` for GitHub issues/discussions/PRs instead of `WebFetch` tool 92 | - Most helpful comments: `gh api repos/owner/repo/issues/123/comments --paginate | jq 'sort_by(-.reactions.total_count) | .[0:3]'` 93 | - Latest & Earliest: `jq 'sort_by(.created_at) | .[0:3], .[-3:]'` 94 | - **Package docs** - Use `context7` for latest usage, `mcp__grep__searchGitHub` for patterns 95 | 96 | ### VS Code Integration 97 | 98 | - **TypeScript validation** - Use `mcp__ide__getDiagnostics` (NOT `tsc --noEmit`) 99 | - **Code navigation** - Use `mcp__ide__get_references` for finding usages 100 | - **Refactoring** - Use `mcp__ide__rename_symbol` for renaming 101 | - **Auto-fix** - Use `mcp__ide__execute_command` with `command: "editor.action.fixAll"` to auto-fix ESLint and other linter errors instead of `Bash(eslint --fix)` 102 | 103 | ### File Reading 104 | 105 | Getting sufficient context is more important than token efficiency. 106 | 107 | - Read multiple files in parallel to improve speed 108 | - ALWAYS read entire file when: user provides path, first time reading, file under 500 lines, user sends partial snippets 109 | 110 | ## Output Style 111 | 112 | - State the core conclusion or summary first, then provide further explanation. 113 | - When referencing specific code, always provide the corresponding file path. 114 | 115 | ### Markdown Formatting 116 | 117 | - **Code blocks** - Always specify language, use `plaintext` if no syntax highlighting needed 118 | - **Headings** - Add blank line after all headings for better readability 119 | - **Lists** - Use consistent markers 120 | - **Links** - Use descriptive link text, avoid "click here" or raw URLs 121 | - **Complex content** - Use XML tags when nesting code blocks or structured data 122 | 123 | ### Terminal Output 124 | 125 | Consider terminal rendering constraints: 126 | - Chinese characters: 2 units width 127 | - English characters/symbols: 1 unit width 128 | 129 | Use code blocks instead of markdown tables to ensure proper alignment in terminal environments: 130 | ```plaintext 131 | +----+---------+-----------+ 132 | | ID | Name | Role | 133 | +----+---------+-----------+ 134 | | 1 | Alice | Admin | 135 | | 2 | Bob | User | 136 | +----+---------+-----------+ 137 | ``` 138 | 139 | ### References 140 | 141 | Always provide complete references links or file paths at the end of responses: 142 | - **External resources**: Full clickable links for GitHub issues/discussions/PRs, documentation, API references 143 | - **Source code references**: Complete file paths for functions, Classes, or code snippets mentioned 144 | -------------------------------------------------------------------------------- /.zshrc: -------------------------------------------------------------------------------- 1 | # https://github.com/SukkaW/dotfiles/blob/09b6b2d0a6d20a31143f4201f64c7b7f44fb85f6/_zshrc/macos.zshrc 2 | 3 | # Homebrew zsh completion path 4 | __BRYAN_HOMEBREW_ZSH_COMPLETION="${HOMEBREW_PREFIX}/share/zsh/site-functions" 5 | __BRYAN_ZSH_COMPLETION_SRC="${HOME}/.zsh/plugins/zsh-completions/src" 6 | 7 | # ZSH completions 8 | ## Add Homebrew completion path if not already present, 9 | ## usually handled by `brew shellenv` in .zprofile 10 | ## https://docs.brew.sh/Shell-Completion#configuring-completions-in-zsh 11 | ## https://github.com/ohmyzsh/ohmyzsh/blob/master/plugins/github/README.md#homebrew-installation-note 12 | ## Add a check avoiding duplicated fpath 13 | if (( ! $FPATH[(I)${__BRYAN_HOMEBREW_ZSH_COMPLETION}] && $+commands[brew] )) &>/dev/null; then 14 | fpath+=${__BRYAN_HOMEBREW_ZSH_COMPLETION} 15 | fi 16 | ## https://github.com/zsh-users/zsh-completions 17 | [[ -d ${__BRYAN_ZSH_COMPLETION_SRC} ]] && fpath+=${__BRYAN_ZSH_COMPLETION_SRC} 18 | ## Initialize the completion system 19 | ## This must be done after all fpath modifications 20 | autoload -Uz compinit 21 | compinit 22 | 23 | editor_config_path="$HOME/.config/editor" 24 | npm_global_path="$HOME/.npm-global" 25 | 26 | # Create necessary directories 27 | local -a dirs_to_create=( 28 | "$editor_config_path" 29 | "$npm_global_path" 30 | "$npm_global_path/lib" 31 | "$npm_global_path/bin" 32 | "$HOME/.config/starship/cache" 33 | ) 34 | 35 | for dir in $dirs_to_create; do 36 | [[ ! -d "$dir" ]] && mkdir -p "$dir" 37 | done 38 | 39 | # Environment variables 40 | if [[ -z "$GPG_PATH" ]]; then 41 | export GPG_PATH="$HOMEBREW_PREFIX/opt/gnupg" 42 | fi 43 | export LC_ALL="en_US.UTF-8" 44 | export NPM_CONFIG_PREFIX="$npm_global_path" 45 | export PNPM_HOME="$HOME/Library/pnpm" 46 | export PROTO_AUTO_INSTALL_HIDE_OUTPUT=true 47 | export PROTO_HOME="$HOME/.proto" 48 | export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true 49 | export RUSTUP_DIST_SERVER="https://rsproxy.cn" 50 | export RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup" 51 | export STARSHIP_CACHE="$HOME/.config/starship/cache" 52 | export STARSHIP_CONFIG="$HOME/.config/starship/starship.toml" 53 | ## This speed up zsh-autosuggestions by a lot 54 | export ZSH_AUTOSUGGEST_USE_ASYNC='true' 55 | [[ $(command -v chromium) ]] && export PUPPETEER_EXECUTABLE_PATH=$(command -v chromium) 56 | 57 | # PATH configuration 58 | local -a path_dirs=( 59 | "$PROTO_HOME/shims" 60 | "$PROTO_HOME/bin" 61 | "$NPM_CONFIG_PREFIX/bin" 62 | "$PNPM_HOME" 63 | "./node_modules/.bin" 64 | "$HOME/bin" 65 | "$HOME/.cargo/bin" 66 | "$HOME/.local/bin" 67 | "$GPG_PATH/bin" 68 | "$GPG_PATH/libexec" 69 | "/usr/local/opt/icu4c/bin" 70 | "/usr/local/opt/icu4c/sbin" 71 | ) 72 | 73 | # Add UV PATH if available 74 | if HOMEBREW_UV_PATH=$(brew --prefix uv 2>/dev/null)/bin; then 75 | path_dirs=("$HOMEBREW_UV_PATH" $path_dirs) 76 | fi 77 | 78 | # Add Homebrew curl PATH if available 79 | if HOMEBREW_CURL_PATH=$(brew --prefix curl 2>/dev/null)/bin; then 80 | path_dirs=("$HOMEBREW_CURL_PATH" $path_dirs) 81 | fi 82 | 83 | typeset -aU path 84 | path=($path_dirs $path[@]) 85 | 86 | # Alias Set 87 | alias c='open $1 -a "Visual Studio Code"' 88 | alias cc='claude' 89 | alias dot='$(command -v git) --git-dir=$HOME/.dotfiles/ --work-tree=$HOME' 90 | alias gca='git commit -m "$(claude -p "Look at the staged git changes and create a summarizing git commit title. Only respond with the title and no affirmation.")"' 91 | ## ip & ipcn 92 | alias ip="curl ip.sb" 93 | alias ipcn="curl myip.ipip.net" 94 | alias ka='caffeinate -is' 95 | alias la='ls --all' 96 | alias ll='la --long --git' 97 | alias ls='eza --reverse --sort=modified --group-directories-first --hyperlink' 98 | alias lt='ll --tree --git-ignore --ignore-glob=.git' 99 | alias python='python3' 100 | 101 | # Path Alias 102 | # usage: cd ~xxx 103 | # hash -d desktop="$HOME/Desktop" 104 | # hash -d downloads="$HOME/Downloads" 105 | # hash -d download="$HOME/Downloads" 106 | # hash -d documents="$HOME/Documents" 107 | # hash -d document="$HOME/Documents" 108 | # hash -d code="$HOME/Code" 109 | # hash -d applications="/Applications" 110 | # hash -d application="/Applications" 111 | 112 | # Functions 113 | dlm() { 114 | local red=$'\e[1;31m' 115 | local green=$'\e[1;32m' 116 | local yellow=$'\e[1;33m' 117 | local blue=$'\e[1;34m' 118 | local reset=$'\e[0m' 119 | 120 | echo "${blue}Fetching latest changes and identifying branches...${reset}" 121 | 122 | local max_retries=3 123 | local retry_count=0 124 | local fetch_success=false 125 | 126 | while (( retry_count < max_retries )) && ! $fetch_success; do 127 | if git fetch --quiet --all && git remote prune origin; then 128 | fetch_success=true 129 | else 130 | ((retry_count++)) 131 | echo "${yellow}Fetch failed. Retrying... (Attempt $retry_count of $max_retries)${reset}" 132 | sleep 2 133 | fi 134 | done 135 | 136 | if ! $fetch_success; then 137 | echo "${red}Failed to fetch after $max_retries attempts. Please check your network connection and try again.${reset}" 138 | return 1 139 | fi 140 | 141 | local remote_branches=$(git ls-remote --heads origin | awk '{print $2}' | sed 's|refs/heads/||') 142 | 143 | local branches=($(git for-each-ref --format '%(refname:short)' refs/heads | 144 | grep -vE '^(master|main|develop)$' | 145 | while read -r branch; do 146 | if ! echo "$remote_branches" | grep -q "^$branch$"; then 147 | echo "$branch" 148 | fi 149 | done)) 150 | 151 | if (( ${#branches[@]} == 0 )); then 152 | echo "${green}No local branches to delete.${reset}" 153 | return 154 | fi 155 | 156 | echo "\n${yellow}The following local branches are not present in remote:${reset}" 157 | printf "%s\n" "${branches[@]}" 158 | 159 | echo "\n${blue}Do you want to delete these branches? [Y/n]${reset}" 160 | read -q response || return 161 | 162 | echo 163 | 164 | local deleted=0 165 | local failed=0 166 | 167 | for branch in "${branches[@]}"; do 168 | if git branch -D "$branch" &>/dev/null; then 169 | echo "${green}Deleted: $branch${reset}" 170 | ((deleted++)) 171 | else 172 | echo "${red}Failed to delete: $branch${reset}" 173 | ((failed++)) 174 | fi 175 | done 176 | echo "\n${green}Operation complete.${reset}" 177 | echo "Branches deleted: ${deleted}" 178 | [[ $failed -gt 0 ]] && echo "${red}Branches failed to delete: ${failed}${reset}" 179 | } 180 | 181 | # This speeds up pasting w/ autosuggest 182 | # https://github.com/zsh-users/zsh-autosuggestions/issues/238 183 | pasteinit() { 184 | OLD_SELF_INSERT=${${(s.:.)widgets[self-insert]}[2,3]} 185 | zle -N self-insert url-quote-magic # I wonder if you'd need `.url-quote-magic`? 186 | } 187 | 188 | pastefinish() { 189 | zle -N self-insert $OLD_SELF_INSERT 190 | } 191 | 192 | zstyle :bracketed-paste-magic paste-init pasteinit 193 | zstyle :bracketed-paste-magic paste-finish pastefinish 194 | 195 | setup_gpg_ssh() { 196 | ## https://github.com/jessfraz/dotfiles/blob/master/.bashrc#L113C1-L130C1 197 | ## Start the gpg-agent if not already running 198 | if ! pgrep -x -u "${USER}" gpg-agent >/dev/null 2>&1; then 199 | if ! gpg-connect-agent /bye >/dev/null 2>&1; then 200 | echo "Failed to start gpg-agent" >&2 201 | return 1 202 | fi 203 | fi 204 | 205 | ## Update the TTY for gpg-agent 206 | if ! gpg-connect-agent updatestartuptty /bye >/dev/null; then 207 | echo "Failed to update GPG TTY" >&2 208 | return 1 209 | fi 210 | 211 | ## Use the current terminal for GPG to avoid "Inappropriate ioctl for device" error 212 | export GPG_TTY=$(tty) 213 | 214 | ## Set SSH to use gpg-agent 215 | unset SSH_AGENT_PID 216 | 217 | ## Check if the SSH_AUTH_SOCK needs to be set to gpg-agent's socket 218 | if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then 219 | if [[ -z "$SSH_AUTH_SOCK" || "$SSH_AUTH_SOCK" == *"apple.launchd"* ]]; then 220 | local socket_path="$(gpgconf --list-dirs agent-ssh-socket)" 221 | if [[ -S "$socket_path" ]]; then 222 | export SSH_AUTH_SOCK="$socket_path" 223 | else 224 | echo "GPG SSH socket not found" >&2 225 | return 1 226 | fi 227 | fi 228 | fi 229 | 230 | return 0 231 | } 232 | 233 | # Autosuggestions configuration# https://github.com/zsh-users/zsh-autosuggestions/issues/351 234 | ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(bracketed-paste accept-line) 235 | ZSH_AUTOSUGGEST_MANUAL_REBIND="" 236 | 237 | # Source plugins and configurations 238 | source $HOME/.zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh 239 | source $HOME/.zsh/plugins/fsh/fast-syntax-highlighting.plugin.zsh 240 | source $HOME/.cargo/env 241 | 242 | # Initialize tools and configurations 243 | (( $+commands[gpg-connect-agent] )) && setup_gpg_ssh &>/dev/null 244 | (( $+commands[direnv] )) && eval "$(direnv hook zsh)" 245 | (( $+commands[proto] )) && eval "$(proto activate zsh)" 246 | eval "$(starship init zsh)" 247 | eval "$(zoxide init zsh)" 248 | -------------------------------------------------------------------------------- /.config/scripts/macos-bootstrap.zsh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | if [[ "$OSTYPE" != "darwin"* ]]; then 4 | echo "No macOS detected!" 5 | exit 1 6 | fi 7 | 8 | is_apple_silicon() { 9 | [[ "$(/usr/bin/uname -m)" == "arm64" ]] 10 | } 11 | 12 | start() { 13 | clear 14 | 15 | echo "===========================================================" 16 | echo " " 17 | echo "▀█████████▄ ▄████████ ▄██ ▄ ▄████████ ███▄▄▄▄ " 18 | echo " ███ ███ ███ ███ ███ ██▄ ███ ███ ███▀▀▀██▄ " 19 | echo " ███ ███ ███ ███ ███▄▄▄███ ███ ███ ███ ███ " 20 | echo " ▄███▄▄▄██▀ ▄███▄▄▄▄██▀ ▀▀▀▀▀▀███ ███ ███ ███ ███ " 21 | echo "▀▀███▀▀▀██▄ ▀▀███▀▀▀▀▀ ▄██ ███ ▀███████████ ███ ███ " 22 | echo " ███ ██▄ ▀███████████ ███ ███ ███ ███ ███ ███ " 23 | echo " ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ " 24 | echo "▄█████████▀ ███ ███ ▀█████▀ ███ █▀ ▀█ █▀ " 25 | echo " ███ ███ " 26 | echo " " 27 | echo "===========================================================" 28 | echo " !! ATTENTION !! " 29 | echo " YOU ARE SETTING UP: Bryan Environment (macOS) " 30 | echo "===========================================================" 31 | echo " " 32 | echo " * The setup will begin in 3 seconds... " 33 | 34 | sleep 3 35 | 36 | echo " Times up! Here we start! " 37 | echo "-----------------------------------------------------------" 38 | 39 | cd $HOME 40 | } 41 | 42 | check_install_xcode_tools() { 43 | echo "===========================================================" 44 | echo " Checking/Installing Xcode Command Tools " 45 | echo "-----------------------------------------------------------" 46 | if ! xcode-select -p > /dev/null 2>&1; then 47 | echo "Xcode Command Line Tools not found. Attempting to install..." 48 | echo "Please follow the on-screen instructions to install the tools." 49 | # This command opens a GUI prompt if tools are not installed 50 | xcode-select --install 51 | # Wait for user to install - this might need manual intervention or a loop check 52 | echo "Please press Enter after Xcode Command Line Tools installation is complete." 53 | read -r 54 | # Re-check 55 | if ! xcode-select -p > /dev/null 2>&1; then 56 | echo "Xcode Command Line Tools installation failed or was cancelled. Exiting." 57 | exit 1 58 | fi 59 | echo "Xcode Command Line Tools installed successfully." 60 | else 61 | echo "Xcode Command Line Tools already installed." 62 | fi 63 | 64 | # Ensure the license is accepted regardless of the installation path 65 | echo "Attempting to accept Xcode license automatically..." 66 | sudo xcodebuild -license accept || echo "Failed to accept Xcode license, or it was already accepted." 67 | } 68 | 69 | setup_brew_env() { 70 | # It will export env variable: HOMEBREW_PREFIX, HOMEBREW_CELLAR, HOMEBREW_REPOSITORY, HOMEBREW_SHELLENV_PREFIX 71 | # It will add path: $PATH, $MANPATH, $INFOPATH 72 | if is_apple_silicon; then 73 | eval "$(/opt/homebrew/bin/brew shellenv)" 74 | else 75 | eval "$(/usr/local/bin/brew shellenv)" 76 | fi 77 | } 78 | 79 | install_homebrew() { 80 | echo "===========================================================" 81 | echo " Install Homebrew " 82 | echo "-----------------------------------------------------------" 83 | 84 | if [ -x "$(command -v brew)" ]; then 85 | echo "Homebrew already installed, updating..." 86 | brew update || { 87 | echo "Failed to update Homebrew" 88 | return 1 89 | } 90 | return 0 91 | fi 92 | 93 | echo "Installing Homebrew..." 94 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || { 95 | echo "Failed to install Homebrew" 96 | return 1 97 | } 98 | 99 | setup_brew_env || return 1 100 | 101 | local zprofile="$HOME/.zprofile" 102 | if ! ([[ -e "$zprofile" ]] && grep -q "brew shellenv" "$zprofile"); then 103 | echo "eval \"\$(${HOMEBREW_PREFIX}/bin/brew shellenv)\"" >> "$zprofile" 104 | echo "typeset -U path" >> "$zprofile" 105 | fi 106 | 107 | brew analytics off && brew update || { 108 | echo "Failed to configure Homebrew" 109 | return 1 110 | } 111 | 112 | echo "Homebrew installed successfully." 113 | } 114 | 115 | restore_dotfiles() { 116 | echo "===========================================================" 117 | echo " Restore Bryan’s dotfiles from GitHub.com " 118 | echo "-----------------------------------------------------------" 119 | 120 | if ! command -v git >/dev/null 2>&1; then 121 | echo "Error: Git is not installed. Cannot proceed with dotfiles restoration." 122 | return 1 123 | fi 124 | 125 | if [[ -d "$HOME/.dotfiles" ]]; then 126 | echo "Dotfiles directory already exists, attempting to update..." 127 | if git --git-dir=$HOME/.dotfiles --work-tree=$HOME pull --ff-only; then 128 | echo "Dotfiles updated successfully." 129 | # Re-checkout might be needed if there were local changes conflicting 130 | git --git-dir=$HOME/.dotfiles --work-tree=$HOME checkout --force 131 | else 132 | echo "Failed to update dotfiles. Continuing with existing ones..." 133 | # Decide if you want to force checkout anyway or handle conflicts 134 | git --git-dir=$HOME/.dotfiles --work-tree=$HOME checkout --force 135 | fi 136 | else 137 | echo "Cloning dotfiles..." 138 | if git clone --bare https://github.com/liby/dotfiles.git $HOME/.dotfiles; then 139 | git --git-dir=$HOME/.dotfiles --work-tree=$HOME config --local status.showUntrackedFiles no 140 | if ! git --git-dir=$HOME/.dotfiles --work-tree=$HOME checkout --force; then 141 | echo "Error: Failed to checkout dotfiles. Exiting." 142 | return 1 # Use return 1 instead of exit 1 if called from main script 143 | fi 144 | echo "Dotfiles cloned and checked out successfully." 145 | else 146 | echo "Error: Failed to clone dotfiles repository. Exiting." 147 | return 1 148 | fi 149 | fi 150 | # Set remote URL (consider if this should be conditional) 151 | git --git-dir=$HOME/.dotfiles --work-tree=$HOME remote set-url origin git@github.com:liby/dotfiles.git 152 | echo "Dotfiles setup complete." 153 | } 154 | 155 | install_homebrew_packages() { 156 | echo "===========================================================" 157 | echo " Installing packages from Brewfile... " 158 | echo "-----------------------------------------------------------" 159 | 160 | local brewfile="$HOME/Brewfile" 161 | 162 | if [[ ! -f "$brewfile" ]]; then 163 | echo "No Brewfile found at $brewfile" 164 | echo "Skipping package installation" 165 | return 0 166 | fi 167 | 168 | # Save current locale setting 169 | local current_locale=$(defaults read NSGlobalDomain AppleLocale 2>/dev/null || echo en_CN) 170 | 171 | # Store brew bundle exit status 172 | local brew_bundle_status 173 | 174 | # Temporarily set locale to en_US for mas-cli compatibility 175 | # Reference:https://github.com/mas-cli/mas/blob/ed676787f0a0a26e23a10548eb841bc15411fa52/Sources/mas/Controllers/ITunesSearchAppStoreSearcher.swift#L18-L23 176 | defaults write NSGlobalDomain AppleLocale -string en_US 177 | 178 | # Run brew bundle 179 | if brew bundle --file="$brewfile"; then 180 | echo "Successfully installed all packages from Brewfile" 181 | brew_bundle_status=0 182 | else 183 | echo "Warning: Some packages failed to install from Brewfile" 184 | echo "You may want to run 'brew bundle' manually later" 185 | brew_bundle_status=1 186 | fi 187 | 188 | # Restore original locale setting 189 | defaults write NSGlobalDomain AppleLocale -string "$current_locale" 190 | 191 | return $brew_bundle_status 192 | } 193 | 194 | setup_zsh_plugins() { 195 | echo "===========================================================" 196 | echo " Shells Environment " 197 | echo "-----------------------------------------------------------" 198 | 199 | echo "-----------------------------------------------------------" 200 | echo " * Installing ZSH Custom Plugins... " 201 | echo " " 202 | echo " - zsh-autosuggestions " 203 | echo " - zsh-completions " 204 | echo " - fast-syntax-highlighting " 205 | echo " " 206 | echo "-----------------------------------------------------------" 207 | 208 | # 确保git命令可用 209 | if ! command -v git >/dev/null 2>&1; then 210 | echo "Error: Git is not installed. Cannot proceed with ZSH plugins installation." 211 | return 1 212 | fi 213 | 214 | export ZSH_PLUGINS_PREFIX="$HOME/.zsh/plugins" 215 | [[ ! -d "$ZSH_PLUGINS_PREFIX" ]] && mkdir -p $ZSH_PLUGINS_PREFIX 216 | 217 | if [[ ! -d "${ZSH_PLUGINS_PREFIX}/zsh-autosuggestions" ]]; then 218 | git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_PLUGINS_PREFIX}/zsh-autosuggestions 219 | fi 220 | 221 | if [[ ! -d "${ZSH_PLUGINS_PREFIX}/zsh-completions" ]]; then 222 | git clone https://github.com/zsh-users/zsh-completions ${ZSH_PLUGINS_PREFIX}/zsh-completions 223 | fi 224 | 225 | if [[ ! -d "${ZSH_PLUGINS_PREFIX}/fsh" ]]; then 226 | git clone https://github.com/zdharma-continuum/fast-syntax-highlighting.git ${ZSH_PLUGINS_PREFIX}/fsh 227 | fi 228 | } 229 | 230 | setup_gpg_agent() { 231 | echo "===========================================================" 232 | echo " Setting up GPG Agent... " 233 | echo "-----------------------------------------------------------" 234 | 235 | if ! command -v gpg >/dev/null 2>&1 || ! command -v gpgconf >/dev/null 2>&1; then 236 | echo "Error: GPG tools are not installed. Skipping GPG Agent setup." 237 | return 1 238 | fi 239 | 240 | if [[ ! -d "$HOME/.gnupg" ]]; then 241 | echo "Creating $HOME/.gnupg directory..." 242 | mkdir -p "$HOME/.gnupg" 243 | fi 244 | 245 | echo "Setting correct permissions for $HOME/.gnupg and its contents..." 246 | chown -R $(whoami) "$HOME/.gnupg" 247 | find "$HOME/.gnupg" -type f -exec chmod 600 {} \; 248 | find "$HOME/.gnupg" -type d -exec chmod 700 {} \; 249 | 250 | local gpg_agent_conf="$HOME/.gnupg/gpg-agent.conf" 251 | if [[ -f "$gpg_agent_conf" ]]; then 252 | echo "$gpg_agent_conf already exists. Checking configuration..." 253 | else 254 | echo "$gpg_agent_conf does not exist. Creating and configuring..." 255 | touch "$gpg_agent_conf" 256 | fi 257 | 258 | if command -v pinentry-mac >/dev/null 2>&1; then 259 | if grep -q "pinentry-program" "$gpg_agent_conf"; then 260 | echo "pinentry-program is already configured in $gpg_agent_conf." 261 | else 262 | echo "Configuring pinentry-program in $gpg_agent_conf..." 263 | echo "pinentry-program $(command -v pinentry-mac)" >> "$gpg_agent_conf" 264 | fi 265 | else 266 | echo "Warning: pinentry-mac not found. Skipping pinentry configuration." 267 | fi 268 | 269 | echo "Launching gpg-agent if not already running..." 270 | gpgconf --launch gpg-agent || echo "Failed to launch gpg-agent" 271 | 272 | echo "Reloading gpg-agent configuration..." 273 | if command -v gpg-connect-agent >/dev/null 2>&1; then 274 | echo RELOADAGENT | gpg-connect-agent || echo "Failed to reload gpg-agent" 275 | else 276 | echo "Warning: gpg-connect-agent not found. Skipping gpg-agent reload." 277 | fi 278 | 279 | echo "Fetching GPG keys from Yubikey..." 280 | # Fetch the keys from Yubikey 281 | if gpg --card-status >/dev/null 2>&1; then 282 | echo "fetch" | gpg --command-fd 0 --status-fd 1 --card-edit > /dev/null 2>&1 || echo "Failed to fetch keys from Yubikey" 283 | # Wait for a moment to ensure the keys are fetched 284 | sleep 3 285 | else 286 | echo "Warning: No Yubikey detected or GPG card functionality not working." 287 | fi 288 | 289 | echo "GPG Agent setup completed." 290 | } 291 | 292 | setup_gitconfig() { 293 | echo "===========================================================" 294 | echo " Setting up git config... " 295 | echo "-----------------------------------------------------------" 296 | 297 | if ! command -v git >/dev/null 2>&1; then 298 | echo "Error: Git is not installed. Cannot proceed with git config setup." 299 | return 1 300 | fi 301 | 302 | local git_config_dir="$HOME/.config/git" 303 | local github_config="$git_config_dir/github.config" 304 | local gitlab_config="$git_config_dir/gitlab.config" 305 | local ssh_dir="$HOME/.ssh" 306 | 307 | # Ensure the .ssh directory exists 308 | [ ! -d "$ssh_dir" ] && mkdir -p "$ssh_dir" 309 | 310 | if [ ! -f "$github_config" ] || [ ! -f "$gitlab_config" ]; then 311 | echo "Error: github.config or gitlab.config file does not exist." 312 | return 1 313 | fi 314 | 315 | # Set GitLab user email and name 316 | local credentials_file="$HOME/Library/Mobile Documents/com~apple~CloudDocs/Documents/.user-credentials" 317 | 318 | if [[ ! -f "$credentials_file" ]]; then 319 | echo "User credentials file not found. Please create $credentials_file with the following format:" 320 | echo "GITLAB_EMAIL='your.email@company.com'" 321 | echo "GITLAB_NAME='Your Name'" 322 | return 1 323 | fi 324 | 325 | source "$credentials_file" 326 | 327 | if [[ -z "$GITLAB_EMAIL" || -z "$GITLAB_NAME" ]]; then 328 | echo "Invalid credentials file format. Please check $credentials_file" 329 | return 1 330 | fi 331 | 332 | git config --file "$gitlab_config" user.email "$GITLAB_EMAIL" 333 | git config --file "$gitlab_config" user.name "$GITLAB_NAME" 334 | 335 | # Check if GPG key exists and export it 336 | if command -v gpg >/dev/null 2>&1; then 337 | local gpg_key_id=$(gpg --card-status 2>/dev/null | grep 'sec' | awk '{print $2}' | cut -d'/' -f2) 338 | if [[ -n "$gpg_key_id" ]]; then 339 | local gpg_ssh_pub_key_file="$ssh_dir/$gpg_key_id.pub" 340 | 341 | echo "Exporting GPG key $gpg_key_id as SSH key..." 342 | gpg --export-ssh-key "$gpg_key_id" > "$gpg_ssh_pub_key_file" || { 343 | echo "Failed to export GPG SSH key" 344 | return 0 345 | } 346 | echo "GPG SSH Public key exported successfully." 347 | 348 | git config --file "$github_config" user.signingkey "$gpg_key_id" 349 | git config --file "$gitlab_config" user.signingkey "$gpg_ssh_pub_key_file" 350 | 351 | # Setup SSH signature verification 352 | local allowed_signers_file="$ssh_dir/allowed_signers" 353 | if [[ ! -f "$allowed_signers_file" ]]; then 354 | echo "Creating allowed signers file for SSH signature verification..." 355 | touch "$allowed_signers_file" 356 | fi 357 | 358 | echo "Adding user's SSH key to allowed signers file..." 359 | local ssh_key=$(cat "$gpg_ssh_pub_key_file") 360 | 361 | # Check if entry already exists 362 | if ! grep -q "$GITLAB_EMAIL" "$allowed_signers_file"; then 363 | echo "$GITLAB_EMAIL namespaces=\"git\" $ssh_key" > "$allowed_signers_file" 364 | echo "SSH key added to allowed signers file." 365 | else 366 | echo "SSH key already exists in allowed signers file." 367 | fi 368 | else 369 | echo "No GPG key found. Please ensure a GPG key is available." 370 | fi 371 | else 372 | echo "GPG not installed. Skipping GPG key export." 373 | fi 374 | 375 | echo "Git config setup completed." 376 | } 377 | 378 | format_gitconfig_files() { 379 | echo "===========================================================" 380 | echo " Format git config files " 381 | echo "-----------------------------------------------------------" 382 | 383 | local git_config_dir="$HOME/.config/git" 384 | 385 | if [[ -d "$git_config_dir" ]]; then 386 | echo "Formatting all files in $git_config_dir..." 387 | 388 | find "$git_config_dir" -type f -name '*config*' | while read -r file; do 389 | perl -pi -e 's/^\s*\[(.*?)\]/\[$1\]/g; s/^\s*(\w)/ $1/g' "$file" 390 | echo "Formatted $file" 391 | done 392 | 393 | echo "All files in $git_config_dir with 'config' in their name have been formatted." 394 | else 395 | echo "Directory $git_config_dir does not exist." 396 | fi 397 | } 398 | 399 | setup_case_sensitive_volume() { 400 | echo "===========================================================" 401 | echo " Setting up case-sensitive APFS volume " 402 | echo "-----------------------------------------------------------" 403 | 404 | # Find APFS container 405 | local container_id=$(diskutil list | grep "APFS Container Scheme" | awk '{print $NF}') 406 | if [[ -z "$container_id" ]]; then 407 | echo "Error: No APFS container found" 408 | return 1 409 | fi 410 | 411 | echo "Found APFS container: $container_id" 412 | 413 | # Create mount point 414 | mkdir -p "$HOME/Code" 415 | 416 | # Check if Code volume exists 417 | local volume_exists=false 418 | if diskutil apfs list | grep -q "Name:.*Code.*Case-sensitive"; then 419 | echo "Code volume already exists" 420 | volume_exists=true 421 | else 422 | echo "Creating case-sensitive Code volume..." 423 | # Use APFSX type for case-sensitive volume 424 | sudo diskutil apfs addVolume "$container_id" APFSX "Code" || return 1 425 | echo "Volume created successfully" 426 | sleep 2 427 | fi 428 | 429 | # Get Code volume ID 430 | local volume_id=$(diskutil apfs list | grep -B 3 "Name:.*Code.*Case-sensitive" | grep "Volume disk" | awk '{print $3}') 431 | if [[ -z "$volume_id" ]]; then 432 | echo "Error: Code volume ID not found" 433 | return 1 434 | fi 435 | 436 | echo "Found volume ID: $volume_id" 437 | 438 | # Check current mount location 439 | local current_mount=$(mount | grep "$volume_id" | awk '{print $3}') 440 | 441 | # If mounted but not at desired location, unmount first 442 | if [[ -n "$current_mount" && "$current_mount" != "$HOME/Code" ]]; then 443 | echo "Volume currently mounted at $current_mount, preparing to remount..." 444 | sudo diskutil unmount "$volume_id" || { 445 | echo "Warning: Could not unmount volume $volume_id, trying force unmount" 446 | sudo diskutil unmount force "$volume_id" || { 447 | echo "Error: Could not unmount volume $volume_id" 448 | return 1 449 | } 450 | } 451 | fi 452 | 453 | # Mount volume to desired location 454 | if ! mount | grep -q "$HOME/Code"; then 455 | echo "Mounting Code volume to $HOME/Code..." 456 | sudo diskutil mount -mountPoint "$HOME/Code" "$volume_id" || return 1 457 | else 458 | echo "Code volume already mounted at desired location" 459 | fi 460 | 461 | # Verify mount and case sensitivity 462 | echo "Verifying mount and case sensitivity..." 463 | if mount | grep -q "$HOME/Code"; then 464 | ( 465 | cd "$HOME/Code" || return 1 466 | local test_file="test_case_sensitive_$(date +%s)" 467 | local test_file_upper="TEST_CASE_SENSITIVE_$(date +%s)" 468 | touch "$test_file" "$test_file_upper" 469 | if [[ -f "$test_file" && -f "$test_file_upper" ]]; then 470 | echo "Verification successful: Volume mounted and case-sensitive" 471 | rm "$test_file" "$test_file_upper" 472 | else 473 | echo "Warning: Case sensitivity test failed" 474 | return 1 475 | fi 476 | ) 477 | else 478 | echo "Error: Volume mount verification failed" 479 | return 1 480 | fi 481 | 482 | echo "Case-sensitive volume setup completed successfully" 483 | } 484 | 485 | install_font() { 486 | echo "===========================================================" 487 | echo " Install Inconsolata LGC " 488 | echo "-----------------------------------------------------------" 489 | 490 | local font_file="/tmp/InconsolataLGC.zip" 491 | local font_dir="/tmp/InconsolataLGC" 492 | local target_dir="$HOME/Library/Fonts" 493 | 494 | if ls "${target_dir}"/*InconsolataLGCNerdFontMono* 1> /dev/null 2>&1; then 495 | echo "Fonts with 'Inconsolata LGC Nerd Font Mono' already installed, skipping download and installation..." 496 | return 497 | fi 498 | 499 | if [[ -e "${font_file}" ]]; then 500 | echo "Font already downloaded, skipping download..." 501 | else 502 | echo "Downloading Inconsolata LGC..." 503 | curl -L https://github.com/ryanoasis/nerd-fonts/releases/latest/download/InconsolataLGC.zip -o "${font_file}" 504 | echo "Font downloaded successfully to ${font_file}." 505 | fi 506 | 507 | if [[ -d "${font_dir}" ]]; then 508 | echo "Font already unzipped, skipping unzip..." 509 | else 510 | echo "Unzipping font..." 511 | mkdir -p "${font_dir}" 512 | unzip "${font_file}" -d "${font_dir}" 513 | echo "Font unzipped successfully to ${font_dir}." 514 | fi 515 | 516 | echo "Copying Mono font files to ${target_dir}..." 517 | cp "${font_dir}"/*InconsolataLGCNerdFontMono*.ttf "${target_dir}/" 518 | echo "Font files copied successfully to ${target_dir}." 519 | 520 | echo "Cleaning up..." 521 | rm -rf "${font_file}" "${font_dir}" 522 | echo "Installation complete and cleanup done." 523 | } 524 | 525 | install_nodejs() { 526 | echo "===========================================================" 527 | echo " Setting up Node.js Environment " 528 | echo "-----------------------------------------------------------" 529 | 530 | if ! command -v xz >/dev/null 2>&1; then 531 | echo "xz is required for unpacking archives. Installing with Homebrew..." 532 | brew install xz || { 533 | echo "Failed to install xz. Cannot proceed with Node.js installation." 534 | return 1 535 | } 536 | fi 537 | 538 | if command -v proto > /dev/null; then 539 | echo "proto is already installed, skipping..." 540 | else 541 | echo "Installing proto..." 542 | curl -fsSL https://moonrepo.dev/install/proto.sh | bash -s -- --no-profile --yes 543 | 544 | echo "Adding proto to PATH..." 545 | export PATH="$HOME/.proto/bin:$PATH" 546 | fi 547 | 548 | if ! command -v proto >/dev/null 2>&1; then 549 | echo "Error: proto command not available. Node.js installation will be skipped." 550 | return 1 551 | fi 552 | 553 | echo "-----------------------------------------------------------" 554 | echo " * Installing Node.js LTS... " 555 | echo "-----------------------------------------------------------" 556 | proto install node 557 | echo "-----------------------------------------------------------" 558 | echo -n " * Node.js Version: " 559 | proto run node -- --version 560 | echo "-----------------------------------------------------------" 561 | 562 | echo " * Installing pnpm... " 563 | echo "-----------------------------------------------------------" 564 | proto install pnpm 565 | echo -n " * pnpm Version: " 566 | proto run pnpm -- --version 567 | echo "-----------------------------------------------------------" 568 | } 569 | 570 | install_rust() { 571 | echo "===========================================================" 572 | echo " Install Rust " 573 | echo "-----------------------------------------------------------" 574 | 575 | if command -v rustc > /dev/null; then 576 | echo "Rust is already installed, skipping..." 577 | else 578 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 579 | fi 580 | } 581 | 582 | reload_zshrc() { 583 | echo "===========================================================" 584 | echo " Reload Bryan env zshrc " 585 | echo "-----------------------------------------------------------" 586 | 587 | if [[ ! -f "$HOME/.zshrc" ]]; then 588 | echo "No .zshrc file found. Exiting." 589 | return 1 590 | fi 591 | 592 | echo "Removing .zcompdump files in $HOME..." 593 | rm -f "$HOME/.zcompdump"* 594 | 595 | echo "Reloading zsh completion system..." 596 | autoload -Uz compinit && compinit -i 597 | } 598 | 599 | setup_macos_defaults() { 600 | echo "===========================================================" 601 | echo " Setting up macOS Defaults... " 602 | echo "-----------------------------------------------------------" 603 | 604 | echo "Setting Dock to auto-hide..." 605 | defaults write com.apple.dock "autohide" -bool "true" 606 | 607 | echo "Setting faster key repeat rate for Vim users..." 608 | # Disable press-and-hold for keys in favor of key repeat 609 | defaults write NSGlobalDomain "ApplePressAndHoldEnabled" -bool "false" 610 | # Set a faster key repeat rate (2 ~ 120, lower value = faster) 611 | defaults write NSGlobalDomain KeyRepeat -int 2 612 | # Set a shorter delay until repeat starts (15 ~ 120, lower value = shorter delay) 613 | defaults write NSGlobalDomain InitialKeyRepeat -int 15 614 | 615 | # Show hidden files in Finder 616 | defaults write com.apple.finder "AppleShowAllFiles" -bool "true" 617 | 618 | # Disable the warning when changing a file extension 619 | defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false 620 | 621 | # Show time in menu bar 622 | defaults write com.apple.menuextra.clock "DateFormat" -string "\"EEE HH:mm:ss\"" 623 | defaults write com.apple.menuextra.clock ShowSeconds -bool false 624 | 625 | echo "Restarting affected applications..." 626 | killall Dock 627 | killall Finder 628 | 629 | echo "macOS defaults have been updated!" 630 | } 631 | 632 | display_todo_list() { 633 | echo "===========================================================" 634 | echo " Done! " 635 | echo " Bryan Environment Setup finished! " 636 | echo "===========================================================" 637 | echo " " 638 | echo " Do not forget to run these things: " 639 | echo " " 640 | echo " - Setup .npmrc " 641 | echo " - Setup launchd for notes " 642 | echo " - https://www.v2ex.com/t/813229?p=1#r_11048555 " 643 | echo " " 644 | echo "===========================================================" 645 | } 646 | 647 | finish() { 648 | cd $HOME 649 | display_todo_list 650 | } 651 | 652 | start 653 | check_install_xcode_tools 654 | install_homebrew 655 | restore_dotfiles 656 | install_homebrew_packages 657 | setup_zsh_plugins 658 | setup_gpg_agent 659 | setup_gitconfig 660 | format_gitconfig_files 661 | setup_case_sensitive_volume 662 | install_font 663 | install_nodejs 664 | install_rust 665 | reload_zshrc 666 | setup_macos_defaults 667 | finish 668 | --------------------------------------------------------------------------------