@sebastienrousseau/dotfiles 0.2.520 → 0.2.521

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE-APACHE +190 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +43 -37
  5. package/install.sh +76 -10
  6. package/package.json +7 -7
  7. package/tools/README.md +49 -0
  8. package/tools/ci/install-chezmoi-verified.sh +68 -0
  9. package/docs/.vitepress/reports/localization-readability-audit.md +0 -73
  10. package/docs/AI.md +0 -179
  11. package/docs/ARCHITECTURE.md +0 -117
  12. package/docs/CNAME +0 -1
  13. package/docs/CONFIG_STRATEGY.md +0 -124
  14. package/docs/COPYRIGHT +0 -7
  15. package/docs/ECOSYSTEM.md +0 -220
  16. package/docs/GOLD-STANDARD-AUDIT.md +0 -352
  17. package/docs/GOVERNANCE.md +0 -98
  18. package/docs/MAINTAINERS.md +0 -41
  19. package/docs/MINIMUM-TOOLCHAIN.md +0 -100
  20. package/docs/NAMING_CONVENTIONS.md +0 -102
  21. package/docs/OPENCODE.md +0 -127
  22. package/docs/README.md +0 -84
  23. package/docs/STRUCTURE.md +0 -102
  24. package/docs/adr/ADR-001-ci-cd-pipeline.md +0 -118
  25. package/docs/adr/ADR-002-shell-performance.md +0 -130
  26. package/docs/adr/ADR-003-security-first.md +0 -158
  27. package/docs/adr/ADR-004-cli-architecture.md +0 -171
  28. package/docs/adr/ADR-005-chezmoi-choice.md +0 -99
  29. package/docs/adr/ADR-006-shell-selection.md +0 -124
  30. package/docs/adr/ADR-007-multi-shell-parity.md +0 -62
  31. package/docs/adr/ADR-008-alias-system-architecture.md +0 -95
  32. package/docs/adr/ADR-009-wallpaper-driven-theming.md +0 -131
  33. package/docs/adr/ADR-010-starship-transient-prompt.md +0 -144
  34. package/docs/adr/ADR-011-nushell-tier3-keep.md +0 -144
  35. package/docs/adr/ADR-012-ai-fleet-local-proxy.md +0 -79
  36. package/docs/adr/README.md +0 -40
  37. package/docs/architecture/AI_COST_OPTIMIZATION.md +0 -144
  38. package/docs/architecture/ARCHITECTURE.md +0 -20
  39. package/docs/architecture/INTEROP.md +0 -44
  40. package/docs/architecture/REPO_LAYOUT.md +0 -241
  41. package/docs/architecture/WALKTHROUGH.md +0 -86
  42. package/docs/architecture/fleet-deployment.md +0 -77
  43. package/docs/archive/EUXIS_2026_REVIEW.md +0 -127
  44. package/docs/archive/LEGACY_ROADMAP.md +0 -6
  45. package/docs/archive/MILESTONE_v0.2.493.md +0 -47
  46. package/docs/archive/PLAN.md +0 -199
  47. package/docs/archive/REPO_AUDIT.md +0 -31
  48. package/docs/articles/.pages +0 -6
  49. package/docs/articles/2026-07-05-custom-mkdocs-material-dark-theme.md +0 -216
  50. package/docs/articles/2026-07-05-fish-startup-abbr.md +0 -153
  51. package/docs/articles/2026-07-05-master-to-main-rename-runbook.md +0 -128
  52. package/docs/articles/index.md +0 -36
  53. package/docs/guides/INSTALL.md +0 -144
  54. package/docs/guides/MACOS_ICLOUD_SYMLINKS.md +0 -121
  55. package/docs/guides/NEOVIM_IDE_GUIDE.md +0 -61
  56. package/docs/guides/THEMING.md +0 -230
  57. package/docs/guides/TROUBLESHOOTING.md +0 -176
  58. package/docs/guides/WSL2_NIX_TROUBLESHOOTING.md +0 -792
  59. package/docs/index.md +0 -132
  60. package/docs/interop/A2A.md +0 -39
  61. package/docs/interop/POWERSHELL.md +0 -102
  62. package/docs/manual/00-introduction.md +0 -89
  63. package/docs/manual/01-concepts/01-architecture.md +0 -138
  64. package/docs/manual/01-concepts/02-trust-model.md +0 -183
  65. package/docs/manual/01-concepts/03-theme-engine.md +0 -186
  66. package/docs/manual/01-concepts/04-fleet.md +0 -148
  67. package/docs/manual/01-concepts/05-self-healing.md +0 -204
  68. package/docs/manual/02-tutorials/01-first-install.md +0 -197
  69. package/docs/manual/02-tutorials/02-add-wallpaper.md +0 -216
  70. package/docs/manual/02-tutorials/03-create-profile.md +0 -244
  71. package/docs/manual/02-tutorials/04-encrypt-secret.md +0 -281
  72. package/docs/manual/02-tutorials/05-deploy-fleet.md +0 -283
  73. package/docs/manual/03-reference/01-dot-cli.md +0 -475
  74. package/docs/manual/03-reference/02-config-files.md +0 -265
  75. package/docs/manual/03-reference/03-environment.md +0 -124
  76. package/docs/manual/03-reference/04-templates.md +0 -190
  77. package/docs/manual/03-reference/05-feature-flags.md +0 -187
  78. package/docs/manual/04-cookbook/01-recipes.md +0 -285
  79. package/docs/manual/04-cookbook/02-troubleshooting.md +0 -351
  80. package/docs/manual/04-cookbook/03-faq.md +0 -175
  81. package/docs/manual/05-appendices/A-platform-matrix.md +0 -101
  82. package/docs/manual/05-appendices/B-security-checklist.md +0 -85
  83. package/docs/manual/05-appendices/C-glossary.md +0 -40
  84. package/docs/manual/05-appendices/D-bibliography.md +0 -58
  85. package/docs/manual/05-appendices/E-license.md +0 -38
  86. package/docs/manual/_toc.yml +0 -58
  87. package/docs/manual/command-index.md +0 -175
  88. package/docs/manual/concept-index.md +0 -170
  89. package/docs/manual/index.md +0 -66
  90. package/docs/migration/README.md +0 -81
  91. package/docs/migration/from-bare-git-repo.md +0 -156
  92. package/docs/migration/from-gnu-stow.md +0 -165
  93. package/docs/migration/from-plain-chezmoi.md +0 -148
  94. package/docs/migration/from-yadm.md +0 -187
  95. package/docs/operations/ARCHITECTURE_ROADMAP.md +0 -7
  96. package/docs/operations/ATTESTATION.md +0 -44
  97. package/docs/operations/CI_CADENCE.md +0 -107
  98. package/docs/operations/CI_COMPOSITES.md +0 -156
  99. package/docs/operations/COMPLETIONS.md +0 -123
  100. package/docs/operations/COVERAGE.md +0 -204
  101. package/docs/operations/DRIFT.md +0 -107
  102. package/docs/operations/HARD_AUDIT_2026.md +0 -631
  103. package/docs/operations/MAINTENANCE.md +0 -63
  104. package/docs/operations/MANIFEST.md +0 -127
  105. package/docs/operations/MIGRATION.md +0 -109
  106. package/docs/operations/OPERATIONS.md +0 -188
  107. package/docs/operations/PERFORMANCE.md +0 -133
  108. package/docs/operations/PERFORMANCE_BUDGETS.md +0 -196
  109. package/docs/operations/REGISTRY.md +0 -90
  110. package/docs/operations/RELEASE_PIPELINE.md +0 -128
  111. package/docs/operations/RELIABILITY.md +0 -122
  112. package/docs/operations/RFC_v0_2_503_reorganization.md +0 -280
  113. package/docs/operations/ROADMAP.md +0 -10
  114. package/docs/operations/ROADMAP_2026.md +0 -7
  115. package/docs/operations/ROADMAP_V0_2_503.md +0 -10
  116. package/docs/operations/TESTING.md +0 -216
  117. package/docs/operations/TRACEABILITY.md +0 -44
  118. package/docs/operations/TRUSTED_AGENT_WORKSTATION.md +0 -65
  119. package/docs/operations/VERSION_SYNC.md +0 -393
  120. package/docs/packaging.md +0 -222
  121. package/docs/reference/ALIASES.md +0 -131
  122. package/docs/reference/ALIASES_CHEATSHEET.md +0 -32
  123. package/docs/reference/ALIASES_DEPRECATIONS.md +0 -13
  124. package/docs/reference/FEATURE-MATRIX.md +0 -646
  125. package/docs/reference/FEATURES.md +0 -66
  126. package/docs/reference/FONTS.md +0 -112
  127. package/docs/reference/POWERSHELL_PARITY.md +0 -82
  128. package/docs/reference/PROFILES.md +0 -69
  129. package/docs/reference/SCREENSHOTS.md +0 -121
  130. package/docs/reference/SCRIPTS.md +0 -71
  131. package/docs/reference/SUPPORT_MATRIX.md +0 -80
  132. package/docs/reference/THEMES.md +0 -117
  133. package/docs/reference/TOOLS.md +0 -110
  134. package/docs/reference/UTILS.md +0 -243
  135. package/docs/registry.json +0 -6
  136. package/docs/schema/dot-env-v1.json +0 -110
  137. package/docs/schema/dot-registry-v1.json +0 -33
  138. package/docs/security/AI_ACT_COMPLIANCE.md +0 -94
  139. package/docs/security/AUDIT_BYPASS.md +0 -103
  140. package/docs/security/AUTOMATION_SECRETS.md +0 -26
  141. package/docs/security/CI_EGRESS_ALLOWLIST.md +0 -127
  142. package/docs/security/CI_PINNING.md +0 -129
  143. package/docs/security/COMMIT_SIGNING.md +0 -138
  144. package/docs/security/COMPLIANCE.md +0 -458
  145. package/docs/security/DEPS_DEV_EXCEPTIONS.md +0 -86
  146. package/docs/security/DISCLOSURE.md +0 -130
  147. package/docs/security/ENCRYPTION.md +0 -57
  148. package/docs/security/FMEA.md +0 -159
  149. package/docs/security/FUZZING.md +0 -209
  150. package/docs/security/HISTORY_FILTERING.md +0 -132
  151. package/docs/security/INCIDENT_RESPONSE.md +0 -579
  152. package/docs/security/INSTALL_VERIFICATION.md +0 -122
  153. package/docs/security/KEYS.md +0 -49
  154. package/docs/security/KEY_ROTATION.md +0 -303
  155. package/docs/security/MCP_POLICY.md +0 -78
  156. package/docs/security/POLICY_RELEASES.md +0 -37
  157. package/docs/security/README.md +0 -28
  158. package/docs/security/SCORECARD.md +0 -195
  159. package/docs/security/SECRETS.md +0 -158
  160. package/docs/security/SECURITY.md +0 -45
  161. package/docs/security/SECURITY_CHECKLIST.md +0 -55
  162. package/docs/security/SHELL_EXEMPTIONS.md +0 -145
  163. package/docs/security/SOUP_REGISTER.md +0 -36
  164. package/docs/security/THREAT_MODEL.md +0 -130
  165. package/docs/security/VERIFICATION_VALIDATION.md +0 -228
  166. package/docs/security/VERIFY_RELEASE.md +0 -201
  167. package/docs/security/security-pubkey.asc +0 -15
  168. package/docs/stylesheets/extra.css +0 -444
  169. package/docs/themes/README.md +0 -10
  170. package/docs/themes/VISUAL_INTEGRITY_REPORT.md +0 -30
  171. package/docs/themes/hero-shot.svg +0 -78
  172. package/scripts/README.md +0 -123
  173. package/scripts/ci/check-copyright-headers.sh +0 -8
  174. package/scripts/ci/check-shell-preamble.sh +0 -8
  175. package/scripts/ci/guard-gitleaks-checkout.sh +0 -8
  176. package/scripts/demo/record.sh +0 -43
  177. package/scripts/diagnostics/a2a-conformance.sh +0 -163
  178. package/scripts/diagnostics/alias-governance.sh +0 -165
  179. package/scripts/diagnostics/aliases-cheatsheet.sh +0 -74
  180. package/scripts/diagnostics/aliases-manifest.sh +0 -77
  181. package/scripts/diagnostics/attest-verify.sh +0 -147
  182. package/scripts/diagnostics/benchmark.sh +0 -408
  183. package/scripts/diagnostics/conflicts.sh +0 -73
  184. package/scripts/diagnostics/doctor-unified.sh +0 -43
  185. package/scripts/diagnostics/doctor.sh +0 -797
  186. package/scripts/diagnostics/drift-dashboard.sh +0 -203
  187. package/scripts/diagnostics/health.sh +0 -656
  188. package/scripts/diagnostics/history-analysis.sh +0 -86
  189. package/scripts/diagnostics/mcp-doctor.sh +0 -582
  190. package/scripts/diagnostics/perf.sh +0 -453
  191. package/scripts/diagnostics/scorecard.sh +0 -120
  192. package/scripts/diagnostics/secret-governance.sh +0 -65
  193. package/scripts/diagnostics/security-score.sh +0 -467
  194. package/scripts/diagnostics/smoke-test.sh +0 -88
  195. package/scripts/diagnostics/snapshot.sh +0 -90
  196. package/scripts/diagnostics/verify.sh +0 -108
  197. package/scripts/diagnostics/verify_state.sh +0 -73
  198. package/scripts/diagnostics/version-locks.sh +0 -94
  199. package/scripts/diagnostics/workstation-attestation.sh +0 -212
  200. package/scripts/dot/commands/agent.sh +0 -535
  201. package/scripts/dot/commands/agents.sh +0 -352
  202. package/scripts/dot/commands/ai.sh +0 -600
  203. package/scripts/dot/commands/aliases.sh +0 -277
  204. package/scripts/dot/commands/appearance.sh +0 -110
  205. package/scripts/dot/commands/completion.sh +0 -171
  206. package/scripts/dot/commands/core.sh +0 -217
  207. package/scripts/dot/commands/diagnostics.sh +0 -265
  208. package/scripts/dot/commands/env-emit.sh +0 -203
  209. package/scripts/dot/commands/fleet.sh +0 -711
  210. package/scripts/dot/commands/init.sh +0 -185
  211. package/scripts/dot/commands/lint.sh +0 -208
  212. package/scripts/dot/commands/manual.sh +0 -169
  213. package/scripts/dot/commands/meta.sh +0 -438
  214. package/scripts/dot/commands/patterns.sh +0 -55
  215. package/scripts/dot/commands/registry.sh +0 -455
  216. package/scripts/dot/commands/restore.sh +0 -232
  217. package/scripts/dot/commands/secrets.sh +0 -296
  218. package/scripts/dot/commands/security.sh +0 -102
  219. package/scripts/dot/commands/tools.sh +0 -570
  220. package/scripts/dot/data/alias-deprecations.tsv +0 -2
  221. package/scripts/dot/powershell/Dot.psm1 +0 -319
  222. package/scripts/fonts/install-nerd-fonts.sh +0 -75
  223. package/scripts/fonts/patch-fonts.sh +0 -36
  224. package/scripts/git-hooks/install.sh +0 -12
  225. package/scripts/git-hooks/pre-commit +0 -12
  226. package/scripts/git-hooks/pre-commit-audit.sh +0 -146
  227. package/scripts/git-hooks/pre-push +0 -105
  228. package/scripts/git-hooks/prepare-commit-msg +0 -29
  229. package/scripts/lib/secrets_provider.sh +0 -200
  230. package/scripts/nvim/headless-upgrade.lua +0 -81
  231. package/scripts/ops/ai-setup.sh +0 -71
  232. package/scripts/ops/bundle.sh +0 -104
  233. package/scripts/ops/chaos.sh +0 -50
  234. package/scripts/ops/chezmoi-apply.sh +0 -333
  235. package/scripts/ops/chezmoi-diff.sh +0 -16
  236. package/scripts/ops/chezmoi-remove.sh +0 -46
  237. package/scripts/ops/chezmoi-update.sh +0 -67
  238. package/scripts/ops/heal-chezmoi.sh +0 -87
  239. package/scripts/ops/heal-system.sh +0 -129
  240. package/scripts/ops/heal-tools.sh +0 -297
  241. package/scripts/ops/heal.sh +0 -223
  242. package/scripts/ops/post-apply-repair.sh +0 -107
  243. package/scripts/ops/prewarm.sh +0 -128
  244. package/scripts/ops/release.sh +0 -262
  245. package/scripts/ops/rollback.sh +0 -613
  246. package/scripts/ops/setup.sh +0 -138
  247. package/scripts/ops/teleport.sh +0 -34
  248. package/scripts/qa/check-feature-matrix.sh +0 -296
  249. package/scripts/qa/check-version-consistency.sh +0 -12
  250. package/scripts/qa/coverage-baseline.sh +0 -61
  251. package/scripts/qa/docs-coverage.sh +0 -118
  252. package/scripts/qa/examples-coverage.sh +0 -94
  253. package/scripts/qa/powershell-contract.ps1 +0 -95
  254. package/scripts/qa/reliability-audit.sh +0 -139
  255. package/scripts/qa/scorecard-snapshot.sh +0 -128
  256. package/scripts/qa/traceability-coverage.sh +0 -124
  257. package/scripts/qa/validate-examples.sh +0 -90
  258. package/scripts/qa/wsl-contract.sh +0 -12
  259. package/scripts/secrets/age-init.sh +0 -82
  260. package/scripts/secrets/create-secrets-file.sh +0 -46
  261. package/scripts/secrets/encrypt-ssh-key.sh +0 -44
  262. package/scripts/security/backup.sh +0 -58
  263. package/scripts/security/check-disclosure-key-expiry.sh +0 -111
  264. package/scripts/security/dns-doh.sh +0 -52
  265. package/scripts/security/encryption-check.sh +0 -55
  266. package/scripts/security/enforce-policies.sh +0 -552
  267. package/scripts/security/firewall.sh +0 -91
  268. package/scripts/security/lock-configs.sh +0 -67
  269. package/scripts/security/lock-screen.sh +0 -56
  270. package/scripts/security/manage-secrets.sh +0 -429
  271. package/scripts/security/ssh-cert.sh +0 -204
  272. package/scripts/security/telemetry-kill.sh +0 -51
  273. package/scripts/security/usb-safety.sh +0 -52
  274. package/scripts/theme/apply-gnome-theme.sh +0 -333
  275. package/scripts/theme/extract-heic-frames.sh +0 -115
  276. package/scripts/theme/extract-theme.py +0 -1020
  277. package/scripts/theme/install-boot-logo.sh +0 -63
  278. package/scripts/theme/install-catppuccin-themes.sh +0 -371
  279. package/scripts/theme/install-cursors.sh +0 -26
  280. package/scripts/theme/install-file-icons.sh +0 -27
  281. package/scripts/theme/install-grub-theme.sh +0 -62
  282. package/scripts/theme/install-lock-icon.sh +0 -31
  283. package/scripts/theme/merge-wallpaper.sh +0 -146
  284. package/scripts/theme/rebuild-themes.sh +0 -603
  285. package/scripts/theme/switch.sh +0 -476
  286. package/scripts/theme/wallpaper-rotate.sh +0 -137
  287. package/scripts/theme/wallpaper-sync.sh +0 -690
  288. package/scripts/tools/cmatrix.sh +0 -22
  289. package/scripts/tools/detect-collisions.py +0 -103
  290. package/scripts/tools/emoji-picker.sh +0 -49
  291. package/scripts/tools/figlet-banner.sh +0 -19
  292. package/scripts/tools/log-rotate.sh +0 -31
  293. package/scripts/tools/lolcat-wrap.sh +0 -20
  294. package/scripts/tools/pipes.sh +0 -49
  295. package/scripts/tuning/linux.sh +0 -186
  296. package/scripts/tuning/macos.sh +0 -56
  297. package/scripts/uninstall.sh +0 -86
  298. package/scripts/verify-release-versions +0 -156
  299. package/scripts/version-sync.sh +0 -714
  300. package/templates/chezmoi-data/geekom-a9.toml.example +0 -21
  301. package/templates/chezmoi-data/mac-m1.toml.example +0 -16
  302. package/templates/chezmoi-data/mac-t2-linux.toml.example +0 -21
  303. package/templates/chezmoi-data/surface-pro-7p.toml.example +0 -21
  304. package/templates/projects/go/.github/workflows/ci.yml +0 -31
  305. package/templates/projects/go/README.md +0 -7
  306. package/templates/projects/go/cmd/__PROJECT_NAME__/main.go +0 -8
  307. package/templates/projects/go/go.mod +0 -3
  308. package/templates/projects/go/go.sum +0 -0
  309. package/templates/projects/molecule/README.md +0 -7
  310. package/templates/projects/molecule/converge.yml +0 -7
  311. package/templates/projects/molecule/molecule.yml +0 -16
  312. package/templates/projects/node/.github/workflows/ci.yml +0 -30
  313. package/templates/projects/node/README.md +0 -7
  314. package/templates/projects/node/package-lock.json +0 -12
  315. package/templates/projects/node/package.json +0 -10
  316. package/templates/projects/node/src/index.js +0 -3
  317. package/templates/projects/packer/README.md +0 -15
  318. package/templates/projects/packer/main.pkr.hcl +0 -15
  319. package/templates/projects/python/.github/workflows/ci.yml +0 -34
  320. package/templates/projects/python/README.md +0 -7
  321. package/templates/projects/python/pyproject.toml +0 -25
  322. package/templates/projects/python/src/__PROJECT_NAME__/__init__.py +0 -2
  323. package/templates/projects/python/tests/test_basic.py +0 -3
@@ -1,153 +0,0 @@
1
- ---
2
- title: "Fish Startup in 2026: Cutting Interactive Shell Latency by Half with abbr on Multi-Shell Dotfiles"
3
- description: Diagnosing and remediating a 232 ms fish cold-start on the .dotfiles multi-shell bridge — one printf format change, one chezmoi hook, half the latency.
4
- date: 2026-07-05
5
- ---
6
-
7
- # Fish Startup in 2026: Cutting Interactive Shell Latency by Half with `abbr` on Multi-Shell Dotfiles
8
-
9
- *Interactive shells have become the primary interface for AI-augmented development; the difference between a 120 ms and a 230 ms first prompt compounds into measurable engineering-hour loss across a global fleet.*
10
-
11
- *Sebastien Rousseau · Published 5 Jul 2026 · 10 min read*
12
-
13
- ## Why Interactive Shell Latency Matters in 2026 #
14
-
15
- The AI-augmented developer opens a terminal dozens of times a day. In fleets running Claude Code, Codex CLI, GitHub Copilot CLI, or agentic frameworks that spawn subshells for every tool call, shell startup latency stops being a personal-comfort metric and becomes a **platform-engineering signal**.
16
-
17
- The [.dotfiles reference framework](https://github.com/sebastienrousseau/dotfiles) treats sub-second shell startup as an SLO alongside SLSA-signed releases and MCP boundary enforcement. When a shell exceeds its budget, the framework's `dot health` command reports it as a failing check, not a warning — because a slow prompt on a workstation running 18 concurrent AI agents is a supply-chain-throughput problem, not an aesthetic one.
18
-
19
- This article walks through the diagnosis and fix that took Fish cold-start latency from **231 ms → 119 ms** — a 48% reduction — on a workstation carrying ~900 bridged bash aliases into Fish for cross-shell parity. The remediation is a one-line change to a code-generation printf statement, plus a chezmoi hook that moves the cost out of the interactive path.
20
-
21
- ## The Multi-Shell Bridge 2026 Architecture Lens #
22
-
23
- Cross-shell parity — the same aliases, functions, environment, and completions across bash, Zsh, Fish, and Nushell — is a distinct architectural property of a mature dotfiles framework. Each layer of that bridge carries its own performance tax:
24
-
25
- | Layer | Design Decision | Why It Matters | Risk if Mishandled |
26
- |---|---|---|---|
27
- | **Source of truth** | Bash-hosted alias library (~900 entries) sourced by `zsh` and `bash` natively | Single canonical location prevents drift; matches how most upstream tooling assumes aliases live | Duplication across shells silently diverges; users on Fish or Nushell get a subtly different alias set |
28
- | **Fish bridge** | `bash --norc --noprofile` subshell dumps `alias -p`, output translated and cached to `~/.cache/fish/bash-aliases.fish` | Fish has no `bash`-sourcing primitive; the bridge is unavoidable | Bridge runs at every shell start unless cached; cache invalidation timing determines the felt cost |
29
- | **Cache format** | `abbr --add NAME -- VALUE` (this article's change) instead of `alias NAME=VALUE` | Fish's `alias` builtin allocates a function per entry (~183 µs each × 900 entries = ~165 ms); `abbr` is a command-line-time expansion at ~40 µs | Choosing `alias` for the cache format silently caps Fish cold-start at ~230 ms even on a warm cache |
30
- | **Cache invalidation** | Compare source mtime + first-line format marker | Alias sources change on every `chezmoi apply`, invalidating the cache and forcing regen on the next shell — the exact moment the user opens a terminal to try their changes | Cache regen on the interactive path punishes the shell that opens right after configuration changes |
31
- | **Pre-warm hook** | `run_onchange_after_` chezmoi hook rebuilds the cache during apply | Moves the ~200 ms regen cost off the user's first prompt into the apply step | Absence of a pre-warm hook makes the first post-apply shell feel broken |
32
-
33
- ## Key Interactive Shell Performance Signals #
34
-
35
- | Signal | Operational Benchmark | Reference | Technical Platform Implementation |
36
- |---|---|---|---|
37
- | **Fish cold-start** | ≤ 200 ms first-prompt latency | Interactive-response threshold (Nielsen 1993, still the industry norm) | `hyperfine --warmup 2 'fish -i -c exit'` in CI; regression fails a PR if the median crosses threshold |
38
- | **Fish warm-start** | ≤ 130 ms after cache is populated | Delta between cold/warm reveals cache-invalidation cost | Same command with `--warmup 3`; the mean tracks the fully-cached shell path |
39
- | **First-post-apply latency** | Warm-shell parity — no cliff after `chezmoi apply` | Signals that regeneration lives outside the shell hot path | `chezmoi apply && hyperfine 'fish -i -c exit'` — cold and warm should be within noise |
40
- | **Cache-format compatibility** | Auto-heal path when upgrading between cache formats | Ensures long-lived workstations don't inherit stale caches on framework upgrade | Staleness check compares first line of cache to expected format marker |
41
- | **Bridge throughput** | ~40 µs per abbreviated entry, ~183 µs per aliased entry | Fish internals — measured, not documented | Choice of `abbr` over `alias` in the cache-emission format string |
42
-
43
- ## Diagnosis: Where the Milliseconds Went #
44
-
45
- `fish --profile-startup=/tmp/f.prof -i -c 'exit'` emits a per-command trace with self-time and cumulative time. Sorted by cumulative time descending, one line dominated the warm-start budget:
46
-
47
- ```
48
- Time (µs) Sum (µs) Command
49
- 1687 132211 ----> source "$_alias_cache"
50
- ```
51
-
52
- **132 ms of a 231 ms budget** — 57% — spent sourcing a single cache file. Everything else (starship prompt initialisation, mise activation, atuin history bindings, direnv hooks) added up cleanly to the remaining ~85 ms.
53
-
54
- The cache file itself was well-formed and cache-invalidation was working correctly. The problem lived at the primitive level: what does `alias name='value'` cost when Fish parses and installs it? Ran in isolation:
55
-
56
- ```
57
- $ hyperfine --warmup 2 "fish -c 'source ~/.cache/fish/bash-aliases.fish'"
58
- Time (mean ± σ): 170.8 ms ± 11.3 ms
59
- ```
60
-
61
- **170 ms just to source 878 alias lines.** Fish's `alias` isn't a shell-level string substitution — it's a function factory. The invocation `alias ll='eza -la --icons'` roughly desugars to:
62
-
63
- ```fish
64
- function ll --wraps='eza -la --icons' --description 'alias ll=eza -la --icons'
65
- eza -la --icons $argv
66
- end
67
- ```
68
-
69
- Every call parses the definition, allocates a function object, installs it in the function table, records the description, and wires the `--wraps` for tab completion. Approximately 183 µs per entry. At 878 entries, the maths is uncompromising: 878 × 183 µs = 161 ms.
70
-
71
- The cost is O(1) per call, but the call count is the problem, and there is no batching path in Fish's `alias` implementation.
72
-
73
- ## Remediation: `abbr --add` as a Cross-Shell-Bridge Primitive #
74
-
75
- Fish exposes two ways to give a short name to a longer command:
76
-
77
- - **`alias`** — function factory. Available in interactive shells, scripts, pipes, subshells, and inside other functions. Costs a function allocation on every source.
78
- - **`abbr --add`** — abbreviation. Expanded at the interactive command line the moment the user types the abbreviation and hits space or enter. Not available in scripts (they need functions). No function allocation on installation.
79
-
80
- For bash-alias bridges, the tradeoff is invisible: users don't call `ll` from inside a Fish script — they'd write a proper Fish function for that use case. Abbreviations for this workload are a strict upgrade: identical interactive UX, no function-table pressure, and — as a side benefit — they show the user what actually runs when they type the abbreviation, which improves shell literacy.
81
-
82
- The code change is a single printf format string in the cache-generator:
83
-
84
- ```diff
85
- - printf "alias %s=%s\n" "$name" "$val"
86
- + printf "abbr --add %s -- %s\n" "$name" "$val"
87
- ```
88
-
89
- Measured in isolation:
90
-
91
- ```
92
- Benchmark 1: fish -c 'source alias-cache.fish'
93
- Time (mean ± σ): 170.8 ms ± 11.3 ms
94
-
95
- Benchmark 2: fish -c 'source abbr-cache.fish'
96
- Time (mean ± σ): 34.0 ms ± 0.9 ms
97
-
98
- Summary
99
- abbr-cache.fish ran 5.02 ± 0.36 times faster than alias-cache.fish
100
- ```
101
-
102
- **5× faster. 137 ms saved on every warm shell start.** End-to-end Fish latency dropped from 231 ms → 119 ms.
103
-
104
- ## The Second-Order Bug: Cache Invalidation Timing #
105
-
106
- Solving the warm case revealed a distinct failure mode: the *first* Fish shell opened after `chezmoi apply` still measured ~306 ms. The apply step writes new versions of the underlying bash alias source files (updated mtimes). The staleness check inside the Fish bridge sees `source.mtime > cache.mtime`, throws the cache away, and rebuilds it — spawning a subshell, sourcing 40 KB of bash, iterating 878 lines. **~200 ms.**
107
-
108
- The shell that pays this cost is whichever one the user opens first, which is almost always the shell they opened *because* they wanted to see the effect of the apply.
109
-
110
- The remediation is architectural, not algorithmic. The regeneration is moved off the interactive path and onto the apply itself via a chezmoi `run_onchange_after_` script:
111
-
112
- ```bash
113
- #!/usr/bin/env bash
114
- # Source-hash retrigger keys — script re-runs when any of these change:
115
- # 90-ux-aliases.sh.tmpl: {{ include "…/90-ux-aliases.sh.tmpl" | sha256sum }}
116
- # 91-ux-aliases-lazy.sh.tmpl: {{ include "…/91-ux-aliases-lazy.sh.tmpl" | sha256sum }}
117
- # aliases.fish.tmpl: {{ include "…/aliases.fish.tmpl" | sha256sum }}
118
-
119
- command -v fish >/dev/null 2>&1 || exit 0
120
-
121
- rm -f "${HOME}/.cache/fish/bash-aliases.fish"
122
- fish -i -c 'exit' >/dev/null 2>&1 || true
123
- ```
124
-
125
- The hook invalidates the cache, spawns a throwaway interactive Fish so the bridge's existing regen path fires, then exits. The user's next actual shell finds a valid, up-to-date cache. The 200 ms cost lands where the user expects "compilation work" to happen — during the apply — not on the terminal they open ten seconds later.
126
-
127
- Cold-start Fish after apply: **306 ms → 120 ms.**
128
-
129
- ## Return on Resilience #
130
-
131
- At a workstation opening 40 shells per day, saving 112 ms per shell reclaims 4.5 seconds daily; 22 minutes annually. At a small engineering org of 50 developers with the same profile, that's 18 engineering-hours reclaimed per year — measurable but modest.
132
-
133
- The stronger case is qualitative. Interactive-latency perception is nonlinear: at ~200 ms the user consciously notices lag; at ~120 ms the shell feels immediate. Once the felt lag is gone, the developer stops flinching before opening a terminal — which changes the frequency and length of exploratory shell work, which changes the shape of what they do at the CLI.
134
-
135
- | Metric | Before | After | Delta |
136
- |---|---|---|---|
137
- | Fish warm-start (median) | 231 ms | 119 ms | −112 ms (−48%) |
138
- | Fish cold-start after apply | 306 ms | 120 ms | −186 ms (−61%) |
139
- | Cache source cost | 170 ms | 34 ms | −136 ms (−80%) |
140
- | CI regression threshold | ✗ 231 > 200 | ✓ 119 ≤ 200 | Now under budget |
141
- | Full test suite | 4703 tests, 0 fail | 4703 tests, 0 fail | Zero regressions |
142
-
143
- ## Takeaways #
144
-
145
- 1. **Profile every interactive shell in CI.** Fish, Zsh, Nushell, Bash — each has its own primitives with different costs. Treat first-prompt latency as a signal with a threshold, not a comfort metric.
146
-
147
- 2. **Prefer `abbr` for interactive-only bridged aliases in Fish.** The distinction between `abbr` (line-time expansion) and `alias` (function factory) is documented; the 5× cost distinction is not. If your users don't call the alias from inside a Fish script — and for bridged bash aliases they don't — `abbr` is a strict upgrade.
148
-
149
- 3. **Move cache regeneration off the interactive path.** Any cache invalidated by a configuration-management action (chezmoi, ansible, dotbot) should be regenerated by that same action, not by whichever shell opens next.
150
-
151
- 4. **Version the cache format itself, not just the source.** The staleness check should include a format marker so upgrading users don't inherit stale caches by mtime luck.
152
-
153
- The reference implementation lives on `main` at [sebastienrousseau/dotfiles](https://github.com/sebastienrousseau/dotfiles); the change landed as [PR #963](https://github.com/sebastienrousseau/dotfiles/pull/963) and [PR #964](https://github.com/sebastienrousseau/dotfiles/pull/964), shipped in [v0.2.510](https://github.com/sebastienrousseau/dotfiles/releases/tag/v0.2.510).
@@ -1,128 +0,0 @@
1
- ---
2
- title: "Renaming master to main in 2026: A Zero-Downtime Runbook"
3
- description: A supply-chain-safe procedure for renaming the default branch of a mature open-source repository — 96 in-repo edits, a grace-period mirror workflow, zero broken install URLs.
4
- date: 2026-07-05
5
- ---
6
-
7
- # Renaming `master` to `main` in 2026: A Zero-Downtime Runbook for a Repository at the Heart of an Open-Source Supply Chain
8
-
9
- *Renaming the default branch of a mature repository is a supply-chain event, not a cosmetic one; done wrong, it strands `curl \| bash` install commands, breaks CI, and severs external distribution channels.*
10
-
11
- *Sebastien Rousseau · Published 5 Jul 2026 · 12 min read*
12
-
13
- ## Why Default-Branch Naming Matters in 2026 #
14
-
15
- Every open-source project with a public install path publishes a URL of the form `raw.githubusercontent.com/<owner>/<repo>/<branch>/install.sh`. Every downstream consumer who has copied that URL — into a README, a Slack message, a devcontainer, a company wiki, a StackOverflow answer, a Homebrew tap, an AUR PKGBUILD, an internal Ansible playbook — has taken an implicit dependency on that branch name. Renaming the branch is a **breaking change to the project's public API surface**, whether the maintainer intended one or not.
16
-
17
- The industry moved off `master` as the default branch name years ago; new repositories default to `main`. Existing repositories, however, live with a naming inconsistency that becomes actively confusing when a maintainer has multiple projects — some on `main`, some still on `master`. The migration is unavoidable, but the operational risk profile is significant enough that most maintainers put it off indefinitely.
18
-
19
- This article documents the migration of the [.dotfiles](https://github.com/sebastienrousseau/dotfiles) repository — 60+ files with hardcoded branch references, four external distribution channels, three CI providers tracking the default branch, and a public install command bookmarked by an unknown number of downstream users. The migration was **zero-downtime**: no install command broke, no CI job failed, no downstream integration degraded. The blueprint is generalisable to any medium-complexity open-source repository.
20
-
21
- ## The Branch-Rename 2026 Architecture Lens #
22
-
23
- A default-branch rename is not a single operation. It's a sequence of coordinated changes across a defined dependency graph, each with its own migration mechanism:
24
-
25
- | Layer | Design Decision | Why It Matters | Risk if Mishandled |
26
- |---|---|---|---|
27
- | **GitHub metadata** | Native rename via Settings → Branches or `POST /repos/{owner}/{repo}/branches/{branch}/rename` | GitHub auto-migrates default-branch setting, open PR targets, branch-protection assignment, ruleset targeting, and Pages source | Renaming via manual `git push :old-name` + `git push new-name` skips the auto-migration and orphans PRs |
28
- | **In-repo workflow triggers** | `.github/workflows/*.yml` `branches:` lists updated pre-rename | Workflows that trigger on `push:` or `pull_request:` targeting the old name silently stop firing after rename | Migration PR itself under-tested — the workflows it edits no longer fire on it |
29
- | **Grace-period mirror** | New workflow fast-forwards `master` from `main` on every push | Preserves `raw.githubusercontent.com/…/master/…` URLs for downstream consumers who cannot be reached | External `curl \| bash` install commands return HTTP 404 the moment `master` ceases to exist |
30
- | **Documentation URIs** | `mkdocs.yml` `edit_uri`, README install commands, docs prose | GitHub redirects `github.com/…/blob/master/…` automatically; `raw.githubusercontent.com/…/master/…` does **not** redirect | Docs site edit buttons point at nonexistent branches; install commands 404 |
31
- | **Rulesets as code** | `.github/rulesets/<branch>.json` file renamed alongside the JSON `target.include` | Machine-readable ruleset files that reference `refs/heads/master` misalign with GitHub's auto-migrated state | Configuration drift between the in-repo policy source of truth and GitHub's live enforcement |
32
- | **Test-suite assertions** | Regression tests that asserted `/master/` URLs in README updated to assert `/main/` | Tests written before rename fail *after* rename in the exact commit that fixes them | Migration PR shows red CI, blocking merge |
33
- | **External distribution** | Homebrew tap, Scoop bucket, AUR PKGBUILD — pinned to release tags, not branches | Version-pinning insulates external distribution from branch renames | Branch-pinned distribution manifests break silently on rename |
34
-
35
- ## Key Branch-Rename Migration Signals #
36
-
37
- | Signal | Operational Benchmark | Reference | Technical Platform Implementation |
38
- |---|---|---|---|
39
- | **URL Reachability Post-Rename** | `HTTP 200` on both `main/install.sh` and `master/install.sh` for the full grace period | External-consumer continuity | Mirror workflow: `on: push: branches: [main]` → `git push origin main:refs/heads/master` |
40
- | **CI Coverage on Migration PR** | Workflow-trigger `branches:` list transitionally includes both `main` and `master` | Migration PR must be tested against the current default | `pull_request: branches: [main, master]` — remove `master` once the mirror retires |
41
- | **In-Repo URL Consistency** | Zero remaining `/master/` URIs on the head branch, excluding intentional mirror-preservation strings | Doc/install correctness | `git grep 'raw.githubusercontent.com/.../master/'` returns empty (or only test-fixture strings) |
42
- | **Ruleset File Alignment** | `.github/rulesets/*.json` filename matches its `target.include` refspec | Config-as-code hygiene | File rename via `git mv` + JSON `target.include` update in the same commit |
43
- | **Grace-Period Retirement Marker** | Calendar-visible tracking item with an explicit sunset date | Operational-debt visibility | GitHub issue with target date in title + calendar event (`.ics` or Google Calendar quick-add URL) |
44
-
45
- ## Diagnosis: Enumerating the Blast Radius #
46
-
47
- Before touching a single file, an authoritative audit surfaces the full inventory of `master` references. On the .dotfiles repository, that surfaced **~88 mechanical replacements across 60 files**, grouped as:
48
-
49
- - **24 GitHub Actions workflow files** — trigger lists, `github.ref_name == 'master'` conditionals, `--base master` PR-creating steps, inline `@SHA # master` comments describing what commit was pinned
50
- - **7 `raw.githubusercontent.com/.../master/…` URLs** — README install command, `install.sh` (referencing itself in its own comment header), `bin/dot-bootstrap`, `docs/index.md`, install guide, MkDocs edit_uri, chezmoi-data JSON `$id` field
51
- - **~25 documentation files** — `github.com/…/blob/master/…` references in operations runbooks, security docs, architecture decision records
52
- - **1 ruleset-as-code file** — `.github/rulesets/master.json` with a `target.include: refs/heads/master` field
53
- - **1 regression test** — asserting the README contains `/master/` (a red-team catch: this test PROTECTS against accidental rename, which now needs its assertion inverted)
54
- - **4 legitimately-kept references** — the `gbd` bulk-branch-delete script with a `main|master` whitelist regex, the `git-primary-branch` shell function's fallback path, the release-branch check in `scripts/ops/release.sh`, and Scorecard-linter fixtures demonstrating `@master` as an anti-pattern
55
-
56
- ## Remediation Sequence #
57
-
58
- The rename is executed as a five-phase sequence, each with a distinct commit or GitHub operation:
59
-
60
- **Phase 1 — Pre-migration content preparation.** A single pull request rewrites all in-repo `master` references while `master` is still the default branch. This PR must merge before any GitHub-side rename. Workflow `pull_request:` triggers gain `[main, master]` (paired) so the PR itself triggers CI against the current default. The ruleset file is renamed via `git mv` and its JSON target updated. The regression test's assertion is inverted from "must contain `/master/`" to "must contain `/main/`".
61
-
62
- **Phase 2 — GitHub-side rename.** Via UI (`Settings → Branches → Rename`) or API (`POST /repos/{owner}/{repo}/branches/master/rename`). GitHub auto-migrates default-branch setting, PR targets, branch-protection assignment, ruleset targeting, Pages source. Blocked if a branch-protection *rule pattern* targeting the new name already exists — delete the empty rule first via GraphQL:
63
-
64
- ```
65
- gh api graphql -f query='
66
- mutation {
67
- deleteBranchProtectionRule(input: { branchProtectionRuleId: "..." }) {
68
- clientMutationId
69
- }
70
- }'
71
- ```
72
-
73
- **Phase 3 — Mirror workflow activation.** Trigger the pre-committed mirror workflow via `workflow_dispatch` or a small push to `main`. The workflow performs a fast-forward `git push origin main:refs/heads/master`, recreating `master` as a passive mirror. From this point forward, every push to `main` mirrors automatically.
74
-
75
- **Phase 4 — Local clone update (per-workstation).** Every developer with an active clone runs:
76
-
77
- ```
78
- git branch -m master main
79
- git fetch origin
80
- git branch -u origin/main main
81
- git remote set-head origin -a
82
- ```
83
-
84
- **Phase 5 — Retirement scheduling.** A GitHub issue with a title-embedded target date (`[2027-07-05] Retire master mirror + tighten workflow triggers to main-only`) plus a calendar reminder ensures the grace-period mirror doesn't become permanent operational debt.
85
-
86
- ## Verification: URLs, CI, Docs Site #
87
-
88
- Post-rename smoke tests:
89
-
90
- ```
91
- $ curl -sI https://raw.githubusercontent.com/sebastienrousseau/dotfiles/main/install.sh | head -1
92
- HTTP/2 200
93
- $ curl -sI https://raw.githubusercontent.com/sebastienrousseau/dotfiles/master/install.sh | head -1
94
- HTTP/2 200
95
- ```
96
-
97
- Both branch names resolve during the grace period. When the mirror is retired in 12 months, `/master/` returns 404 by design — but by then no active install command should still reference it.
98
-
99
- CI status post-rename: on the .dotfiles repository, the migration PR (`#961`) ran **70 checks green, 0 failed**, driven by the `[main, master]` trigger-list transitional configuration. The follow-up test-coverage PR (`#963`) confirmed no downstream test regression.
100
-
101
- Docs site: MkDocs `edit_uri` — updated from `edit/master/docs/` to `edit/main/docs/` — resolves correctly. `github.com/…/blob/master/…` links continue to work via GitHub's built-in redirect; `raw.githubusercontent.com/…/master/…` works via the mirror.
102
-
103
- ## Return on Resilience #
104
-
105
- The commercial value of a supply-chain-safe rename is defensive, not offensive: nothing new is built, but nothing existing breaks.
106
-
107
- | Metric | Result |
108
- |---|---|
109
- | Files updated in migration PR | 97 (96 edits + 1 rename + 1 new workflow) |
110
- | Grace-period external URL uptime | 100% (both `/main/` and `/master/` return HTTP 200) |
111
- | CI checks post-rename | 70 green, 0 failed |
112
- | Downstream distribution channels broken | 0 (Homebrew tap, Scoop bucket, AUR pinned to tags) |
113
- | Local-clone update commands | 4 lines, ~5 seconds per workstation |
114
- | Operational debt introduced | 1 mirror workflow, retired via calendar-scheduled task 2027-07-05 |
115
-
116
- ## Takeaways #
117
-
118
- 1. **Land the in-repo edits before the GitHub-side rename.** Merging Phase 1 while `master` is still default gives you both a working CI baseline and a rehearsal.
119
-
120
- 2. **Add the mirror workflow before renaming, not after.** The interval between the GitHub rename and the mirror's first fast-forward push is the window during which `raw.githubusercontent.com/…/master/…` returns 404. Minimising that window is a matter of ordering.
121
-
122
- 3. **Update `pull_request:` triggers transitionally.** `branches: [main, master]` covers the migration PR itself (which targets the pre-rename default) and every future PR (which will target `main`). The grace-period `master` entry retires with the mirror.
123
-
124
- 4. **Track retirement as an issue with an explicit sunset date.** Mirror workflows are the classic case of "temporary" becoming "permanent". A calendar event and a GitHub issue with a date-anchored title enforce end-of-life.
125
-
126
- 5. **Rulesets-as-code files must be renamed alongside their JSON targets.** GitHub auto-migrates the *live* ruleset assignment; the file in your repo is the source-of-truth if you ever reapply. Both must agree.
127
-
128
- The reference implementation landed as [PR #961](https://github.com/sebastienrousseau/dotfiles/pull/961) with the retirement issue tracked at [#962](https://github.com/sebastienrousseau/dotfiles/issues/962), shipped in [v0.2.510](https://github.com/sebastienrousseau/dotfiles/releases/tag/v0.2.510).
@@ -1,36 +0,0 @@
1
- ---
2
- title: Articles
3
- description: Long-form writing on the design and operation of the .dotfiles framework.
4
- ---
5
-
6
- # Articles
7
-
8
- Long-form writing on the design and operation of the [.dotfiles framework](https://github.com/sebastienrousseau/dotfiles) — deep dives into performance, supply-chain safety, developer experience, and the architectural decisions that shape a mature open-source dotfiles project.
9
-
10
- <div class="grid cards" markdown>
11
-
12
- - :material-console:{ .lg .middle } **[Fish Startup in 2026](2026-07-05-fish-startup-abbr.md)**
13
-
14
- ---
15
-
16
- Cutting interactive shell latency by 48% with `abbr` on multi-shell dotfiles. One printf format change + one chezmoi hook = 112 ms saved per fresh terminal.
17
-
18
- *5 Jul 2026 · 10 min read*
19
-
20
- - :material-source-branch:{ .lg .middle } **[Renaming `master` to `main`](2026-07-05-master-to-main-rename-runbook.md)**
21
-
22
- ---
23
-
24
- A zero-downtime runbook for a repository at the heart of an open-source supply chain. 96 in-repo edits, a 12-month mirror workflow, zero broken install URLs.
25
-
26
- *5 Jul 2026 · 12 min read*
27
-
28
- - :material-palette:{ .lg .middle } **[Custom Documentation Sites in 2026](2026-07-05-custom-mkdocs-material-dark-theme.md)**
29
-
30
- ---
31
-
32
- Building a distinctive dark-themed developer reference on MkDocs Material — three files, no theme forking, WCAG AAA contrast, hash-locked build.
33
-
34
- *5 Jul 2026 · 11 min read*
35
-
36
- </div>
@@ -1,144 +0,0 @@
1
- ---
2
- render_with_liquid: false
3
- ---
4
-
5
- # Install
6
-
7
- Set up on macOS, Linux, or WSL in 3 to 5 minutes.
8
-
9
- **You will need:** `git` and `curl`.
10
-
11
- **Default shell:** Fish. You can change it after you install.
12
-
13
- ## Install
14
-
15
- ```bash
16
- bash -c "$(
17
- curl -fsSL https://raw.githubusercontent.com/sebastienrousseau/dotfiles/main/install.sh
18
- )"
19
- ```
20
-
21
- Then restart your terminal or run `exec $SHELL`.
22
-
23
- ## Verify
24
-
25
- ```bash
26
- dot --version # Confirm installation
27
- dot doctor # Check shell, git, and tools
28
- dot help # Browse available commands
29
- ```
30
-
31
- ## Choose a shell
32
-
33
- Fish is the default shell. To switch to Zsh or Nushell, edit `~/.dotfiles/.chezmoidata.toml`:
34
-
35
- ```toml
36
- default_shell = "zsh"
37
- ```
38
-
39
- Supported values: `zsh`, `fish`, `nushell`.
40
-
41
- Then apply the change:
42
-
43
- ```bash
44
- dot apply
45
- ```
46
-
47
- ## Feature flags
48
-
49
- Turn optional tools on or off after setup. Edit `.chezmoidata.toml`:
50
-
51
- ```toml
52
- [features]
53
- zsh = true # Zsh shell configuration
54
- fish = true # Fish shell configuration
55
- nushell = true # Nushell configuration
56
- nvim = true # Neovim IDE configuration
57
- tmux = true # Terminal multiplexer
58
- nix = true # Nix package manager integration
59
- ```
60
-
61
- ## Update
62
-
63
- ```bash
64
- dot update
65
- ```
66
-
67
- ## Next steps
68
-
69
- 1. Run `dot learn` to take a guided tour.
70
- 2. Add your own tweaks in `~/.config/shell/custom/`.
71
- 3. Read the [Utilities and `dot` CLI](../reference/UTILS.md) reference.
72
-
73
- ---
74
-
75
- ## Advanced
76
-
77
- ### Local source install
78
-
79
- Clone the repo first, then run the installer from your local copy:
80
-
81
- ```bash
82
- git clone https://github.com/sebastienrousseau/dotfiles.git ~/.dotfiles
83
- cd ~/.dotfiles
84
- ./install.sh
85
- ```
86
-
87
- ### Minimal install
88
-
89
- Install only shells and core tools. This skips editor and terminal extras:
90
-
91
- ```bash
92
- ./install.sh --minimal
93
- ```
94
-
95
- ### Non-interactive install
96
-
97
- Use this mode for CI or scripts that run without user input:
98
-
99
- ```bash
100
- DOTFILES_SILENT=1 DOTFILES_NONINTERACTIVE=1 \
101
- bash -c "$(
102
- curl -fsSL https://raw.githubusercontent.com/sebastienrousseau/dotfiles/main/install.sh
103
- )"
104
- ```
105
-
106
- ### Nix
107
-
108
- ```bash
109
- nix develop ~/.dotfiles/nix
110
- nix profile install ~/.dotfiles/nix#dot-utils
111
- ```
112
-
113
- ### Offline bundle
114
-
115
- On a machine with internet access, create a bundle:
116
-
117
- ```bash
118
- dot bundle ~/Downloads
119
- ```
120
-
121
- Copy the archive to the target machine. Then unpack and install:
122
-
123
- ```bash
124
- tar --zstd -xf dotfiles_offline_bundle_*.tar.zst -P
125
- cd ~/.dotfiles
126
- ./install.sh --force
127
- ```
128
-
129
- ### Uninstall
130
-
131
- ```bash
132
- chezmoi purge
133
- rm -rf ~/.dotfiles
134
- rm -rf ~/.local/share/chezmoi ~/.local/share/dotfiles.log
135
- ```
136
-
137
- ### Signed contributions
138
-
139
- All commits must be signed. See [Contributing](https://github.com/sebastienrousseau/dotfiles/blob/main/CONTRIBUTING.md) for details.
140
-
141
- ---
142
-
143
- - [Troubleshooting](TROUBLESHOOTING.md)
144
- - [Support matrix](../reference/SUPPORT_MATRIX.md)
@@ -1,121 +0,0 @@
1
- ---
2
- render_with_liquid: false
3
- ---
4
-
5
- # macOS iCloud Drive Symlinks
6
-
7
- On macOS, this dotfiles setup can (safely) symlink personal directories into iCloud Drive so `~/Desktop`, `~/Documents`, `~/Downloads`, `~/Movies`, `~/Music`, `~/Pictures`, and `~/Public` all live in iCloud and back up automatically.
8
-
9
- The mechanism is `defaults/run_before_macos-icloud-symlinks.sh.tmpl`, which chezmoi runs before **every** `apply` — not once, and not only when the script changes. That is deliberate: a run-once script gets a single attempt, and on a fresh Mac that attempt lands while iCloud Drive is still syncing the folder tree down, so every candidate would skip and the one chance would be spent. Running each time lets it link whatever has become safe to link since the last apply. Repeat runs are no-ops that cost a few milliseconds.
10
-
11
- ## `~/Desktop` and `~/Documents` are a special case
12
-
13
- macOS has its own **"Desktop & Documents Folders"** iCloud feature (System Settings -> Apple Account -> iCloud -> iCloud Drive -> Options). When it is on, macOS itself syncs those two folders, and it marks them in the iCloud container by putting symlinks there pointing *back* at your home directory:
14
-
15
- ```text
16
- com~apple~CloudDocs/Desktop -> ~/Desktop
17
- com~apple~CloudDocs/Documents -> ~/Documents
18
- ```
19
-
20
- Check whether it is on:
21
-
22
- ```bash
23
- defaults read com.apple.finder FXICloudDriveDesktop # 1 = Desktop sync on
24
- defaults read com.apple.finder FXICloudDriveDocuments # 1 = Documents sync on
25
- ```
26
-
27
- **If those return `1`, `~/Desktop` and `~/Documents` are already fully synced to iCloud and across your devices — by macOS, not by this script.** They will show as `SKIP ... iCloud source is itself a symlink` in the log. That is the correct outcome, not a failure.
28
-
29
- Do **not** apply the manual migration recipe below to `~/Desktop` or `~/Documents` while native sync is on: you would be moving data into a symlink that points back at its own source, fighting a feature macOS is already managing. Turn native sync off first if you genuinely want the symlink approach — but for these two folders the native feature is the better mechanism, since Finder, Migration Assistant, and iOS all understand it.
30
-
31
- The symlink approach in this guide is for the folders macOS does *not* cover natively: `~/Downloads`, `~/Movies`, `~/Music`, `~/Pictures`, `~/Public`.
32
-
33
- ## Safety guarantees
34
-
35
- This script is the **replacement** for a previous `symlink_*.tmpl` set that had a data-loss bug (see #1018): chezmoi's built-in symlink handling does `rm -rf $target; ln -s $source $target`, which destroys real content if the target directory has data.
36
-
37
- The current script's contract is:
38
-
39
- | Situation | What the script does |
40
- |---|---|
41
- | `~/$name` is already the correct symlink to iCloud | no-op |
42
- | `~/$name` is a symlink pointing somewhere else | **skip, log** — never overwrite your own link |
43
- | `~/$name` is a directory with any content (including `.DS_Store`) | **skip, log** — never delete your data |
44
- | `~/$name` is an empty directory | `rmdir` (fails safely if non-empty due to a race) + create symlink |
45
- | `~/$name` is a regular file | **skip, log** — never touch |
46
- | `~/$name` does not exist | create symlink directly |
47
- | iCloud source `com~apple~CloudDocs/$name` does not exist | **skip, log** — iCloud may still be syncing |
48
- | iCloud Drive is not set up on the Mac | **skip everything, log** — no partial state |
49
- | iCloud source `com~apple~CloudDocs/$name` is itself a symlink | **skip, log** — macOS's native Desktop & Documents sync owns it (see below) |
50
-
51
- The script has **two kill-switches**:
52
-
53
- - `touch ~/.dotfiles.icloud-skip` — permanent per-machine opt-out
54
- - `DOTFILES_ICLOUD_SYMLINKS=0` — env var, one run only
55
-
56
- And a **dry-run mode**:
57
-
58
- - `DOTFILES_ICLOUD_DRY_RUN=1` — log every decision, take no action
59
-
60
- ## Rescue: how to link a directory that has content
61
-
62
- If one of the non-native folders (`~/Downloads`, `~/Movies`, `~/Music`, `~/Pictures`, `~/Public`) already has content and you want it in iCloud — the example uses `Documents`, which applies only if you have turned native Desktop & Documents sync **off**:
63
-
64
- ```bash
65
- # 1. Move your data into iCloud MANUALLY
66
- mv ~/Documents/* ~/Documents/.* "~/Library/Mobile Documents/com~apple~CloudDocs/Documents/" 2>/dev/null
67
-
68
- # 2. Verify iCloud has everything
69
- ls -la "~/Library/Mobile Documents/com~apple~CloudDocs/Documents/"
70
-
71
- # 3. Now the dir is empty — chezmoi's next apply will do the rmdir + symlink
72
- rmdir ~/Documents # sanity check: this succeeds only if truly empty
73
- chezmoi apply
74
- ```
75
-
76
- ## After the links exist: what changes
77
-
78
- Linking a folder into iCloud does exactly what it says, and that has consequences worth understanding *before* you migrate rather than after.
79
-
80
- **Storage.** Everything saved to a linked folder counts against your iCloud quota, not just local disk. `~/Downloads` is the one that catches people out.
81
-
82
- **Deletion.** Deleting a file from a linked folder deletes it from iCloud, and therefore from every device signed into that account. There is no local-only copy any more. This is inherent to symlinking and is not something the hook can guard.
83
-
84
- **Eviction.** macOS may evict a synced file's contents to reclaim disk space, leaving a placeholder behind. Reading it downloads it again — so a script that walks one of these folders can block on the network where it used to return instantly.
85
-
86
- **Two things in this repository write into these folders.** Neither runs during `chezmoi apply`; both are commands you invoke deliberately:
87
-
88
- | What | Where | Why it matters after linking |
89
- |---|---|---|
90
- | `scripts/theme/merge-wallpaper.sh` | `~/Pictures/Wallpapers` (override with `DOTFILES_WALLPAPER_DIR`) | Merges a light/dark pair into one `.heic` and then **deletes both source files**. After linking, that pair is consumed from iCloud. Point `DOTFILES_WALLPAPER_DIR` at a local directory to keep the library off iCloud. |
91
- | `defaults/dot_config/mpv/mpv.conf` | `~/Pictures/Screenshots` | `screenshot-directory` is set here, so mpv screenshots land in iCloud. |
92
-
93
- Nothing else in this repository writes into the seven folders, and chezmoi itself never touches them: they are listed in the Darwin block of `.chezmoiignore.tmpl`, and `chezmoi managed` reports none of them among the paths it controls.
94
-
95
- ## Log
96
-
97
- Every run appends to `$XDG_STATE_HOME/dotfiles/icloud-symlinks.log` (default: `~/.local/state/dotfiles/icloud-symlinks.log`). Grep for `SKIP` to see why a candidate wasn't linked, or `LINK` to see what was created.
98
-
99
- ## Belt-and-braces protection
100
-
101
- The same directory names are listed in `defaults/.chezmoiignore.tmpl`'s Darwin block, so even if the script never ran, chezmoi's regular `apply` would still refuse to touch these paths on macOS. Two independent layers of protection.
102
-
103
- ## Tests
104
-
105
- Three suites, 52 assertions, all of them run under `/bin/bash` 3.2 as well as bash 5:
106
-
107
- - `tests/unit/misc/test_macos_icloud_symlinks.sh` — 29 assertions covering every branch of the refusal matrix.
108
- - `tests/regression/test_macos_icloud_symlinks_safety.sh` — 12 assertions on the data-loss invariants, checking that named canary files survive.
109
- - `tests/regression/test_macos_icloud_symlinks_manifest.sh` — 11 assertions that hash the *whole* sandbox tree before and after, so a file lost in a path nobody named still shows up. Verified to drop from 11/11 to 4/11 against a deliberately reintroduced #1018.
110
-
111
- `tests/regression/test_gate_integrity.sh` adds 10 more asserting those gates fail when they should. If any of them regress, CI blocks the merge.
112
-
113
- ## Manual override: disable entirely
114
-
115
- If you never want the automation:
116
-
117
- ```bash
118
- touch ~/.dotfiles.icloud-skip
119
- ```
120
-
121
- The script sees this file in the first 5 lines of execution and exits 0 immediately, without touching the filesystem.
@@ -1,61 +0,0 @@
1
- ---
2
- render_with_liquid: false
3
- ---
4
-
5
- # Neovim
6
-
7
- ## Modern Core
8
-
9
- - **Requirement**: Neovim >= 0.11.2. The config checks this at startup.
10
- - **Configuration**: Modular, lazy-loaded configuration based on `lazy.nvim`.
11
- - **Completion**: `blink.cmp` for fast, keystroke-level completion with LSP, snippets, and path sources.
12
- - **UI**: Snacks.nvim dashboard, notifications, status line, buffer line, and file explorer.
13
- - **LSP**: Full LSP support for diagnostics, code actions, and more, managed by `mason.nvim`.
14
-
15
- ## Language Support
16
-
17
- - **Rust**: Out-of-the-box support with `rustaceanvim`.
18
- - **Python**: Full support with `basedpyright`, `ruff`, and `venv-selector`.
19
- - **Web**: Support for web development with `prettier` and `eslint`.
20
-
21
- ## AI Integration
22
-
23
- - **Copilot**: `copilot.lua` for code completion. Run `:Copilot auth` to sign in.
24
- - **Copilot Chat**: AI sidebar for asking questions and getting help (`<leader>cc`).
25
-
26
- ## VS Code Parity
27
-
28
- - **Problem Panel**: `trouble.nvim` for a VS Code-like problems panel (`<leader>xx`).
29
- - **Search & Replace**: `nvim-spectre` for project-wide search and replace (`<leader>S`).
30
- - **Auto-Pairs**: Automatically closes brackets and pairs (via `blink.cmp` auto-brackets).
31
- - **TODOs**: Highlights `TODO` and `FIXME` comments.
32
- - **Session**: Auto-restores your last session.
33
- - **Snippets**: Native `vim.snippet` engine (Neovim 0.11+).
34
-
35
- ## Keybindings
36
-
37
- | Key | Action |
38
- | --- | --- |
39
- | `<Space>ff` | Find Files |
40
- | `<Space>fg` | Grep (Search) text |
41
- | `<Space>fb` | File browser |
42
- | `<Space>fp` | Projects |
43
- | `<Space>e` | Toggle File Explorer |
44
- | `<Space>a` | Add file to Harpoon |
45
- | `<Ctrl>e` | Open Harpoon menu |
46
- | `gd` | Go to Definition |
47
- | `gr` | Go to References |
48
- | `<leader>ca` | Code actions |
49
- | `<leader>cf` | Format code |
50
- | `<leader>cr` | Rename symbol |
51
- | `<leader>tt` | Toggle terminal |
52
- | `<leader>tn` | Test nearest |
53
- | `<leader>tf` | Test file |
54
- | `<leader>ts` | Test suite |
55
- | `<F5>` | Continue (Debug) |
56
- | `<F10>` | Step over (Debug) |
57
- | `<F11>` | Step into (Debug) |
58
- | `<F12>` | Step out (Debug) |
59
- | `<leader>cc` | Toggle Copilot Chat |
60
- | `<leader>xx` | Toggle Diagnostics (Trouble) |
61
- | `<leader>S` | Toggle Spectre (Search/Replace) |