@sebastienrousseau/dotfiles 0.2.519 → 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 (306) hide show
  1. package/CHANGELOG.md +200 -0
  2. package/LICENSE-APACHE +190 -0
  3. package/{LICENSE → LICENSE-MIT} +1 -1
  4. package/README.md +1172 -166
  5. package/install.sh +77 -11
  6. package/package.json +8 -8
  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/CNAME +0 -1
  12. package/docs/CONFIG_STRATEGY.md +0 -124
  13. package/docs/COPYRIGHT +0 -7
  14. package/docs/GOVERNANCE.md +0 -98
  15. package/docs/MAINTAINERS.md +0 -41
  16. package/docs/NAMING_CONVENTIONS.md +0 -102
  17. package/docs/OPENCODE.md +0 -127
  18. package/docs/README.md +0 -84
  19. package/docs/STRUCTURE.md +0 -102
  20. package/docs/adr/ADR-001-ci-cd-pipeline.md +0 -118
  21. package/docs/adr/ADR-002-shell-performance.md +0 -130
  22. package/docs/adr/ADR-003-security-first.md +0 -158
  23. package/docs/adr/ADR-004-cli-architecture.md +0 -171
  24. package/docs/adr/ADR-005-chezmoi-choice.md +0 -99
  25. package/docs/adr/ADR-006-shell-selection.md +0 -124
  26. package/docs/adr/ADR-007-multi-shell-parity.md +0 -62
  27. package/docs/adr/ADR-008-alias-system-architecture.md +0 -95
  28. package/docs/adr/ADR-009-wallpaper-driven-theming.md +0 -131
  29. package/docs/adr/ADR-010-starship-transient-prompt.md +0 -144
  30. package/docs/adr/ADR-011-nushell-tier3-keep.md +0 -144
  31. package/docs/adr/ADR-012-ai-fleet-local-proxy.md +0 -79
  32. package/docs/adr/README.md +0 -40
  33. package/docs/architecture/AI_COST_OPTIMIZATION.md +0 -144
  34. package/docs/architecture/ARCHITECTURE.md +0 -117
  35. package/docs/architecture/INTEROP.md +0 -44
  36. package/docs/architecture/REPO_LAYOUT.md +0 -241
  37. package/docs/architecture/WALKTHROUGH.md +0 -86
  38. package/docs/architecture/fleet-deployment.md +0 -77
  39. package/docs/archive/EUXIS_2026_REVIEW.md +0 -127
  40. package/docs/archive/LEGACY_ROADMAP.md +0 -6
  41. package/docs/archive/MILESTONE_v0.2.493.md +0 -47
  42. package/docs/archive/PLAN.md +0 -199
  43. package/docs/archive/REPO_AUDIT.md +0 -31
  44. package/docs/articles/.pages +0 -6
  45. package/docs/articles/2026-07-05-custom-mkdocs-material-dark-theme.md +0 -216
  46. package/docs/articles/2026-07-05-fish-startup-abbr.md +0 -153
  47. package/docs/articles/2026-07-05-master-to-main-rename-runbook.md +0 -128
  48. package/docs/articles/index.md +0 -36
  49. package/docs/guides/INSTALL.md +0 -144
  50. package/docs/guides/NEOVIM_IDE_GUIDE.md +0 -61
  51. package/docs/guides/THEMING.md +0 -230
  52. package/docs/guides/TROUBLESHOOTING.md +0 -176
  53. package/docs/guides/WSL2_NIX_TROUBLESHOOTING.md +0 -792
  54. package/docs/index.md +0 -132
  55. package/docs/interop/A2A.md +0 -39
  56. package/docs/interop/POWERSHELL.md +0 -102
  57. package/docs/manual/00-introduction.md +0 -89
  58. package/docs/manual/01-concepts/01-architecture.md +0 -138
  59. package/docs/manual/01-concepts/02-trust-model.md +0 -183
  60. package/docs/manual/01-concepts/03-theme-engine.md +0 -186
  61. package/docs/manual/01-concepts/04-fleet.md +0 -148
  62. package/docs/manual/01-concepts/05-self-healing.md +0 -204
  63. package/docs/manual/02-tutorials/01-first-install.md +0 -197
  64. package/docs/manual/02-tutorials/02-add-wallpaper.md +0 -216
  65. package/docs/manual/02-tutorials/03-create-profile.md +0 -244
  66. package/docs/manual/02-tutorials/04-encrypt-secret.md +0 -281
  67. package/docs/manual/02-tutorials/05-deploy-fleet.md +0 -283
  68. package/docs/manual/03-reference/01-dot-cli.md +0 -450
  69. package/docs/manual/03-reference/02-config-files.md +0 -265
  70. package/docs/manual/03-reference/03-environment.md +0 -124
  71. package/docs/manual/03-reference/04-templates.md +0 -190
  72. package/docs/manual/03-reference/05-feature-flags.md +0 -187
  73. package/docs/manual/04-cookbook/01-recipes.md +0 -285
  74. package/docs/manual/04-cookbook/02-troubleshooting.md +0 -351
  75. package/docs/manual/04-cookbook/03-faq.md +0 -175
  76. package/docs/manual/05-appendices/A-platform-matrix.md +0 -101
  77. package/docs/manual/05-appendices/B-security-checklist.md +0 -85
  78. package/docs/manual/05-appendices/C-glossary.md +0 -40
  79. package/docs/manual/05-appendices/D-bibliography.md +0 -58
  80. package/docs/manual/05-appendices/E-license.md +0 -38
  81. package/docs/manual/_toc.yml +0 -58
  82. package/docs/manual/command-index.md +0 -155
  83. package/docs/manual/concept-index.md +0 -168
  84. package/docs/manual/index.md +0 -66
  85. package/docs/operations/ARCHITECTURE_ROADMAP.md +0 -7
  86. package/docs/operations/ATTESTATION.md +0 -44
  87. package/docs/operations/CI_CADENCE.md +0 -107
  88. package/docs/operations/CI_COMPOSITES.md +0 -156
  89. package/docs/operations/COMPLETIONS.md +0 -123
  90. package/docs/operations/COVERAGE.md +0 -204
  91. package/docs/operations/DRIFT.md +0 -107
  92. package/docs/operations/HARD_AUDIT_2026.md +0 -631
  93. package/docs/operations/MAINTENANCE.md +0 -63
  94. package/docs/operations/MANIFEST.md +0 -127
  95. package/docs/operations/MIGRATION.md +0 -109
  96. package/docs/operations/OPERATIONS.md +0 -188
  97. package/docs/operations/PERFORMANCE.md +0 -133
  98. package/docs/operations/REGISTRY.md +0 -90
  99. package/docs/operations/RELEASE_PIPELINE.md +0 -128
  100. package/docs/operations/RELIABILITY.md +0 -122
  101. package/docs/operations/RFC_v0_2_503_reorganization.md +0 -280
  102. package/docs/operations/ROADMAP.md +0 -10
  103. package/docs/operations/ROADMAP_2026.md +0 -7
  104. package/docs/operations/ROADMAP_V0_2_503.md +0 -10
  105. package/docs/operations/TESTING.md +0 -216
  106. package/docs/operations/TRACEABILITY.md +0 -43
  107. package/docs/operations/TRUSTED_AGENT_WORKSTATION.md +0 -65
  108. package/docs/operations/VERSION_SYNC.md +0 -393
  109. package/docs/reference/ALIASES.md +0 -131
  110. package/docs/reference/ALIASES_CHEATSHEET.md +0 -32
  111. package/docs/reference/ALIASES_DEPRECATIONS.md +0 -13
  112. package/docs/reference/FEATURES.md +0 -66
  113. package/docs/reference/FONTS.md +0 -112
  114. package/docs/reference/POWERSHELL_PARITY.md +0 -82
  115. package/docs/reference/PROFILES.md +0 -69
  116. package/docs/reference/SCREENSHOTS.md +0 -121
  117. package/docs/reference/SCRIPTS.md +0 -71
  118. package/docs/reference/SUPPORT_MATRIX.md +0 -80
  119. package/docs/reference/THEMES.md +0 -117
  120. package/docs/reference/TOOLS.md +0 -110
  121. package/docs/reference/UTILS.md +0 -242
  122. package/docs/registry.json +0 -6
  123. package/docs/schema/dot-env-v1.json +0 -110
  124. package/docs/schema/dot-registry-v1.json +0 -33
  125. package/docs/security/AI_ACT_COMPLIANCE.md +0 -94
  126. package/docs/security/AUDIT_BYPASS.md +0 -103
  127. package/docs/security/AUTOMATION_SECRETS.md +0 -26
  128. package/docs/security/CI_EGRESS_ALLOWLIST.md +0 -127
  129. package/docs/security/CI_PINNING.md +0 -129
  130. package/docs/security/COMMIT_SIGNING.md +0 -138
  131. package/docs/security/COMPLIANCE.md +0 -458
  132. package/docs/security/DEPS_DEV_EXCEPTIONS.md +0 -86
  133. package/docs/security/DISCLOSURE.md +0 -130
  134. package/docs/security/ENCRYPTION.md +0 -57
  135. package/docs/security/FMEA.md +0 -159
  136. package/docs/security/FUZZING.md +0 -114
  137. package/docs/security/HISTORY_FILTERING.md +0 -132
  138. package/docs/security/INCIDENT_RESPONSE.md +0 -579
  139. package/docs/security/INSTALL_VERIFICATION.md +0 -122
  140. package/docs/security/KEYS.md +0 -49
  141. package/docs/security/KEY_ROTATION.md +0 -303
  142. package/docs/security/MCP_POLICY.md +0 -78
  143. package/docs/security/POLICY_RELEASES.md +0 -37
  144. package/docs/security/README.md +0 -28
  145. package/docs/security/SCORECARD.md +0 -195
  146. package/docs/security/SECRETS.md +0 -158
  147. package/docs/security/SECURITY.md +0 -45
  148. package/docs/security/SECURITY_CHECKLIST.md +0 -55
  149. package/docs/security/SHELL_EXEMPTIONS.md +0 -145
  150. package/docs/security/SOUP_REGISTER.md +0 -36
  151. package/docs/security/THREAT_MODEL.md +0 -130
  152. package/docs/security/VERIFICATION_VALIDATION.md +0 -228
  153. package/docs/security/VERIFY_RELEASE.md +0 -201
  154. package/docs/security/security-pubkey.asc +0 -15
  155. package/docs/stylesheets/extra.css +0 -444
  156. package/docs/themes/README.md +0 -10
  157. package/docs/themes/VISUAL_INTEGRITY_REPORT.md +0 -30
  158. package/docs/themes/hero-shot.svg +0 -78
  159. package/scripts/README.md +0 -123
  160. package/scripts/ci/check-copyright-headers.sh +0 -8
  161. package/scripts/ci/check-shell-preamble.sh +0 -8
  162. package/scripts/ci/guard-gitleaks-checkout.sh +0 -8
  163. package/scripts/demo/record.sh +0 -43
  164. package/scripts/diagnostics/a2a-conformance.sh +0 -163
  165. package/scripts/diagnostics/alias-governance.sh +0 -138
  166. package/scripts/diagnostics/aliases-cheatsheet.sh +0 -74
  167. package/scripts/diagnostics/aliases-manifest.sh +0 -77
  168. package/scripts/diagnostics/benchmark.sh +0 -408
  169. package/scripts/diagnostics/conflicts.sh +0 -73
  170. package/scripts/diagnostics/doctor-unified.sh +0 -39
  171. package/scripts/diagnostics/doctor.sh +0 -751
  172. package/scripts/diagnostics/drift-dashboard.sh +0 -202
  173. package/scripts/diagnostics/health.sh +0 -623
  174. package/scripts/diagnostics/history-analysis.sh +0 -86
  175. package/scripts/diagnostics/mcp-doctor.sh +0 -582
  176. package/scripts/diagnostics/perf.sh +0 -453
  177. package/scripts/diagnostics/scorecard.sh +0 -119
  178. package/scripts/diagnostics/secret-governance.sh +0 -65
  179. package/scripts/diagnostics/security-score.sh +0 -467
  180. package/scripts/diagnostics/smoke-test.sh +0 -88
  181. package/scripts/diagnostics/snapshot.sh +0 -90
  182. package/scripts/diagnostics/verify.sh +0 -108
  183. package/scripts/diagnostics/verify_state.sh +0 -73
  184. package/scripts/diagnostics/version-locks.sh +0 -94
  185. package/scripts/diagnostics/workstation-attestation.sh +0 -187
  186. package/scripts/dot/commands/agent.sh +0 -485
  187. package/scripts/dot/commands/agents.sh +0 -336
  188. package/scripts/dot/commands/ai.sh +0 -587
  189. package/scripts/dot/commands/aliases.sh +0 -277
  190. package/scripts/dot/commands/appearance.sh +0 -110
  191. package/scripts/dot/commands/completion.sh +0 -134
  192. package/scripts/dot/commands/core.sh +0 -217
  193. package/scripts/dot/commands/diagnostics.sh +0 -265
  194. package/scripts/dot/commands/env-emit.sh +0 -203
  195. package/scripts/dot/commands/fleet.sh +0 -688
  196. package/scripts/dot/commands/init.sh +0 -185
  197. package/scripts/dot/commands/lint.sh +0 -208
  198. package/scripts/dot/commands/manual.sh +0 -169
  199. package/scripts/dot/commands/meta.sh +0 -333
  200. package/scripts/dot/commands/patterns.sh +0 -55
  201. package/scripts/dot/commands/registry.sh +0 -419
  202. package/scripts/dot/commands/restore.sh +0 -232
  203. package/scripts/dot/commands/secrets.sh +0 -296
  204. package/scripts/dot/commands/security.sh +0 -102
  205. package/scripts/dot/commands/tools.sh +0 -556
  206. package/scripts/dot/data/alias-deprecations.tsv +0 -2
  207. package/scripts/dot/powershell/Dot.psm1 +0 -319
  208. package/scripts/fonts/install-nerd-fonts.sh +0 -75
  209. package/scripts/fonts/patch-fonts.sh +0 -36
  210. package/scripts/git-hooks/install.sh +0 -12
  211. package/scripts/git-hooks/pre-commit +0 -12
  212. package/scripts/git-hooks/pre-commit-audit.sh +0 -146
  213. package/scripts/git-hooks/pre-push +0 -105
  214. package/scripts/git-hooks/prepare-commit-msg +0 -29
  215. package/scripts/lib/secrets_provider.sh +0 -185
  216. package/scripts/ops/ai-setup.sh +0 -71
  217. package/scripts/ops/bundle.sh +0 -104
  218. package/scripts/ops/chaos.sh +0 -50
  219. package/scripts/ops/chezmoi-apply.sh +0 -333
  220. package/scripts/ops/chezmoi-diff.sh +0 -16
  221. package/scripts/ops/chezmoi-remove.sh +0 -46
  222. package/scripts/ops/chezmoi-update.sh +0 -63
  223. package/scripts/ops/heal-chezmoi.sh +0 -87
  224. package/scripts/ops/heal-system.sh +0 -129
  225. package/scripts/ops/heal-tools.sh +0 -297
  226. package/scripts/ops/heal.sh +0 -223
  227. package/scripts/ops/post-apply-repair.sh +0 -107
  228. package/scripts/ops/prewarm.sh +0 -128
  229. package/scripts/ops/release.sh +0 -262
  230. package/scripts/ops/rollback.sh +0 -604
  231. package/scripts/ops/setup.sh +0 -138
  232. package/scripts/ops/teleport.sh +0 -34
  233. package/scripts/qa/check-version-consistency.sh +0 -124
  234. package/scripts/qa/coverage-baseline.sh +0 -61
  235. package/scripts/qa/docs-coverage.sh +0 -112
  236. package/scripts/qa/examples-coverage.sh +0 -94
  237. package/scripts/qa/powershell-contract.ps1 +0 -95
  238. package/scripts/qa/reliability-audit.sh +0 -139
  239. package/scripts/qa/scorecard-snapshot.sh +0 -128
  240. package/scripts/qa/traceability-coverage.sh +0 -117
  241. package/scripts/qa/validate-examples.sh +0 -27
  242. package/scripts/qa/wsl-contract.sh +0 -12
  243. package/scripts/secrets/age-init.sh +0 -82
  244. package/scripts/secrets/create-secrets-file.sh +0 -46
  245. package/scripts/secrets/encrypt-ssh-key.sh +0 -44
  246. package/scripts/security/backup.sh +0 -58
  247. package/scripts/security/check-disclosure-key-expiry.sh +0 -111
  248. package/scripts/security/dns-doh.sh +0 -52
  249. package/scripts/security/encryption-check.sh +0 -55
  250. package/scripts/security/enforce-policies.sh +0 -335
  251. package/scripts/security/firewall.sh +0 -91
  252. package/scripts/security/lock-configs.sh +0 -67
  253. package/scripts/security/lock-screen.sh +0 -56
  254. package/scripts/security/manage-secrets.sh +0 -429
  255. package/scripts/security/ssh-cert.sh +0 -204
  256. package/scripts/security/telemetry-kill.sh +0 -51
  257. package/scripts/security/usb-safety.sh +0 -52
  258. package/scripts/theme/apply-gnome-theme.sh +0 -333
  259. package/scripts/theme/extract-heic-frames.sh +0 -115
  260. package/scripts/theme/extract-theme.py +0 -742
  261. package/scripts/theme/install-boot-logo.sh +0 -63
  262. package/scripts/theme/install-catppuccin-themes.sh +0 -371
  263. package/scripts/theme/install-cursors.sh +0 -26
  264. package/scripts/theme/install-file-icons.sh +0 -27
  265. package/scripts/theme/install-grub-theme.sh +0 -62
  266. package/scripts/theme/install-lock-icon.sh +0 -31
  267. package/scripts/theme/merge-wallpaper.sh +0 -146
  268. package/scripts/theme/rebuild-themes.sh +0 -544
  269. package/scripts/theme/switch.sh +0 -449
  270. package/scripts/theme/wallpaper-rotate.sh +0 -137
  271. package/scripts/theme/wallpaper-sync.sh +0 -690
  272. package/scripts/tools/cmatrix.sh +0 -22
  273. package/scripts/tools/detect-collisions.py +0 -103
  274. package/scripts/tools/emoji-picker.sh +0 -49
  275. package/scripts/tools/figlet-banner.sh +0 -19
  276. package/scripts/tools/log-rotate.sh +0 -31
  277. package/scripts/tools/lolcat-wrap.sh +0 -20
  278. package/scripts/tools/pipes.sh +0 -49
  279. package/scripts/tuning/linux.sh +0 -186
  280. package/scripts/tuning/macos.sh +0 -56
  281. package/scripts/uninstall.sh +0 -86
  282. package/scripts/version-sync.sh +0 -654
  283. package/templates/chezmoi-data/geekom-a9.toml.example +0 -21
  284. package/templates/chezmoi-data/mac-m1.toml.example +0 -16
  285. package/templates/chezmoi-data/mac-t2-linux.toml.example +0 -21
  286. package/templates/chezmoi-data/surface-pro-7p.toml.example +0 -21
  287. package/templates/projects/go/.github/workflows/ci.yml +0 -31
  288. package/templates/projects/go/README.md +0 -7
  289. package/templates/projects/go/cmd/__PROJECT_NAME__/main.go +0 -8
  290. package/templates/projects/go/go.mod +0 -3
  291. package/templates/projects/go/go.sum +0 -0
  292. package/templates/projects/molecule/README.md +0 -7
  293. package/templates/projects/molecule/converge.yml +0 -7
  294. package/templates/projects/molecule/molecule.yml +0 -16
  295. package/templates/projects/node/.github/workflows/ci.yml +0 -30
  296. package/templates/projects/node/README.md +0 -7
  297. package/templates/projects/node/package-lock.json +0 -12
  298. package/templates/projects/node/package.json +0 -10
  299. package/templates/projects/node/src/index.js +0 -3
  300. package/templates/projects/packer/README.md +0 -15
  301. package/templates/projects/packer/main.pkr.hcl +0 -15
  302. package/templates/projects/python/.github/workflows/ci.yml +0 -34
  303. package/templates/projects/python/README.md +0 -7
  304. package/templates/projects/python/pyproject.toml +0 -25
  305. package/templates/projects/python/src/__PROJECT_NAME__/__init__.py +0 -2
  306. package/templates/projects/python/tests/test_basic.py +0 -3
@@ -1,419 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: 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
- _registry_data_dir() {
80
- printf '%s/dotfiles/modules\n' "${XDG_DATA_HOME:-$HOME/.local/share}"
81
- }
82
-
83
- _registry_validate_index() {
84
- local index="$1"
85
- jq -e '
86
- .version == 1 and
87
- (.modules | type == "array") and
88
- all(.modules[];
89
- (.name | test("^[a-z0-9][a-z0-9-]{0,31}$")) and
90
- (.version | test("^[0-9]+\\.[0-9]+\\.[0-9]+([+-][0-9A-Za-z.-]+)?$")) and
91
- (.description | type == "string" and length <= 200) and
92
- (.archive_url | test("^(https|file)://")) and
93
- (.sha256 | test("^[0-9a-f]{64}$"))
94
- )
95
- ' "$index" >/dev/null 2>&1
96
- }
97
-
98
- _registry_fetch() {
99
- local url cache_dir cache_file
100
- url="$(_registry_url)"
101
- [[ "$url" =~ ^(https://|file://) ]] || {
102
- ui_err "registry" "registry URL must use https:// (or file:// for local testing)"
103
- return 1
104
- }
105
- cache_dir="$(_registry_cache_dir)"
106
- cache_file="$cache_dir/index.json"
107
- mkdir -p "$cache_dir"
108
- # Refresh if older than 6h or missing.
109
- local now mtime
110
- now="$(date +%s)"
111
- if [[ -s "$cache_file" ]]; then
112
- if ! _registry_validate_index "$cache_file"; then
113
- rm -f "$cache_file"
114
- elif mtime="$(stat -c %Y "$cache_file" 2>/dev/null || stat -f %m "$cache_file" 2>/dev/null)"; then
115
- if ((now - mtime < 21600)); then
116
- printf '%s\n' "$cache_file"
117
- return 0
118
- fi
119
- fi
120
- fi
121
- if ! command -v curl >/dev/null 2>&1; then
122
- ui_err "registry" "curl not installed"
123
- return 127
124
- fi
125
- local tmp
126
- tmp="$(mktemp "${cache_file}.XXXXXX")"
127
- # `-fsSL -o file` should be silent, but with some upstreams (e.g.
128
- # GitHub Pages) curl still writes a stray newline to stdout. That
129
- # newline leaks into the caller's `$(_registry_fetch)` and later
130
- # into `jq FILE` as a two-argument invocation
131
- # (`jq \n /path/to/file`), producing a confusing
132
- # "Could not open file" error. Silence stdout explicitly.
133
- local curl_args=(-fsSL --max-time 15)
134
- if [[ "$url" == https://* ]]; then
135
- curl_args+=(--proto '=https' --tlsv1.2)
136
- fi
137
- if ! curl "${curl_args[@]}" -o "$tmp" "$url" >/dev/null; then
138
- rm -f "$tmp"
139
- if [[ -s "$cache_file" ]]; then
140
- ui_warn "registry" "fetch failed; using stale cache at $cache_file"
141
- printf '%s\n' "$cache_file"
142
- return 0
143
- fi
144
- ui_err "registry" "could not fetch $url"
145
- return 1
146
- fi
147
- mv "$tmp" "$cache_file"
148
- if ! _registry_validate_index "$cache_file"; then
149
- rm -f "$cache_file"
150
- ui_err "registry" "index failed schema and integrity validation"
151
- return 1
152
- fi
153
- printf '%s\n' "$cache_file"
154
- }
155
-
156
- _registry_require_jq() {
157
- command -v jq >/dev/null 2>&1 || {
158
- ui_err "registry" "jq is required"
159
- return 127
160
- }
161
- }
162
-
163
- _registry_archive_is_safe() {
164
- local archive="$1"
165
- local entry
166
- while IFS= read -r entry; do
167
- [[ -n "$entry" ]] || continue
168
- if [[ "$entry" == /* || "$entry" == ../* || "$entry" == *"/../"* || "$entry" == *"/.." ]]; then
169
- ui_err "registry" "archive contains unsafe path: $entry"
170
- return 1
171
- fi
172
- done < <(tar -tzf "$archive")
173
- if tar -tvzf "$archive" | awk 'substr($1,1,1) == "l" || substr($1,1,1) == "h" { found=1 } END { exit !found }'; then
174
- ui_err "registry" "archive contains links; links are forbidden in registry modules"
175
- return 1
176
- fi
177
- }
178
-
179
- _registry_install() (
180
- local name="$1"
181
- local apply="${2:-0}"
182
- local index metadata version archive_url expected tmp archive extract module_root actual destination
183
-
184
- [[ "$name" =~ ^[a-z0-9][a-z0-9-]{0,31}$ ]] || {
185
- ui_err "install" "invalid module name: $name"
186
- return 1
187
- }
188
- _registry_require_jq || return $?
189
- index="$(_registry_fetch)" || return $?
190
- _registry_validate_index "$index" || {
191
- ui_err "registry" "index failed schema validation"
192
- return 1
193
- }
194
- metadata="$(jq -c --arg name "$name" '.modules[] | select(.name == $name)' "$index")"
195
- [[ -n "$metadata" ]] || {
196
- ui_err "install" "module not found: $name"
197
- return 1
198
- }
199
- version="$(jq -r '.version' <<<"$metadata")"
200
- archive_url="$(jq -r '.archive_url' <<<"$metadata")"
201
- expected="$(jq -r '.sha256' <<<"$metadata")"
202
-
203
- tmp="$(mktemp -d -t dot-registry.XXXXXX)"
204
- archive="$tmp/module.tar.gz"
205
- extract="$tmp/source"
206
- trap 'rm -rf "$tmp"' EXIT
207
- mkdir -p "$extract"
208
- local curl_args=(-fsSL --max-time 60)
209
- if [[ "$archive_url" == https://* ]]; then
210
- curl_args+=(--proto '=https' --tlsv1.2)
211
- fi
212
- if ! curl "${curl_args[@]}" -o "$archive" "$archive_url"; then
213
- ui_err "install" "could not download $archive_url"
214
- return 1
215
- fi
216
- local archive_size
217
- archive_size="$(wc -c <"$archive" | tr -d '[:space:]')"
218
- if ((archive_size > 52428800)); then
219
- ui_err "install" "archive exceeds the 50 MiB safety limit"
220
- return 1
221
- fi
222
- actual="$(_dot_sha256_file "$archive")" || return $?
223
- [[ "$actual" == "$expected" ]] || {
224
- ui_err "install" "SHA-256 mismatch for $name@$version"
225
- return 1
226
- }
227
- _registry_archive_is_safe "$archive" || return $?
228
- tar -xzf "$archive" -C "$extract" --no-same-owner --no-same-permissions
229
-
230
- module_root="$extract"
231
- local roots=()
232
- while IFS= read -r entry; do roots+=("$entry"); done < <(find "$extract" -mindepth 1 -maxdepth 1 -print)
233
- if [[ ${#roots[@]} -eq 1 && -d "${roots[0]}" ]]; then
234
- module_root="${roots[0]}"
235
- fi
236
- [[ -n "$(find "$module_root" -mindepth 1 -print -quit)" ]] || {
237
- ui_err "install" "module archive is empty"
238
- return 1
239
- }
240
-
241
- ui_ok "Verified" "$name@$version ($actual)"
242
- ui_section "Chezmoi preview"
243
- chezmoi apply --source "$module_root" --destination "$HOME" --dry-run --no-tty
244
- if [[ "$apply" != "1" ]]; then
245
- ui_info "Preview only" "rerun with --yes to install and apply"
246
- return 0
247
- fi
248
-
249
- destination="$(_registry_data_dir)/$name/$version"
250
- mkdir -p "$(dirname "$destination")"
251
- rm -rf "$destination"
252
- mv "$module_root" "$destination"
253
- chezmoi apply --source "$destination" --destination "$HOME" --no-tty
254
- printf '%s\n' "$metadata" >"$(dirname "$destination")/installed.json"
255
- ui_ok "Installed" "$name@$version"
256
- )
257
-
258
- cmd_registry() {
259
- local subcommand="${1:-list}"
260
- shift || true
261
-
262
- case "$subcommand" in
263
- url)
264
- printf '%s\n' "$(_registry_url)"
265
- ;;
266
- set-url)
267
- local new_url="${1:-}"
268
- [[ -n "$new_url" ]] || {
269
- ui_err "set-url" "missing URL"
270
- return 1
271
- }
272
- # Refuse non-HTTPS schemes. The registry index is unsigned today,
273
- # so HTTPS is the only transport that gives us cert-pinned
274
- # integrity. The `file://` exemption is for local testing only
275
- # (the bench-script and unit tests use it).
276
- if [[ ! "$new_url" =~ ^(https://|file://) ]]; then
277
- ui_err "set-url" "registry URL must use https:// (or file:// for local testing) — got: $new_url"
278
- return 1
279
- fi
280
- local cfg
281
- cfg="$(_registry_config_file)"
282
- mkdir -p "$(dirname "$cfg")"
283
- # Atomic write so a concurrent invocation can't read a half-
284
- # written file. Explicit if/else (avoid SC2015 A && B || C).
285
- local _tmp
286
- _tmp="$(mktemp "${cfg}.XXXXXX")"
287
- if printf 'url = "%s"\n' "$new_url" >"$_tmp"; then
288
- if ! mv "$_tmp" "$cfg"; then
289
- rm -f "$_tmp"
290
- ui_err "set-url" "failed to commit $cfg"
291
- return 1
292
- fi
293
- else
294
- rm -f "$_tmp"
295
- ui_err "set-url" "failed to write $cfg"
296
- return 1
297
- fi
298
- ui_ok "registry" "set to $new_url ($cfg)"
299
- ;;
300
- list)
301
- _registry_require_jq || return $?
302
- local index
303
- index="$(_registry_fetch)" || return $?
304
- ui_header "Registry modules"
305
- ui_info "Source" "$(_registry_url)"
306
- echo ""
307
- if ! jq -e '.modules | length > 0' "$index" >/dev/null 2>&1; then
308
- ui_warn "registry" "no modules published yet — see docs/operations/REGISTRY.md to contribute one"
309
- return 0
310
- fi
311
- ui_table_begin "Module" "Version" "Description"
312
- while IFS=$'\t' read -r name ver desc; do
313
- ui_table_add "$name" "v$ver" "$desc"
314
- done < <(jq -r '.modules[] | "\(.name)\t\(.version // "-")\t\(.description // "")"' "$index")
315
- ui_table_end
316
- ;;
317
- search)
318
- _registry_require_jq || return $?
319
- local q="${1:-}"
320
- [[ -n "$q" ]] || {
321
- ui_err "search" "missing query"
322
- return 1
323
- }
324
- local index
325
- index="$(_registry_fetch)" || return $?
326
- ui_header "Registry search: $q"
327
- echo ""
328
- ui_table_begin "Module" "Version" "Description"
329
- while IFS=$'\t' read -r name ver desc; do
330
- ui_table_add "$name" "v$ver" "$desc"
331
- done < <(jq -r --arg q "$q" '
332
- .modules[]
333
- | select(
334
- (.name // "" | ascii_downcase | contains($q | ascii_downcase)) or
335
- (.description // "" | ascii_downcase | contains($q | ascii_downcase)) or
336
- ((.tags // []) | map(ascii_downcase) | index($q | ascii_downcase))
337
- )
338
- | "\(.name)\t\(.version // "-")\t\(.description // "")"
339
- ' "$index")
340
- ui_table_end
341
- ;;
342
- info)
343
- _registry_require_jq || return $?
344
- local name="${1:-}"
345
- [[ -n "$name" ]] || {
346
- ui_err "info" "missing module name"
347
- return 1
348
- }
349
- local index
350
- index="$(_registry_fetch)" || return $?
351
- local found
352
- found="$(jq -r --arg n "$name" '.modules[] | select(.name == $n) | "OK"' "$index" 2>/dev/null)"
353
- if [[ "$found" != "OK" ]]; then
354
- ui_err "info" "module not found: $name"
355
- return 1
356
- fi
357
- jq -r --arg n "$name" '.modules[] | select(.name == $n) | to_entries[] | "\(.key)\t\(.value | if type == "array" then join(", ") else tostring end)"' "$index" |
358
- while IFS=$'\t' read -r key value; do
359
- ui_ok "$key" "$value"
360
- done
361
- ;;
362
- install)
363
- local name="${1:-}"
364
- [[ -n "$name" ]] || {
365
- ui_err "install" "missing module name"
366
- return 1
367
- }
368
- shift || true
369
- local apply=0
370
- case "${1:-}" in
371
- "") ;;
372
- --yes | -y) apply=1 ;;
373
- --dry-run | -n) apply=0 ;;
374
- *)
375
- ui_err "install" "unknown option: $1"
376
- return 2
377
- ;;
378
- esac
379
- _registry_install "$name" "$apply"
380
- ;;
381
- installed)
382
- _registry_require_jq || return $?
383
- local modules_dir
384
- modules_dir="$(_registry_data_dir)"
385
- if [[ ! -d "$modules_dir" ]]; then
386
- ui_info "registry" "no modules installed"
387
- return 0
388
- fi
389
- find "$modules_dir" -name installed.json -type f -exec jq -r '"\(.name)\t\(.version)\t\(.description)"' {} \; |
390
- while IFS=$'\t' read -r module version description; do
391
- ui_ok "$module" "v$version — $description"
392
- done
393
- ;;
394
- --help | -h | help)
395
- cat <<EOF
396
- Usage: dot registry <subcommand>
397
-
398
- Subcommands:
399
- list List modules in the configured registry
400
- search <q> Filter modules by keyword (name, description, tags)
401
- info <name> Print metadata for a single module
402
- install <name> Verify and preview a module; pass --yes to apply
403
- installed List locally installed modules
404
- url Show the active registry URL
405
- set-url <url> Override the registry URL (persists to user config)
406
-
407
- Env overrides:
408
- DOTFILES_REGISTRY_URL One-shot override of the registry URL.
409
-
410
- Default registry: $(_registry_default_url)
411
- EOF
412
- ;;
413
- *)
414
- ui_err "Unknown subcommand" "$subcommand"
415
- echo "Run 'dot registry --help' for usage." >&2
416
- return 1
417
- ;;
418
- esac
419
- }
@@ -1,232 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: 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