@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,453 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: Apache-2.0 OR MIT
3
- # Copyright (c) 2015-2026 Sebastien Rousseau
4
- # Shell Performance Profiling
5
- # Usage: dot perf [--json|-j] [--profile|-p] [--runs|-r N] [--target|-t MS]
6
-
7
- set -euo pipefail
8
-
9
- _cleanup_files=()
10
- trap 'set +u; rm -f "${_cleanup_files[@]}" 2>/dev/null; set -u' EXIT
11
-
12
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
- # shellcheck source=../../lib/dot/ui.sh
14
- # shellcheck disable=SC1091
15
- source "$SCRIPT_DIR/../../lib/dot/ui.sh"
16
- # shellcheck source=../../lib/dot/log.sh
17
- # shellcheck disable=SC1091
18
- source "$SCRIPT_DIR/../../lib/dot/log.sh"
19
- export DOT_COMMAND="perf"
20
-
21
- ui_init
22
-
23
- if ! command -v python3 >/dev/null 2>&1; then
24
- ui_err "python3" "required for perf timing"
25
- exit 1
26
- fi
27
-
28
- JSON_OUTPUT=false
29
- PROFILE=false
30
- RUNS=3
31
- TARGET_MS="${DOTFILES_PERF_TARGET_MS:-250}"
32
- MAX_MS="${DOTFILES_PERF_MAX_MS:-1000}"
33
- SHELL_FILTER=""
34
- BY_TOOL=false
35
- RESET_TIMINGS=false
36
-
37
- while [[ $# -gt 0 ]]; do
38
- case "$1" in
39
- --json | -j)
40
- JSON_OUTPUT=true
41
- shift
42
- ;;
43
- --profile | -p)
44
- PROFILE=true
45
- shift
46
- ;;
47
- --runs | -r)
48
- RUNS="${2:-3}"
49
- shift 2
50
- ;;
51
- --target | -t)
52
- TARGET_MS="${2:-$TARGET_MS}"
53
- shift 2
54
- ;;
55
- --shell | -s)
56
- SHELL_FILTER="${2:-}"
57
- shift 2
58
- ;;
59
- --by-tool)
60
- BY_TOOL=true
61
- shift
62
- ;;
63
- --reset)
64
- RESET_TIMINGS=true
65
- shift
66
- ;;
67
- --baseline)
68
- # Write the current per-shell means to the baseline file so
69
- # subsequent runs can warn on regression. Closes part of #863.
70
- WRITE_BASELINE=true
71
- shift
72
- ;;
73
- --no-baseline-check)
74
- # Skip the baseline-vs-current comparison entirely. Useful for
75
- # the very first run on a machine, or for short-lived CI.
76
- NO_BASELINE_CHECK=true
77
- shift
78
- ;;
79
- *)
80
- shift
81
- ;;
82
- esac
83
- done
84
-
85
- WRITE_BASELINE="${WRITE_BASELINE:-false}"
86
- NO_BASELINE_CHECK="${NO_BASELINE_CHECK:-false}"
87
- BASELINE_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/dotfiles/perf-baseline.json"
88
- BASELINE_REGRESSION_PCT="${DOTFILES_PERF_REGRESSION_PCT:-10}"
89
-
90
- # --by-tool reader: aggregate $XDG_STATE_HOME/dotfiles/eval-timings.jsonl
91
- # (populated by _cached_eval when EVALCACHE_TIMING=1) and report which
92
- # tools dominate startup time. Independent of the runtime measurement
93
- # loop above, so it returns immediately.
94
- if $BY_TOOL || $RESET_TIMINGS; then
95
- log_dir="${XDG_STATE_HOME:-$HOME/.local/state}/dotfiles"
96
- log_file="$log_dir/eval-timings.jsonl"
97
- if $RESET_TIMINGS; then
98
- : >"$log_file" 2>/dev/null || rm -f "$log_file" 2>/dev/null || true
99
- ui_ok "eval timings" "cleared $log_file"
100
- $BY_TOOL || exit 0
101
- fi
102
- if [[ ! -s "$log_file" ]]; then
103
- ui_warn "eval timings" "no data at $log_file"
104
- echo " Hint: open a new shell with EVALCACHE_TIMING=1 to start collecting." >&2
105
- exit 0
106
- fi
107
- ui_dot_banner "Diagnostics"
108
- ui_header "Per-tool timing breakdown"
109
- ui_section "$log_file"
110
- python3 - "$log_file" <<'PY'
111
- import json, sys
112
- from collections import defaultdict
113
-
114
- def percentile(sorted_vals, p):
115
- """Linear-interpolation percentile, matching numpy.percentile defaults."""
116
- if not sorted_vals:
117
- return 0
118
- if len(sorted_vals) == 1:
119
- return sorted_vals[0]
120
- k = (len(sorted_vals) - 1) * (p / 100.0)
121
- f = int(k)
122
- c = min(f + 1, len(sorted_vals) - 1)
123
- if f == c:
124
- return sorted_vals[f]
125
- return sorted_vals[f] + (sorted_vals[c] - sorted_vals[f]) * (k - f)
126
-
127
- samples = defaultdict(list) # label -> [ms, ms, ...]
128
- shells_by_label = defaultdict(set) # label -> {shell, ...}
129
-
130
- with open(sys.argv[1]) as f:
131
- for line in f:
132
- line = line.strip()
133
- if not line:
134
- continue
135
- try:
136
- ev = json.loads(line)
137
- except Exception:
138
- continue
139
- label = ev.get("label", "?")
140
- try:
141
- ms = int(ev.get("ms", 0) or 0)
142
- except (TypeError, ValueError):
143
- continue
144
- samples[label].append(ms)
145
- shells_by_label[label].add(ev.get("shell", "?"))
146
-
147
- rows = []
148
- for label, vals in samples.items():
149
- vals_sorted = sorted(vals)
150
- rows.append({
151
- "label": label,
152
- "count": len(vals_sorted),
153
- "total": sum(vals_sorted),
154
- "mean": sum(vals_sorted) // max(len(vals_sorted), 1),
155
- "min": vals_sorted[0],
156
- "max": vals_sorted[-1],
157
- "p50": int(percentile(vals_sorted, 50)),
158
- "p95": int(percentile(vals_sorted, 95)),
159
- "p99": int(percentile(vals_sorted, 99)),
160
- "shells": ",".join(sorted(shells_by_label[label])),
161
- })
162
-
163
- rows.sort(key=lambda r: r["total"], reverse=True)
164
- header = (f" {'label':<20} {'calls':>5} {'total':>8} "
165
- f"{'mean':>7} {'p50':>5} {'p95':>5} {'p99':>5} shells")
166
- print(header)
167
- print(" " + "-" * (len(header) - 2))
168
- for r in rows:
169
- print(f" {r['label']:<20} {r['count']:>5} {r['total']:>6}ms "
170
- f"{r['mean']:>5}ms {r['p50']:>3}ms {r['p95']:>3}ms {r['p99']:>3}ms {r['shells']}")
171
- PY
172
- exit 0
173
- fi
174
-
175
- # Per-shell defaults. nu and pwsh are genuinely slower than POSIX shells;
176
- # bash should be quickest. Override via DOTFILES_PERF_TARGET_<SHELL>_MS.
177
- shell_target_for() {
178
- case "$1" in
179
- zsh) echo "${DOTFILES_PERF_TARGET_ZSH_MS:-$TARGET_MS}" ;;
180
- bash) echo "${DOTFILES_PERF_TARGET_BASH_MS:-60}" ;;
181
- fish) echo "${DOTFILES_PERF_TARGET_FISH_MS:-200}" ;;
182
- nu) echo "${DOTFILES_PERF_TARGET_NU_MS:-500}" ;;
183
- pwsh) echo "${DOTFILES_PERF_TARGET_PWSH_MS:-600}" ;;
184
- *) echo "$TARGET_MS" ;;
185
- esac
186
- }
187
-
188
- # Invoke a near-empty session for the named shell, picking flags that
189
- # load the user's interactive profile (matches what a fresh terminal does).
190
- invoke_shell() {
191
- case "$1" in
192
- zsh) zsh -i -c exit </dev/null ;;
193
- bash) bash -i -c exit </dev/null ;;
194
- fish) fish -i -c exit </dev/null ;;
195
- nu) nu -c exit </dev/null ;;
196
- pwsh) pwsh -Command exit </dev/null ;;
197
- *) return 1 ;;
198
- esac
199
- }
200
-
201
- time_one_run() {
202
- local shell_name="$1" start end
203
- start=$(python3 -c 'import time; print(int(time.time() * 1000))')
204
- invoke_shell "$shell_name" >/dev/null 2>&1 || true
205
- end=$(python3 -c 'import time; print(int(time.time() * 1000))')
206
- echo $((end - start))
207
- }
208
-
209
- # Existing single-shell helper retained for backward compatibility with
210
- # callers/tests that reference time_shell_startup; routes to time_one_run.
211
- time_shell_startup() { time_one_run zsh; }
212
-
213
- # Measure a shell across $RUNS iterations, return "mean min max".
214
- measure_shell() {
215
- local shell_name="$1" sum=0 min=999999 max=0 t
216
- local times=()
217
- # One warm-up run discarded — caches a $_SHELL_CACHE on first invocation.
218
- invoke_shell "$shell_name" >/dev/null 2>&1 || true
219
- for _ in $(seq 1 "$RUNS"); do
220
- t=$(time_one_run "$shell_name")
221
- times+=("$t")
222
- sum=$((sum + t))
223
- [[ "$t" -lt "$min" ]] && min="$t"
224
- [[ "$t" -gt "$max" ]] && max="$t"
225
- done
226
- echo "$((sum / RUNS)) $min $max"
227
- }
228
-
229
- calc_score() {
230
- local mean_ms="$1"
231
- if [[ "$mean_ms" -le "$TARGET_MS" ]]; then
232
- echo 100
233
- return
234
- fi
235
- if [[ "$mean_ms" -ge "$MAX_MS" ]]; then
236
- echo 0
237
- return
238
- fi
239
- echo $((100 - (mean_ms - TARGET_MS) * 100 / (MAX_MS - TARGET_MS)))
240
- }
241
-
242
- run_profile() {
243
- zsh -c '
244
- zmodload zsh/zprof
245
- # shellcheck disable=SC1091
246
- source ~/.zshenv 2>/dev/null
247
- # shellcheck disable=SC1091
248
- source ~/.config/zsh/.zshrc 2>/dev/null
249
- zprof
250
- ' 2>/dev/null | head -20
251
- }
252
-
253
- # Discover installed shells. SHELL_FILTER (--shell <name>) restricts to one.
254
- all_shells=(zsh bash fish nu pwsh)
255
- shells_to_measure=()
256
- for s in "${all_shells[@]}"; do
257
- command -v "$s" >/dev/null 2>&1 || continue
258
- if [[ -n "$SHELL_FILTER" && "$s" != "$SHELL_FILTER" ]]; then
259
- continue
260
- fi
261
- shells_to_measure+=("$s")
262
- done
263
- if ((${#shells_to_measure[@]} == 0)); then
264
- ui_err "perf" "no measurable shells found${SHELL_FILTER:+ (filter: $SHELL_FILTER)}"
265
- exit 1
266
- fi
267
-
268
- # Measure each shell once into parallel arrays.
269
- declare -a shell_names shell_means shell_mins shell_maxs shell_targets shell_passes
270
- for s in "${shells_to_measure[@]}"; do
271
- read -r m mn mx <<<"$(measure_shell "$s")"
272
- t=$(shell_target_for "$s")
273
- shell_names+=("$s")
274
- shell_means+=("$m")
275
- shell_mins+=("$mn")
276
- shell_maxs+=("$mx")
277
- shell_targets+=("$t")
278
- if [[ "$m" -le "$t" ]]; then
279
- shell_passes+=("1")
280
- else
281
- shell_passes+=("0")
282
- fi
283
- done
284
-
285
- # Primary "score" continues to use the zsh measurement (when available)
286
- # so existing dashboards / JSON consumers keep their reference number.
287
- mean=0
288
- for i in "${!shell_names[@]}"; do
289
- if [[ "${shell_names[$i]}" == "zsh" ]]; then
290
- mean="${shell_means[$i]}"
291
- break
292
- fi
293
- done
294
- [[ "$mean" -eq 0 && "${#shell_means[@]}" -gt 0 ]] && mean="${shell_means[0]}"
295
- score=$(calc_score "$mean")
296
-
297
- # -----------------------------------------------------------------------------
298
- # Baseline persistence + regression detection (closes part of #863).
299
- # Stored at $XDG_CACHE_HOME/dotfiles/perf-baseline.json. The file is a
300
- # JSON object mapping shell name → recorded mean (ms). `--baseline`
301
- # overwrites it with the current measurement; default behavior reads
302
- # it (if present) and warns when any shell regresses by more than
303
- # DOTFILES_PERF_REGRESSION_PCT (default 10%).
304
- # -----------------------------------------------------------------------------
305
-
306
- declare -a baseline_warnings=()
307
- if $WRITE_BASELINE; then
308
- mkdir -p "$(dirname "$BASELINE_FILE")"
309
- {
310
- printf '{\n "recorded_at": "%s",\n "regression_pct": %d,\n "shells": {' \
311
- "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$BASELINE_REGRESSION_PCT"
312
- for i in "${!shell_names[@]}"; do
313
- [[ "$i" -gt 0 ]] && printf ','
314
- printf '\n "%s": %d' "${shell_names[$i]}" "${shell_means[$i]}"
315
- done
316
- printf '\n }\n}\n'
317
- } >"$BASELINE_FILE"
318
- fi
319
-
320
- if ! $NO_BASELINE_CHECK && [[ -s "$BASELINE_FILE" ]]; then
321
- # Read the baseline and compare each measured shell.
322
- for i in "${!shell_names[@]}"; do
323
- name="${shell_names[$i]}"
324
- m="${shell_means[$i]}"
325
- baseline_ms=$(python3 -c '
326
- import json, sys
327
- try:
328
- d = json.load(open(sys.argv[1]))
329
- print(d.get("shells", {}).get(sys.argv[2], 0))
330
- except Exception:
331
- print(0)
332
- ' "$BASELINE_FILE" "$name" 2>/dev/null)
333
- [[ -z "$baseline_ms" || "$baseline_ms" -eq 0 ]] && continue
334
- # Threshold: current > baseline * (1 + pct/100)
335
- threshold=$((baseline_ms * (100 + BASELINE_REGRESSION_PCT) / 100))
336
- if ((m > threshold)); then
337
- delta_pct=$((((m - baseline_ms) * 100) / baseline_ms))
338
- baseline_warnings+=("$name: $m ms vs baseline $baseline_ms ms (+${delta_pct}%, threshold +${BASELINE_REGRESSION_PCT}%)")
339
- fi
340
- done
341
- fi
342
-
343
- if $JSON_OUTPUT; then
344
- printf '{\n "runs": %d,\n "target_ms": %d,\n "max_ms_target": %d,\n "score": %d,\n "mean_ms": %d,\n "regression_count": %d,\n "shells": {' \
345
- "$RUNS" "$TARGET_MS" "$MAX_MS" "$score" "$mean" "${#baseline_warnings[@]}"
346
- for i in "${!shell_names[@]}"; do
347
- [[ "$i" -gt 0 ]] && printf ','
348
- printf '\n "%s": {"mean_ms": %d, "min_ms": %d, "max_ms": %d, "target_ms": %d, "pass": %s}' \
349
- "${shell_names[$i]}" "${shell_means[$i]}" "${shell_mins[$i]}" "${shell_maxs[$i]}" \
350
- "${shell_targets[$i]}" "$([[ ${shell_passes[$i]} == 1 ]] && echo true || echo false)"
351
- done
352
- printf '\n }'
353
- if [[ "${#baseline_warnings[@]}" -gt 0 ]]; then
354
- printf ',\n "regressions": ['
355
- for i in "${!baseline_warnings[@]}"; do
356
- [[ "$i" -gt 0 ]] && printf ','
357
- # JSON-escape the warning text minimally (no embedded quotes expected).
358
- printf '\n "%s"' "${baseline_warnings[$i]}"
359
- done
360
- printf '\n ]'
361
- fi
362
- printf '\n}\n'
363
- exit 0
364
- fi
365
-
366
- ui_dot_banner "Diagnostics"
367
- ui_header "Shell Performance"
368
-
369
- ui_section "Per-shell startup ($RUNS runs each, after one warm-up)"
370
-
371
- # Aligned table: name (6) | mean (8) | min/max (16) | target (12) | status
372
- for i in "${!shell_names[@]}"; do
373
- name="${shell_names[$i]}"
374
- m="${shell_means[$i]}"
375
- mn="${shell_mins[$i]}"
376
- mx="${shell_maxs[$i]}"
377
- t="${shell_targets[$i]}"
378
- if [[ "${shell_passes[$i]}" == "1" ]]; then
379
- status_marker="✓"
380
- detail=""
381
- else
382
- status_marker="✗"
383
- detail=" — over by $((m - t))ms"
384
- fi
385
- printf ' %s %-6s mean %4dms (min %3dms, max %3dms) target %4dms%s\n' \
386
- "$status_marker" "$name" "$m" "$mn" "$mx" "$t" "$detail"
387
- done
388
-
389
- # Baseline comparison summary
390
- if [[ "${#baseline_warnings[@]}" -gt 0 ]]; then
391
- ui_section "Baseline regressions"
392
- for w in "${baseline_warnings[@]}"; do
393
- printf ' ✗ %s\n' "$w"
394
- done
395
- printf ' (threshold: >%s%% over the recorded baseline at %s)\n' \
396
- "$BASELINE_REGRESSION_PCT" "$BASELINE_FILE"
397
- elif [[ -s "$BASELINE_FILE" ]] && ! $NO_BASELINE_CHECK; then
398
- ui_section "Baseline"
399
- printf ' ✓ all measured shells within %s%% of baseline (%s)\n' \
400
- "$BASELINE_REGRESSION_PCT" "$BASELINE_FILE"
401
- elif $WRITE_BASELINE; then
402
- ui_section "Baseline"
403
- printf ' ✓ wrote baseline to %s\n' "$BASELINE_FILE"
404
- fi
405
-
406
- # Per-component breakdown (Zsh only, uses DOTFILES_DEBUG timing)
407
- ui_section "Component breakdown (estimated)"
408
- for component in "bare zsh" "paths+env" "aliases" "functions" "tools"; do
409
- case "$component" in
410
- "bare zsh")
411
- c_start=$(python3 -c 'import time; print(int(time.time() * 1000))')
412
- zsh --no-rcs -c exit >/dev/null 2>&1 || true
413
- c_end=$(python3 -c 'import time; print(int(time.time() * 1000))')
414
- ;;
415
- "paths+env")
416
- c_start=$(python3 -c 'import time; print(int(time.time() * 1000))')
417
- DOTFILES_ULTRA_FAST=1 zsh -i -c exit >/dev/null 2>&1 || true
418
- c_end=$(python3 -c 'import time; print(int(time.time() * 1000))')
419
- ;;
420
- *)
421
- # Approximate — full minus ultra gives the delta
422
- c_start=0
423
- c_end=0
424
- ;;
425
- esac
426
- c_time=$((c_end - c_start))
427
- if [[ "$c_time" -gt 0 ]]; then
428
- if [[ "$UI_ENABLED" = "1" ]]; then
429
- ui_kv "$component" "${c_time}ms"
430
- else
431
- echo " $component: ${c_time}ms"
432
- fi
433
- fi
434
- done
435
-
436
- if $PROFILE; then
437
- echo ""
438
- ui_section "Top contributors (zprof)"
439
- run_profile
440
- echo ""
441
- fi
442
-
443
- dot_log info "perf_complete" "mean_ms=$mean" "score=$score"
444
- dot_metric "shell_startup_mean" "$mean" "ms"
445
- dot_metric "perf_score" "$score" "percent"
446
-
447
- if [[ "$score" -eq 100 ]]; then
448
- ui_ok "Performance" "Excellent"
449
- elif [[ "$score" -ge 80 ]]; then
450
- ui_warn "Performance" "Good (tune to reach 100)"
451
- else
452
- ui_err "Performance" "Needs attention"
453
- fi
@@ -1,120 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: Apache-2.0 OR MIT
3
- # Copyright (c) 2015-2026 Sebastien Rousseau
4
- # Dotfiles Scorecard
5
- # Usage: dot scorecard [--json|-j]
6
-
7
- set -euo pipefail
8
-
9
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
- # shellcheck source=../../lib/dot/ui.sh
11
- # shellcheck disable=SC1091
12
- source "$SCRIPT_DIR/../../lib/dot/ui.sh"
13
- # shellcheck source=../../lib/dot/log.sh
14
- # shellcheck disable=SC1091
15
- source "$SCRIPT_DIR/../../lib/dot/log.sh"
16
- export DOT_COMMAND="scorecard"
17
-
18
- ui_init
19
- set +x
20
-
21
- if ! command -v python3 >/dev/null 2>&1; then
22
- ui_err "python3" "required for scorecard"
23
- exit 1
24
- fi
25
-
26
- JSON_OUTPUT=false
27
- if [[ "${1:-}" == "--json" || "${1:-}" == "-j" ]]; then
28
- JSON_OUTPUT=true
29
- fi
30
-
31
- extract_json() {
32
- # strip any leading noise before the JSON payload
33
- awk 'BEGIN{p=0} /^[[:space:]]*[{]/{p=1} p{print}'
34
- }
35
-
36
- health_json=$(env -u SHELLOPTS bash "$SCRIPT_DIR/health.sh" --json 2>/dev/null | extract_json)
37
- security_json=$(env -u SHELLOPTS bash "$SCRIPT_DIR/security-score.sh" --json 2>/dev/null | extract_json)
38
- perf_json=$(env -u SHELLOPTS bash "$SCRIPT_DIR/perf.sh" --json 2>/dev/null | extract_json)
39
-
40
- if [[ -z "$health_json" || -z "$security_json" || -z "$perf_json" ]]; then
41
- ui_err "scorecard" "failed to collect JSON metrics"
42
- exit 1
43
- fi
44
-
45
- health_score=$(python3 -c 'import json,sys; j=json.loads(sys.stdin.read()); print(j.get("score",0))' <<<"$health_json")
46
- health_warnings=$(python3 -c 'import json,sys; j=json.loads(sys.stdin.read()); print(j.get("warnings",0))' <<<"$health_json")
47
- health_failures=$(python3 -c 'import json,sys; j=json.loads(sys.stdin.read()); print(j.get("failures",0))' <<<"$health_json")
48
- security_score=$(python3 -c 'import json,sys; j=json.loads(sys.stdin.read()); print(j.get("score",0))' <<<"$security_json")
49
- perf_score=$(python3 -c 'import json,sys; j=json.loads(sys.stdin.read()); print(j.get("score",0))' <<<"$perf_json")
50
- perf_mean=$(python3 -c 'import json,sys; j=json.loads(sys.stdin.read()); print(j.get("mean_ms",0))' <<<"$perf_json")
51
- perf_target=$(python3 -c 'import json,sys; j=json.loads(sys.stdin.read()); print(j.get("target_ms",0))' <<<"$perf_json")
52
-
53
- # Drift count
54
- if command -v chezmoi >/dev/null 2>&1; then
55
- # --exclude=always: always-run scripts are pending by design, not drift.
56
- drift_count=$(chezmoi status --exclude=always 2>/dev/null | wc -l | tr -d ' ')
57
- else
58
- drift_count=0
59
- fi
60
-
61
- dot_log info "scorecard_complete" "health=$health_score" "security=$security_score" "perf=$perf_score"
62
- dot_metric "scorecard_health" "$health_score" "percent"
63
- dot_metric "scorecard_security" "$security_score" "percent"
64
- dot_metric "scorecard_perf" "$perf_score" "percent"
65
-
66
- if $JSON_OUTPUT; then
67
- cat <<JSON
68
- {
69
- "health": {"score": $health_score, "warnings": $health_warnings, "failures": $health_failures},
70
- "security": {"score": $security_score},
71
- "performance": {"score": $perf_score, "mean_ms": $perf_mean, "target_ms": $perf_target},
72
- "drift": {"count": $drift_count}
73
- }
74
- JSON
75
- exit 0
76
- fi
77
-
78
- ui_dot_banner "Diagnostics"
79
- ui_header "Dotfiles Scorecard"
80
-
81
- ui_section "Scores"
82
- ui_kv "Health" "${health_score}/100"
83
- ui_kv "Security" "${security_score}/100"
84
- ui_kv "Performance" "${perf_score}/100 (avg ${perf_mean}ms, target ${perf_target}ms)"
85
- ui_kv "Drift" "${drift_count} file(s)"
86
-
87
- if [[ "$security_score" -lt 100 ]]; then
88
- ui_warn "Security" "Run 'dot security-score' to reach 100/100"
89
- else
90
- ui_ok "Security" "100/100"
91
- fi
92
-
93
- if [[ "$perf_score" -lt 100 ]]; then
94
- ui_warn "Performance" "Tune startup to reach 100/100"
95
- else
96
- ui_ok "Performance" "100/100"
97
- fi
98
-
99
- if [[ "$health_failures" -gt 0 ]]; then
100
- ui_err "Health" "Failures detected (run 'dot heal')"
101
- elif [[ "$health_warnings" -gt 0 ]]; then
102
- ui_warn "Health" "Warnings detected (run 'dot heal')"
103
- else
104
- ui_ok "Health" "All checks passing"
105
- fi
106
-
107
- ui_section "Tips"
108
- ui_info "Fix" "dot heal"
109
- ui_info "Profile" "dot perf --profile"
110
- ui_info "Security" "dot security-score"
111
- ui_info "Drift" "dot drift"
112
-
113
- exit_code=0
114
- if [[ "$security_score" -lt 100 ]]; then
115
- exit_code=1
116
- fi
117
- if [[ "$perf_score" -lt 100 ]]; then
118
- exit_code=1
119
- fi
120
- exit "$exit_code"
@@ -1,65 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: Apache-2.0 OR MIT
3
- # Copyright (c) 2015-2026 Sebastien Rousseau
4
- set -euo pipefail
5
-
6
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
- REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
8
- # shellcheck source=../lib/secrets_provider.sh
9
- # shellcheck disable=SC1091
10
- source "$REPO_ROOT/scripts/lib/secrets_provider.sh"
11
-
12
- STRICT="${DOTFILES_ALIAS_STRICT_MODE:-${DOTFILES_SECRETS_STRICT_MODE:-0}}"
13
-
14
- cd "$REPO_ROOT"
15
-
16
- # Portable read — `mapfile` is bash 4 only, and this runs from the
17
- # pre-commit hook on macOS where /bin/bash is 3.2. Without this the
18
- # hook exits 127 and the secret scan is skipped entirely.
19
- staged_files=()
20
- while IFS= read -r _line; do
21
- [[ -n "$_line" ]] && staged_files+=("$_line")
22
- done < <(git diff --cached --name-only --diff-filter=ACM || true)
23
- if [[ "${#staged_files[@]}" -eq 0 ]]; then
24
- echo "Secret governance: no staged files."
25
- exit 0
26
- fi
27
-
28
- staged_content() {
29
- local f
30
- for f in "${staged_files[@]}"; do
31
- git show -- ":$f" 2>/dev/null || true
32
- done
33
- }
34
-
35
- violations=0
36
- tmp_content="$(umask 077 && mktemp)"
37
- trap 'rm -f "$tmp_content"' EXIT
38
- staged_content >"$tmp_content"
39
-
40
- scan_patterns="(api[_-]?key|api[_-]?secret|token|password)[[:space:]]*[:=][[:space:]]*[\"'][^\"']{8,}[\"']"
41
- if grep -Eqi "$scan_patterns" "$tmp_content"; then
42
- echo "Secret governance: potential plaintext secret pattern detected in staged content."
43
- violations=$((violations + 1))
44
- fi
45
-
46
- if [[ "$STRICT" == "1" ]]; then
47
- while IFS= read -r key; do
48
- [[ -n "$key" ]] || continue
49
- value="$(dot_secrets_get "$key" || true)"
50
- [[ -n "$value" ]] || continue
51
- [[ "${#value}" -ge 8 ]] || continue
52
-
53
- if grep -Fq "$value" "$tmp_content"; then
54
- echo "Secret governance: exact managed secret value leaked for key: $key"
55
- violations=$((violations + 1))
56
- fi
57
- done < <(dot_secrets_index_list)
58
- fi
59
-
60
- if [[ "$violations" -gt 0 ]]; then
61
- echo "Secret governance failed: $violations violation(s)."
62
- exit 1
63
- fi
64
-
65
- echo "Secret governance passed."