@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,453 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: 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,119 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: 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
- drift_count=$(chezmoi status 2>/dev/null | wc -l | tr -d ' ')
56
- else
57
- drift_count=0
58
- fi
59
-
60
- dot_log info "scorecard_complete" "health=$health_score" "security=$security_score" "perf=$perf_score"
61
- dot_metric "scorecard_health" "$health_score" "percent"
62
- dot_metric "scorecard_security" "$security_score" "percent"
63
- dot_metric "scorecard_perf" "$perf_score" "percent"
64
-
65
- if $JSON_OUTPUT; then
66
- cat <<JSON
67
- {
68
- "health": {"score": $health_score, "warnings": $health_warnings, "failures": $health_failures},
69
- "security": {"score": $security_score},
70
- "performance": {"score": $perf_score, "mean_ms": $perf_mean, "target_ms": $perf_target},
71
- "drift": {"count": $drift_count}
72
- }
73
- JSON
74
- exit 0
75
- fi
76
-
77
- ui_dot_banner "Diagnostics"
78
- ui_header "Dotfiles Scorecard"
79
-
80
- ui_section "Scores"
81
- ui_kv "Health" "${health_score}/100"
82
- ui_kv "Security" "${security_score}/100"
83
- ui_kv "Performance" "${perf_score}/100 (avg ${perf_mean}ms, target ${perf_target}ms)"
84
- ui_kv "Drift" "${drift_count} file(s)"
85
-
86
- if [[ "$security_score" -lt 100 ]]; then
87
- ui_warn "Security" "Run 'dot security-score' to reach 100/100"
88
- else
89
- ui_ok "Security" "100/100"
90
- fi
91
-
92
- if [[ "$perf_score" -lt 100 ]]; then
93
- ui_warn "Performance" "Tune startup to reach 100/100"
94
- else
95
- ui_ok "Performance" "100/100"
96
- fi
97
-
98
- if [[ "$health_failures" -gt 0 ]]; then
99
- ui_err "Health" "Failures detected (run 'dot heal')"
100
- elif [[ "$health_warnings" -gt 0 ]]; then
101
- ui_warn "Health" "Warnings detected (run 'dot heal')"
102
- else
103
- ui_ok "Health" "All checks passing"
104
- fi
105
-
106
- ui_section "Tips"
107
- ui_info "Fix" "dot heal"
108
- ui_info "Profile" "dot perf --profile"
109
- ui_info "Security" "dot security-score"
110
- ui_info "Drift" "dot drift"
111
-
112
- exit_code=0
113
- if [[ "$security_score" -lt 100 ]]; then
114
- exit_code=1
115
- fi
116
- if [[ "$perf_score" -lt 100 ]]; then
117
- exit_code=1
118
- fi
119
- exit "$exit_code"
@@ -1,65 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: 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."