@sebastienrousseau/dotfiles 0.2.520 → 0.2.522

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 +52 -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,455 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: Apache-2.0 OR MIT
3
- # Copyright (c) 2015-2026 Sebastien Rousseau
4
- # shellcheck shell=bash
5
- #
6
- # scripts/dot/commands/registry.sh
7
- #
8
- # `dot registry` — verified module registry for reusable chezmoi sources.
9
- #
10
- # §3 audit roadmap: ship a registry of reusable dotfile modules
11
- # ("rust-dev-setup", "k8s-operator-laptop") to seed network effects.
12
- # Hosted as a GitHub-Pages-indexed JSON file to keep ops cost near
13
- # zero.
14
- #
15
- # Subcommands:
16
- # list Show modules in the configured registry
17
- # search <q> Filter modules by keyword (name, description, tags)
18
- # info <name> Print full metadata for a module
19
- # install <name> Verify and preview a module; --yes applies it.
20
- # url Show the active registry URL
21
- # set-url <u> Override the registry URL (writes to user config)
22
- #
23
- # Registry JSON shape:
24
- # {
25
- # "version": 1,
26
- # "updated": "2026-05-15T16:00:00Z",
27
- # "modules": [
28
- # { "name": "rust-dev-setup",
29
- # "description": "Rust toolchain + cargo plugins + IDE config",
30
- # "repo": "https://github.com/example/rust-dev-setup",
31
- # "tags": ["rust", "dev", "language"],
32
- # "maintainer": "alice@example.com",
33
- # "version": "1.2.0",
34
- # "archive_url": "https://example.com/rust-dev-setup-1.2.0.tar.gz",
35
- # "sha256": "<64 lowercase hexadecimal characters>" }
36
- # ]
37
- # }
38
-
39
- set -euo pipefail
40
-
41
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
42
- # shellcheck source=../../../lib/dot/ui.sh disable=SC1091
43
- source "$SCRIPT_DIR/../../../lib/dot/ui.sh"
44
- # shellcheck source=../../../lib/dot/utils.sh disable=SC1091
45
- source "$SCRIPT_DIR/../../../lib/dot/utils.sh"
46
- # shellcheck source=../../../lib/dot/verified-download.sh disable=SC1091
47
- source "$SCRIPT_DIR/../../../lib/dot/verified-download.sh"
48
-
49
- _registry_default_url() {
50
- printf '%s\n' "https://sebastienrousseau.github.io/dotfiles/registry.json"
51
- }
52
-
53
- _registry_config_file() {
54
- printf '%s/dotfiles/registry.toml\n' "${XDG_CONFIG_HOME:-$HOME/.config}"
55
- }
56
-
57
- _registry_url() {
58
- if [[ -n "${DOTFILES_REGISTRY_URL:-}" ]]; then
59
- printf '%s\n' "$DOTFILES_REGISTRY_URL"
60
- return
61
- fi
62
- local cfg
63
- cfg="$(_registry_config_file)"
64
- if [[ -f "$cfg" ]]; then
65
- local u
66
- u="$(awk -F'[ \t]*=[ \t]*' '/^url[ \t]*=/{gsub(/"/,"",$2); print $2; exit}' "$cfg")"
67
- [[ -n "$u" ]] && {
68
- printf '%s\n' "$u"
69
- return
70
- }
71
- fi
72
- _registry_default_url
73
- }
74
-
75
- _registry_cache_dir() {
76
- printf '%s/dotfiles/registry\n' "${XDG_CACHE_HOME:-$HOME/.cache}"
77
- }
78
-
79
- ## The index cache is keyed by URL, not by one fixed path.
80
- ##
81
- ## It used to live at <cache>/index.json for every registry, and be treated as
82
- ## fresh for six hours: a freshness window says nothing about WHICH registry
83
- ## produced the file, so changing DOTFILES_REGISTRY_URL — or running
84
- ## `dot registry set-url` — kept serving the previous registry's index for up
85
- ## to six hours. Keying by URL also means switching back and forth does not
86
- ## re-download.
87
- ##
88
- ## SHA-256 where a hasher exists, POSIX `cksum` otherwise: this is a cache
89
- ## key, not a security boundary, and every index is schema-validated on read.
90
- _registry_cache_key() {
91
- local url="$1" digest=""
92
- if command -v shasum >/dev/null 2>&1; then
93
- digest="$(printf '%s' "$url" | shasum -a 256 2>/dev/null | awk '{print $1}')"
94
- elif command -v sha256sum >/dev/null 2>&1; then
95
- digest="$(printf '%s' "$url" | sha256sum 2>/dev/null | awk '{print $1}')"
96
- fi
97
- if [[ -z "$digest" ]]; then
98
- digest="$(printf '%s' "$url" | cksum | awk '{print $1 "-" $2}')"
99
- fi
100
- printf '%s\n' "${digest:0:32}"
101
- }
102
-
103
- ## _registry_cache_file [url] — absolute path of the cached index for a URL
104
- ## (the active one by default). Tests seed the cache through this.
105
- _registry_cache_file() {
106
- local url="${1:-}"
107
- [[ -n "$url" ]] || url="$(_registry_url)"
108
- printf '%s/index-%s.json\n' "$(_registry_cache_dir)" "$(_registry_cache_key "$url")"
109
- }
110
-
111
- _registry_data_dir() {
112
- printf '%s/dotfiles/modules\n' "${XDG_DATA_HOME:-$HOME/.local/share}"
113
- }
114
-
115
- _registry_validate_index() {
116
- local index="$1"
117
- jq -e '
118
- .version == 1 and
119
- (.modules | type == "array") and
120
- all(.modules[];
121
- (.name | test("^[a-z0-9][a-z0-9-]{0,31}$")) and
122
- (.version | test("^[0-9]+\\.[0-9]+\\.[0-9]+([+-][0-9A-Za-z.-]+)?$")) and
123
- (.description | type == "string" and length <= 200) and
124
- (.archive_url | test("^(https|file)://")) and
125
- (.sha256 | test("^[0-9a-f]{64}$"))
126
- )
127
- ' "$index" >/dev/null 2>&1
128
- }
129
-
130
- # Prints the path of a validated index file on stdout. Every diagnostic goes
131
- # to stderr: stdout is this function's return channel, and callers read it
132
- # with `index="$(_registry_fetch)"` — a warning printed here would be
133
- # captured into that variable and then handed to jq as a filename.
134
- _registry_fetch() {
135
- local url cache_dir cache_file
136
- url="$(_registry_url)"
137
- [[ "$url" =~ ^(https://|file://) ]] || {
138
- ui_err "registry" "registry URL must use https:// (or file:// for local testing)" >&2
139
- return 1
140
- }
141
- cache_dir="$(_registry_cache_dir)"
142
- cache_file="$(_registry_cache_file "$url")"
143
- mkdir -p "$cache_dir"
144
- # Refresh if older than 6h or missing.
145
- local now mtime
146
- now="$(date +%s)"
147
- if [[ -s "$cache_file" ]]; then
148
- if ! _registry_validate_index "$cache_file"; then
149
- rm -f "$cache_file"
150
- elif mtime="$(stat -c %Y "$cache_file" 2>/dev/null || stat -f %m "$cache_file" 2>/dev/null)"; then
151
- if ((now - mtime < 21600)); then
152
- printf '%s\n' "$cache_file"
153
- return 0
154
- fi
155
- fi
156
- fi
157
- if ! command -v curl >/dev/null 2>&1; then
158
- ui_err "registry" "curl not installed" >&2
159
- return 127
160
- fi
161
- local tmp
162
- tmp="$(mktemp "${cache_file}.XXXXXX")"
163
- # `-fsSL -o file` should be silent, but with some upstreams (e.g.
164
- # GitHub Pages) curl still writes a stray newline to stdout. That
165
- # newline leaks into the caller's `$(_registry_fetch)` and later
166
- # into `jq FILE` as a two-argument invocation
167
- # (`jq \n /path/to/file`), producing a confusing
168
- # "Could not open file" error. Silence stdout explicitly.
169
- local curl_args=(-fsSL --max-time 15)
170
- if [[ "$url" == https://* ]]; then
171
- curl_args+=(--proto '=https' --tlsv1.2)
172
- fi
173
- if ! curl "${curl_args[@]}" -o "$tmp" "$url" >/dev/null; then
174
- rm -f "$tmp"
175
- if [[ -s "$cache_file" ]]; then
176
- ui_warn "registry" "fetch failed; using stale cache at $cache_file" >&2
177
- printf '%s\n' "$cache_file"
178
- return 0
179
- fi
180
- ui_err "registry" "could not fetch $url" >&2
181
- return 1
182
- fi
183
- mv "$tmp" "$cache_file"
184
- if ! _registry_validate_index "$cache_file"; then
185
- rm -f "$cache_file"
186
- ui_err "registry" "index failed schema and integrity validation" >&2
187
- return 1
188
- fi
189
- printf '%s\n' "$cache_file"
190
- }
191
-
192
- _registry_require_jq() {
193
- command -v jq >/dev/null 2>&1 || {
194
- ui_err "registry" "jq is required"
195
- return 127
196
- }
197
- }
198
-
199
- _registry_archive_is_safe() {
200
- local archive="$1"
201
- local entry
202
- while IFS= read -r entry; do
203
- [[ -n "$entry" ]] || continue
204
- if [[ "$entry" == /* || "$entry" == ../* || "$entry" == *"/../"* || "$entry" == *"/.." ]]; then
205
- ui_err "registry" "archive contains unsafe path: $entry"
206
- return 1
207
- fi
208
- done < <(tar -tzf "$archive")
209
- if tar -tvzf "$archive" | awk 'substr($1,1,1) == "l" || substr($1,1,1) == "h" { found=1 } END { exit !found }'; then
210
- ui_err "registry" "archive contains links; links are forbidden in registry modules"
211
- return 1
212
- fi
213
- }
214
-
215
- _registry_install() (
216
- local name="$1"
217
- local apply="${2:-0}"
218
- local index metadata version archive_url expected tmp archive extract module_root actual destination
219
-
220
- [[ "$name" =~ ^[a-z0-9][a-z0-9-]{0,31}$ ]] || {
221
- ui_err "install" "invalid module name: $name"
222
- return 1
223
- }
224
- _registry_require_jq || return $?
225
- index="$(_registry_fetch)" || return $?
226
- _registry_validate_index "$index" || {
227
- ui_err "registry" "index failed schema validation"
228
- return 1
229
- }
230
- metadata="$(jq -c --arg name "$name" '.modules[] | select(.name == $name)' "$index")"
231
- [[ -n "$metadata" ]] || {
232
- ui_err "install" "module not found: $name"
233
- return 1
234
- }
235
- version="$(jq -r '.version' <<<"$metadata")"
236
- archive_url="$(jq -r '.archive_url' <<<"$metadata")"
237
- expected="$(jq -r '.sha256' <<<"$metadata")"
238
-
239
- tmp="$(mktemp -d -t dot-registry.XXXXXX)"
240
- archive="$tmp/module.tar.gz"
241
- extract="$tmp/source"
242
- trap 'rm -rf "$tmp"' EXIT
243
- mkdir -p "$extract"
244
- local curl_args=(-fsSL --max-time 60)
245
- if [[ "$archive_url" == https://* ]]; then
246
- curl_args+=(--proto '=https' --tlsv1.2)
247
- fi
248
- if ! curl "${curl_args[@]}" -o "$archive" "$archive_url"; then
249
- ui_err "install" "could not download $archive_url"
250
- return 1
251
- fi
252
- local archive_size
253
- archive_size="$(wc -c <"$archive" | tr -d '[:space:]')"
254
- if ((archive_size > 52428800)); then
255
- ui_err "install" "archive exceeds the 50 MiB safety limit"
256
- return 1
257
- fi
258
- actual="$(_dot_sha256_file "$archive")" || return $?
259
- [[ "$actual" == "$expected" ]] || {
260
- ui_err "install" "SHA-256 mismatch for $name@$version"
261
- return 1
262
- }
263
- _registry_archive_is_safe "$archive" || return $?
264
- tar -xzf "$archive" -C "$extract" --no-same-owner --no-same-permissions
265
-
266
- module_root="$extract"
267
- local roots=()
268
- while IFS= read -r entry; do roots+=("$entry"); done < <(find "$extract" -mindepth 1 -maxdepth 1 -print)
269
- if [[ ${#roots[@]} -eq 1 && -d "${roots[0]}" ]]; then
270
- module_root="${roots[0]}"
271
- fi
272
- [[ -n "$(find "$module_root" -mindepth 1 -print -quit)" ]] || {
273
- ui_err "install" "module archive is empty"
274
- return 1
275
- }
276
-
277
- ui_ok "Verified" "$name@$version ($actual)"
278
- ui_section "Chezmoi preview"
279
- chezmoi apply --source "$module_root" --destination "$HOME" --dry-run --no-tty
280
- if [[ "$apply" != "1" ]]; then
281
- ui_info "Preview only" "rerun with --yes to install and apply"
282
- return 0
283
- fi
284
-
285
- destination="$(_registry_data_dir)/$name/$version"
286
- mkdir -p "$(dirname "$destination")"
287
- rm -rf "$destination"
288
- mv "$module_root" "$destination"
289
- chezmoi apply --source "$destination" --destination "$HOME" --no-tty
290
- printf '%s\n' "$metadata" >"$(dirname "$destination")/installed.json"
291
- ui_ok "Installed" "$name@$version"
292
- )
293
-
294
- cmd_registry() {
295
- local subcommand="${1:-list}"
296
- shift || true
297
-
298
- case "$subcommand" in
299
- url)
300
- printf '%s\n' "$(_registry_url)"
301
- ;;
302
- set-url)
303
- local new_url="${1:-}"
304
- [[ -n "$new_url" ]] || {
305
- ui_err "set-url" "missing URL"
306
- return 1
307
- }
308
- # Refuse non-HTTPS schemes. The registry index is unsigned today,
309
- # so HTTPS is the only transport that gives us cert-pinned
310
- # integrity. The `file://` exemption is for local testing only
311
- # (the bench-script and unit tests use it).
312
- if [[ ! "$new_url" =~ ^(https://|file://) ]]; then
313
- ui_err "set-url" "registry URL must use https:// (or file:// for local testing) — got: $new_url"
314
- return 1
315
- fi
316
- local cfg
317
- cfg="$(_registry_config_file)"
318
- mkdir -p "$(dirname "$cfg")"
319
- # Atomic write so a concurrent invocation can't read a half-
320
- # written file. Explicit if/else (avoid SC2015 A && B || C).
321
- local _tmp
322
- _tmp="$(mktemp "${cfg}.XXXXXX")"
323
- if printf 'url = "%s"\n' "$new_url" >"$_tmp"; then
324
- if ! mv "$_tmp" "$cfg"; then
325
- rm -f "$_tmp"
326
- ui_err "set-url" "failed to commit $cfg"
327
- return 1
328
- fi
329
- else
330
- rm -f "$_tmp"
331
- ui_err "set-url" "failed to write $cfg"
332
- return 1
333
- fi
334
- ui_ok "registry" "set to $new_url ($cfg)"
335
- ;;
336
- list)
337
- _registry_require_jq || return $?
338
- local index
339
- index="$(_registry_fetch)" || return $?
340
- ui_header "Registry modules"
341
- ui_info "Source" "$(_registry_url)"
342
- echo ""
343
- if ! jq -e '.modules | length > 0' "$index" >/dev/null 2>&1; then
344
- ui_warn "registry" "no modules published yet — see docs/operations/REGISTRY.md to contribute one"
345
- return 0
346
- fi
347
- ui_table_begin "Module" "Version" "Description"
348
- while IFS=$'\t' read -r name ver desc; do
349
- ui_table_add "$name" "v$ver" "$desc"
350
- done < <(jq -r '.modules[] | "\(.name)\t\(.version // "-")\t\(.description // "")"' "$index")
351
- ui_table_end
352
- ;;
353
- search)
354
- _registry_require_jq || return $?
355
- local q="${1:-}"
356
- [[ -n "$q" ]] || {
357
- ui_err "search" "missing query"
358
- return 1
359
- }
360
- local index
361
- index="$(_registry_fetch)" || return $?
362
- ui_header "Registry search: $q"
363
- echo ""
364
- ui_table_begin "Module" "Version" "Description"
365
- while IFS=$'\t' read -r name ver desc; do
366
- ui_table_add "$name" "v$ver" "$desc"
367
- done < <(jq -r --arg q "$q" '
368
- .modules[]
369
- | select(
370
- (.name // "" | ascii_downcase | contains($q | ascii_downcase)) or
371
- (.description // "" | ascii_downcase | contains($q | ascii_downcase)) or
372
- ((.tags // []) | map(ascii_downcase) | index($q | ascii_downcase))
373
- )
374
- | "\(.name)\t\(.version // "-")\t\(.description // "")"
375
- ' "$index")
376
- ui_table_end
377
- ;;
378
- info)
379
- _registry_require_jq || return $?
380
- local name="${1:-}"
381
- [[ -n "$name" ]] || {
382
- ui_err "info" "missing module name"
383
- return 1
384
- }
385
- local index
386
- index="$(_registry_fetch)" || return $?
387
- local found
388
- found="$(jq -r --arg n "$name" '.modules[] | select(.name == $n) | "OK"' "$index" 2>/dev/null)"
389
- if [[ "$found" != "OK" ]]; then
390
- ui_err "info" "module not found: $name"
391
- return 1
392
- fi
393
- jq -r --arg n "$name" '.modules[] | select(.name == $n) | to_entries[] | "\(.key)\t\(.value | if type == "array" then join(", ") else tostring end)"' "$index" |
394
- while IFS=$'\t' read -r key value; do
395
- ui_ok "$key" "$value"
396
- done
397
- ;;
398
- install)
399
- local name="${1:-}"
400
- [[ -n "$name" ]] || {
401
- ui_err "install" "missing module name"
402
- return 1
403
- }
404
- shift || true
405
- local apply=0
406
- case "${1:-}" in
407
- "") ;;
408
- --yes | -y) apply=1 ;;
409
- --dry-run | -n) apply=0 ;;
410
- *)
411
- ui_err "install" "unknown option: $1"
412
- return 2
413
- ;;
414
- esac
415
- _registry_install "$name" "$apply"
416
- ;;
417
- installed)
418
- _registry_require_jq || return $?
419
- local modules_dir
420
- modules_dir="$(_registry_data_dir)"
421
- if [[ ! -d "$modules_dir" ]]; then
422
- ui_info "registry" "no modules installed"
423
- return 0
424
- fi
425
- find "$modules_dir" -name installed.json -type f -exec jq -r '"\(.name)\t\(.version)\t\(.description)"' {} \; |
426
- while IFS=$'\t' read -r module version description; do
427
- ui_ok "$module" "v$version — $description"
428
- done
429
- ;;
430
- --help | -h | help)
431
- cat <<EOF
432
- Usage: dot registry <subcommand>
433
-
434
- Subcommands:
435
- list List modules in the configured registry
436
- search <q> Filter modules by keyword (name, description, tags)
437
- info <name> Print metadata for a single module
438
- install <name> Verify and preview a module; pass --yes to apply
439
- installed List locally installed modules
440
- url Show the active registry URL
441
- set-url <url> Override the registry URL (persists to user config)
442
-
443
- Env overrides:
444
- DOTFILES_REGISTRY_URL One-shot override of the registry URL.
445
-
446
- Default registry: $(_registry_default_url)
447
- EOF
448
- ;;
449
- *)
450
- ui_err "Unknown subcommand" "$subcommand"
451
- echo "Run 'dot registry --help' for usage." >&2
452
- return 1
453
- ;;
454
- esac
455
- }
@@ -1,232 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: Apache-2.0 OR MIT
3
- # Copyright (c) 2015-2026 Sebastien Rousseau
4
- # dot restore - Restore dotfiles from backup or previous state
5
- # Usage: dot restore [--list|-l|--latest|-L|<backup-id>]
6
-
7
- set -euo pipefail
8
-
9
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
- # shellcheck source=../../../lib/dot/utils.sh
11
- source "$SCRIPT_DIR/../../../lib/dot/utils.sh"
12
- # shellcheck source=../../../lib/dot/log.sh
13
- source "$SCRIPT_DIR/../../../lib/dot/log.sh"
14
-
15
- DOTFILES_DIR="${DOTFILES_DIR:-$HOME/.dotfiles}"
16
- BACKUP_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/dotfiles/backups"
17
- CHEZMOI_SOURCE="${HOME}/.local/share/chezmoi"
18
-
19
- ui_init
20
-
21
- usage() {
22
- echo "Usage: dot restore [OPTIONS]"
23
- echo ""
24
- echo "Options:"
25
- echo " --list, -l List available backups"
26
- echo " --latest, -L Restore from latest backup"
27
- echo " --git, -g <ref> Restore from git ref (commit, tag, branch)"
28
- echo " --diff, -d <ref> Show diff between current and ref"
29
- echo " --dry-run, -n Show what would be restored"
30
- echo " -h, --help Show this help"
31
- echo ""
32
- echo "Examples:"
33
- echo " dot restore --list"
34
- echo " dot restore --latest"
35
- echo " dot restore --git HEAD~1"
36
- echo " dot restore --git v0.2.470"
37
- }
38
-
39
- list_backups() {
40
- if [[ ! -d "$BACKUP_DIR" ]]; then
41
- log_warn "No backups found at $BACKUP_DIR"
42
- return 1
43
- fi
44
-
45
- ui_header "Available Backups"
46
- echo "─────────────────────────────────────────"
47
-
48
- while IFS= read -r backup; do
49
- echo " $backup"
50
- done < <(list_backup_names)
51
-
52
- echo ""
53
- echo ""
54
- ui_header "Git History (last 10)"
55
- echo "─────────────────────────────────────────"
56
-
57
- if [[ -d "$DOTFILES_DIR/.git" ]]; then
58
- git -C "$DOTFILES_DIR" log --oneline -10
59
- elif [[ -d "$CHEZMOI_SOURCE/.git" ]]; then
60
- git -C "$CHEZMOI_SOURCE" log --oneline -10
61
- fi
62
- }
63
-
64
- restore_from_git() {
65
- local ref="$1"
66
- local dry_run="${2:-false}"
67
- local source_dir
68
-
69
- if [[ -d "$DOTFILES_DIR/.git" ]]; then
70
- source_dir="$DOTFILES_DIR"
71
- elif [[ -d "$CHEZMOI_SOURCE/.git" ]]; then
72
- source_dir="$CHEZMOI_SOURCE"
73
- else
74
- log_error "No git repository found"
75
- return 1
76
- fi
77
-
78
- log_info "Restoring from git ref: $ref"
79
-
80
- if $dry_run; then
81
- log_info "Dry run - showing changes:"
82
- git -C "$source_dir" diff "$ref" --stat
83
- return 0
84
- fi
85
-
86
- # Create backup first
87
- create_backup
88
-
89
- # Restore
90
- git -C "$source_dir" checkout "$ref" -- .
91
- log_success "Restored from $ref"
92
-
93
- # Re-apply chezmoi
94
- if has_command chezmoi; then
95
- log_info "Re-applying chezmoi..."
96
- chezmoi apply
97
- fi
98
- }
99
-
100
- show_diff() {
101
- local ref="$1"
102
- local source_dir
103
-
104
- if [[ -d "$DOTFILES_DIR/.git" ]]; then
105
- source_dir="$DOTFILES_DIR"
106
- elif [[ -d "$CHEZMOI_SOURCE/.git" ]]; then
107
- source_dir="$CHEZMOI_SOURCE"
108
- else
109
- log_error "No git repository found"
110
- return 1
111
- fi
112
-
113
- git -C "$source_dir" diff "$ref"
114
- }
115
-
116
- create_backup() {
117
- local backup_name
118
- backup_name="backup-$(date +%Y%m%d_%H%M%S)"
119
- local backup_path="$BACKUP_DIR/$backup_name"
120
- local rel_path
121
-
122
- mkdir -p "$backup_path"
123
-
124
- log_info "Creating backup: $backup_name"
125
-
126
- # Backup key config files
127
- local files_to_backup=(
128
- "$HOME/.zshrc"
129
- "$HOME/.config/zsh"
130
- "$HOME/.config/nvim"
131
- "$HOME/.config/git"
132
- "$HOME/.gitconfig"
133
- )
134
-
135
- for f in "${files_to_backup[@]}"; do
136
- if [[ -e "$f" ]]; then
137
- rel_path="${f#"$HOME"/}"
138
- mkdir -p "$backup_path/$(dirname "$rel_path")"
139
- cp -r "$f" "$backup_path/$rel_path" 2>/dev/null || true
140
- fi
141
- done
142
-
143
- log_success "Backup created at $backup_path"
144
- }
145
-
146
- restore_latest() {
147
- if [[ ! -d "$BACKUP_DIR" ]]; then
148
- log_error "No backups found"
149
- return 1
150
- fi
151
-
152
- local latest
153
- latest=$(list_backup_names | head -1)
154
-
155
- if [[ -z "$latest" ]]; then
156
- log_error "No backups found"
157
- return 1
158
- fi
159
-
160
- log_info "Restoring from: $latest"
161
-
162
- local backup_path="$BACKUP_DIR/$latest"
163
- local item rel_path target
164
- while IFS= read -r -d '' item; do
165
- rel_path="${item#"$backup_path"/}"
166
- target="$HOME/$rel_path"
167
- mkdir -p "$(dirname "$target")"
168
- cp -r "$item" "$target"
169
- log_success "Restored: $rel_path"
170
- done < <(find "$backup_path" -mindepth 1 -maxdepth 1 -print0)
171
- }
172
-
173
- list_backup_names() {
174
- local backup_path
175
-
176
- shopt -s nullglob
177
- for backup_path in "$BACKUP_DIR"/backup-*; do
178
- [[ -d "$backup_path" ]] || continue
179
- printf '%s\n' "${backup_path##*/}"
180
- done | while IFS= read -r backup_name; do
181
- printf '%s\t%s\n' "$(portable_mtime "$BACKUP_DIR/$backup_name")" "$backup_name"
182
- done | sort -rn | cut -f2-
183
- shopt -u nullglob
184
- }
185
-
186
- portable_mtime() {
187
- if stat -c %Y "$1" >/dev/null 2>&1; then
188
- stat -c %Y "$1"
189
- else
190
- stat -f %m "$1"
191
- fi
192
- }
193
-
194
- # Parse arguments
195
- DRY_RUN=false
196
-
197
- while [[ $# -gt 0 ]]; do
198
- case "$1" in
199
- -l | --list)
200
- list_backups
201
- exit 0
202
- ;;
203
- --latest | -L)
204
- restore_latest
205
- exit 0
206
- ;;
207
- --git | -g)
208
- restore_from_git "$2" "$DRY_RUN"
209
- exit 0
210
- ;;
211
- --diff | -d)
212
- show_diff "$2"
213
- exit 0
214
- ;;
215
- --dry-run | -n)
216
- DRY_RUN=true
217
- shift
218
- ;;
219
- -h | --help)
220
- usage
221
- exit 0
222
- ;;
223
- *)
224
- log_error "Unknown option: $1"
225
- usage
226
- exit 1
227
- ;;
228
- esac
229
- done
230
-
231
- # Default: show usage
232
- usage