@sebastienrousseau/dotfiles 0.2.500 → 0.2.502
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.
- package/CHANGELOG.md +154 -0
- package/README.md +113 -45
- package/docs/.vitepress/reports/localization-readability-audit.md +4 -0
- package/docs/AI.md +8 -2
- package/docs/CNAME +1 -0
- package/docs/COPYRIGHT +1 -1
- package/docs/NAMING_CONVENTIONS.md +7 -0
- package/docs/README.md +4 -0
- package/docs/_config.yml +59 -0
- package/docs/adr/ADR-001-ci-cd-pipeline.md +17 -0
- package/docs/adr/ADR-002-shell-performance.md +9 -0
- package/docs/adr/ADR-003-security-first.md +18 -0
- package/docs/adr/ADR-004-cli-architecture.md +14 -0
- package/docs/adr/ADR-005-chezmoi-choice.md +10 -0
- package/docs/adr/ADR-006-shell-selection.md +9 -0
- package/docs/adr/ADR-007-multi-shell-parity.md +10 -1
- package/docs/adr/ADR-008-alias-system-architecture.md +10 -0
- package/docs/adr/ADR-009-wallpaper-driven-theming.md +131 -0
- package/docs/adr/ADR-010-starship-transient-prompt.md +144 -0
- package/docs/adr/ADR-011-nushell-tier3-keep.md +144 -0
- package/docs/adr/README.md +7 -0
- package/docs/architecture/ARCHITECTURE.md +4 -0
- package/docs/architecture/INTEROP.md +8 -0
- package/docs/architecture/REPO_LAYOUT.md +5 -1
- package/docs/architecture/WALKTHROUGH.md +4 -0
- package/docs/architecture/fleet-deployment.md +4 -0
- package/docs/archive/EUXIS_2026_REVIEW.md +11 -3
- package/docs/archive/LEGACY_ROADMAP.md +45 -27
- package/docs/archive/MILESTONE_v0.2.493.md +4 -0
- package/docs/archive/PLAN.md +46 -8
- package/docs/archive/REPO_AUDIT.md +8 -0
- package/docs/guides/INSTALL.md +4 -0
- package/docs/guides/NEOVIM_IDE_GUIDE.md +9 -0
- package/docs/guides/THEMING.md +8 -0
- package/docs/guides/TROUBLESHOOTING.md +28 -0
- package/docs/guides/WSL2_NIX_TROUBLESHOOTING.md +70 -1
- package/docs/index.md +6 -2
- package/docs/interop/A2A.md +7 -0
- package/docs/interop/POWERSHELL.md +102 -0
- package/docs/manual/00-introduction.md +5 -1
- package/docs/manual/01-concepts/01-architecture.md +4 -0
- package/docs/manual/01-concepts/02-trust-model.md +6 -2
- package/docs/manual/01-concepts/03-theme-engine.md +4 -0
- package/docs/manual/01-concepts/04-fleet.md +7 -1
- package/docs/manual/01-concepts/05-self-healing.md +8 -1
- package/docs/manual/02-tutorials/01-first-install.md +5 -0
- package/docs/manual/02-tutorials/02-add-wallpaper.md +5 -0
- package/docs/manual/02-tutorials/03-create-profile.md +6 -0
- package/docs/manual/02-tutorials/04-encrypt-secret.md +6 -0
- package/docs/manual/02-tutorials/05-deploy-fleet.md +5 -1
- package/docs/manual/03-reference/01-dot-cli.md +67 -45
- package/docs/manual/03-reference/02-config-files.md +8 -2
- package/docs/manual/03-reference/03-environment.md +4 -0
- package/docs/manual/03-reference/04-templates.md +7 -1
- package/docs/manual/03-reference/05-feature-flags.md +72 -3
- package/docs/manual/04-cookbook/01-recipes.md +4 -0
- package/docs/manual/04-cookbook/02-troubleshooting.md +32 -0
- package/docs/manual/04-cookbook/03-faq.md +8 -0
- package/docs/manual/05-appendices/A-platform-matrix.md +4 -0
- package/docs/manual/05-appendices/B-security-checklist.md +6 -0
- package/docs/manual/05-appendices/C-glossary.md +4 -0
- package/docs/manual/05-appendices/D-bibliography.md +5 -1
- package/docs/manual/05-appendices/E-license.md +4 -0
- package/docs/manual/_toc.yml +1 -1
- package/docs/manual/command-index.md +16 -8
- package/docs/manual/concept-index.md +4 -0
- package/docs/manual/index.md +66 -0
- package/docs/operations/ATTESTATION.md +5 -0
- package/docs/operations/CI_CADENCE.md +107 -0
- package/docs/operations/CI_COMPOSITES.md +156 -0
- package/docs/operations/COMPLETIONS.md +123 -0
- package/docs/operations/COVERAGE.md +182 -0
- package/docs/operations/DRIFT.md +107 -0
- package/docs/operations/HARD_AUDIT_2026.md +631 -0
- package/docs/operations/MAINTENANCE.md +5 -1
- package/docs/operations/MIGRATION.md +14 -6
- package/docs/operations/OPERATIONS.md +24 -0
- package/docs/operations/PERFORMANCE.md +133 -0
- package/docs/operations/REGISTRY.md +89 -0
- package/docs/operations/RELIABILITY.md +6 -0
- package/docs/operations/ROADMAP.md +36 -18
- package/docs/operations/ROADMAP_2026.md +665 -0
- package/docs/operations/TESTING.md +4 -0
- package/docs/operations/TRACEABILITY.md +7 -0
- package/docs/operations/TRUSTED_AGENT_WORKSTATION.md +4 -0
- package/docs/operations/VERSION_SYNC.md +54 -5
- package/docs/reference/ALIASES.md +7 -0
- package/docs/reference/ALIASES_CHEATSHEET.md +5 -1
- package/docs/reference/ALIASES_DEPRECATIONS.md +5 -1
- package/docs/reference/FEATURES.md +4 -0
- package/docs/reference/FONTS.md +4 -0
- package/docs/reference/POWERSHELL_PARITY.md +80 -0
- package/docs/reference/PROFILES.md +4 -0
- package/docs/reference/SCREENSHOTS.md +4 -0
- package/docs/reference/SCRIPTS.md +4 -0
- package/docs/reference/SUPPORT_MATRIX.md +10 -4
- package/docs/reference/THEMES.md +4 -0
- package/docs/reference/TOOLS.md +4 -0
- package/docs/reference/UTILS.md +16 -12
- package/docs/registry.json +6 -0
- package/docs/security/AI_ACT_COMPLIANCE.md +4 -0
- package/docs/security/AUDIT_BYPASS.md +103 -0
- package/docs/security/AUTOMATION_SECRETS.md +4 -0
- package/docs/security/CI_EGRESS_ALLOWLIST.md +127 -0
- package/docs/security/CI_PINNING.md +113 -0
- package/docs/security/COMMIT_SIGNING.md +138 -0
- package/docs/security/COMPLIANCE.md +5 -0
- package/docs/security/DEPS_DEV_EXCEPTIONS.md +86 -0
- package/docs/security/DISCLOSURE.md +130 -0
- package/docs/security/ENCRYPTION.md +5 -0
- package/docs/security/FMEA.md +4 -0
- package/docs/security/HISTORY_FILTERING.md +132 -0
- package/docs/security/INCIDENT_RESPONSE.md +4 -0
- package/docs/security/INSTALL_VERIFICATION.md +122 -0
- package/docs/security/KEYS.md +4 -0
- package/docs/security/KEY_ROTATION.md +85 -1
- package/docs/security/MCP_POLICY.md +9 -0
- package/docs/security/POLICY_RELEASES.md +4 -0
- package/docs/security/README.md +4 -0
- package/docs/security/SCORECARD.md +140 -0
- package/docs/security/SECRETS.md +12 -0
- package/docs/security/SECURITY.md +4 -0
- package/docs/security/SECURITY_CHECKLIST.md +11 -0
- package/docs/security/SHELL_EXEMPTIONS.md +145 -0
- package/docs/security/SOUP_REGISTER.md +4 -0
- package/docs/security/THREAT_MODEL.md +10 -0
- package/docs/security/VERIFICATION_VALIDATION.md +5 -1
- package/docs/security/security-pubkey.asc +15 -0
- package/docs/themes/README.md +4 -0
- package/docs/themes/VISUAL_INTEGRITY_REPORT.md +4 -0
- package/dot_config/ai/identity.md +3 -0
- package/dot_config/ai/patterns/architect.md +2 -0
- package/dot_config/ai/patterns/hardener.md +2 -0
- package/dot_config/ai/patterns/refactor.md +2 -0
- package/dot_config/alacritty/alacritty.toml.tmpl +3 -3
- package/dot_config/atuin/config.toml.tmpl +47 -0
- package/dot_config/dotfiles/agent-card.json +1 -1
- package/dot_config/dotfiles/boot/README.md +2 -0
- package/dot_config/dotfiles/grub/README.md +2 -0
- package/dot_config/dotfiles/lock/README.md +2 -0
- package/dot_config/fish/conf.d/direnv.fish +4 -0
- package/dot_config/fish/conf.d/init.fish.tmpl +21 -0
- package/dot_config/fish/conf.d/mise-activate.fish +5 -0
- package/dot_config/fish/functions/_cached_eval.fish +84 -11
- package/dot_config/fish/functions/_cached_eval_clear.fish +17 -0
- package/dot_config/foot/foot.ini.tmpl +3 -3
- package/dot_config/fuzzel/fuzzel.ini.tmpl +2 -2
- package/dot_config/ghostty/config.tmpl +3 -3
- package/dot_config/git/hooks/executable_commit-msg +146 -0
- package/dot_config/goose/config.yaml +2 -2
- package/dot_config/gtk-3.0/gtk.css.tmpl +2 -2
- package/dot_config/gtk-3.0/settings.ini.tmpl +2 -2
- package/dot_config/gtk-4.0/gtk.css.tmpl +2 -2
- package/dot_config/gtk-4.0/settings.ini.tmpl +2 -2
- package/dot_config/kitty/kitty.conf.tmpl +3 -3
- package/dot_config/mise/config.toml +1 -1
- package/dot_config/niri/config.kdl.tmpl +2 -2
- package/dot_config/nushell/cached_eval.nu +80 -0
- package/dot_config/nushell/env.nu.tmpl +21 -13
- package/dot_config/shell/00-core-paths.sh.tmpl +9 -1
- package/dot_config/shell/05-core-safety.sh +1 -0
- package/dot_config/shell/10-secrets.sh +1 -0
- package/dot_config/shell/40-fzf-defaults.sh.tmpl +1 -0
- package/dot_config/shell/40-ls-colors.sh +1 -0
- package/dot_config/shell/50-logic-functions-core.sh.tmpl +1 -0
- package/dot_config/shell/50-logic-functions.sh.tmpl +1 -0
- package/dot_config/shell/51-logic-functions-extra.sh.tmpl +1 -0
- package/dot_config/shell/90-ux-aliases.sh.tmpl +1 -0
- package/dot_config/shell/91-ux-aliases-lazy.sh.tmpl +1 -0
- package/dot_config/shell/README.md +25 -8
- package/dot_config/starship.toml.tmpl +2 -2
- package/dot_config/tmux/tmux.conf.tmpl +3 -3
- package/dot_config/user-dirs.dirs +1 -0
- package/dot_config/vscode/settings.json.tmpl +2 -2
- package/dot_config/waybar/config.jsonc.tmpl +2 -2
- package/dot_config/waybar/style.css.tmpl +2 -2
- package/dot_config/wezterm/wezterm.lua.tmpl +3 -3
- package/dot_config/zsh/dot_zshrc.tmpl +207 -17
- package/dot_config/zsh/rc.d/00-alias-shims.zsh +28 -6
- package/dot_config/zsh/rc.d/30-options.zsh.tmpl +27 -7
- package/dot_local/bin/executable_bm +2 -0
- package/dot_local/bin/executable_dot +56 -9
- package/dot_local/bin/executable_dot-bootstrap +0 -1
- package/dot_local/bin/executable_dot-load-benchmark +1 -1
- package/dot_local/bin/executable_dot-theme-sync +8 -8
- package/dot_local/bin/executable_notify +2 -0
- package/dot_local/bin/executable_open +2 -0
- package/dot_local/bin/executable_tour +4 -2
- package/dot_local/bin/executable_up +3 -1
- package/dot_local/share/man/man1/dot.1 +1 -1
- package/dot_local/share/zsh/completions/_dot +4 -0
- package/install.sh +53 -38
- package/package.json +1 -1
- package/scripts/ci/check-dangerous-chmod.sh +19 -0
- package/scripts/ci/check-deps-dev.sh +236 -0
- package/scripts/ci/check-insecure-tls.sh +61 -0
- package/scripts/ci/check-regression-traceability.sh +67 -0
- package/scripts/ci/check-shell-preamble.sh +106 -0
- package/scripts/ci/dot-cli-startup-bench.sh +126 -0
- package/scripts/ci/install-chezmoi-verified.sh +4 -1
- package/scripts/ci/lint-reusable-pins.sh +78 -0
- package/scripts/ci/run-coverage.sh +451 -0
- package/scripts/ci/validate-chezmoidata.sh +25 -0
- package/scripts/ci/windows-smoke-test.ps1 +136 -0
- package/scripts/diagnostics/doctor.sh +203 -5
- package/scripts/diagnostics/drift-dashboard.sh +177 -13
- package/scripts/diagnostics/health.sh +21 -4
- package/scripts/diagnostics/perf.sh +304 -77
- package/scripts/diagnostics/workstation-attestation.sh +6 -1
- package/scripts/dot/commands/agent.sh +33 -27
- package/scripts/dot/commands/agents.sh +325 -0
- package/scripts/dot/commands/ai.sh +75 -10
- package/scripts/dot/commands/aliases.sh +10 -8
- package/scripts/dot/commands/core.sh +10 -4
- package/scripts/dot/commands/fleet.sh +278 -3
- package/scripts/dot/commands/init.sh +184 -0
- package/scripts/dot/commands/meta.sh +7 -4
- package/scripts/dot/commands/registry.sh +263 -0
- package/scripts/dot/commands/tools.sh +49 -1
- package/scripts/dot/lib/bento.sh +2 -1
- package/scripts/dot/lib/log.sh +6 -0
- package/scripts/dot/lib/platform.sh +22 -8
- package/scripts/dot/lib/ui.sh +145 -2
- package/scripts/dot/lib/utils.sh +6 -1
- package/scripts/git-hooks/pre-commit-audit.sh +1 -1
- package/scripts/git-hooks/pre-push +66 -3
- package/scripts/lib/secrets_provider.sh +32 -6
- package/scripts/ops/heal-chezmoi.sh +41 -6
- package/scripts/ops/rollback.sh +14 -0
- package/scripts/qa/powershell-contract.ps1 +95 -0
- package/scripts/security/check-disclosure-key-expiry.sh +110 -0
- package/scripts/security/lock-configs.sh +11 -2
- package/scripts/theme/merge-wallpaper.sh +4 -0
- package/scripts/theme/switch.sh +20 -10
- package/scripts/version-sync.sh +3 -0
- package/dot_config/atuin/config.toml +0 -40
- package/dot_local/bin/__pycache__/executable_dot-load-benchmark-ptycpython-312.pyc +0 -0
|
@@ -387,6 +387,49 @@ else
|
|
|
387
387
|
_fail "dot" "not found in PATH"
|
|
388
388
|
fi
|
|
389
389
|
|
|
390
|
+
# --- Pre-push audit bypass log (closes #871) ---
|
|
391
|
+
_section "Pre-Push Audit Bypass"
|
|
392
|
+
|
|
393
|
+
bypass_log="${XDG_STATE_HOME:-$HOME/.local/state}/dotfiles/audit-bypass.log"
|
|
394
|
+
if [[ -s "$bypass_log" ]]; then
|
|
395
|
+
# Count entries in the last 7 days (timestamps are ISO-8601 UTC).
|
|
396
|
+
seven_days_ago=$(date -u -v-7d +%Y-%m-%dT00:00:00Z 2>/dev/null ||
|
|
397
|
+
date -u -d '7 days ago' +%Y-%m-%dT00:00:00Z 2>/dev/null ||
|
|
398
|
+
echo "1970-01-01T00:00:00Z")
|
|
399
|
+
recent=$(awk -v cutoff="$seven_days_ago" '$1 >= cutoff' "$bypass_log" | wc -l | tr -d ' ')
|
|
400
|
+
if ((recent > 0)); then
|
|
401
|
+
_warn "audit bypass" "$recent push(es) bypassed in last 7 days — see $(pretty_path "$bypass_log")"
|
|
402
|
+
else
|
|
403
|
+
_ok "audit bypass" "log exists; no recent entries (last 7d)"
|
|
404
|
+
fi
|
|
405
|
+
else
|
|
406
|
+
_ok "audit bypass" "clean — no pre-push audits bypassed"
|
|
407
|
+
fi
|
|
408
|
+
|
|
409
|
+
# --- Atuin history-filter (closes #872) ---
|
|
410
|
+
_section "Atuin History Filter"
|
|
411
|
+
|
|
412
|
+
atuin_cfg="$HOME/.config/atuin/config.toml"
|
|
413
|
+
if [[ ! -f "$atuin_cfg" ]]; then
|
|
414
|
+
_warn "atuin config" "not found at $(pretty_path "$atuin_cfg")"
|
|
415
|
+
else
|
|
416
|
+
if grep -Eq "^history_filter[[:space:]]*=" "$atuin_cfg"; then
|
|
417
|
+
pattern_count=$(awk '
|
|
418
|
+
/^history_filter[[:space:]]*=[[:space:]]*\[/ { inside = 1; next }
|
|
419
|
+
inside && /^\]/ { inside = 0 }
|
|
420
|
+
inside && /^[[:space:]]*"/ { count++ }
|
|
421
|
+
END { print count + 0 }
|
|
422
|
+
' "$atuin_cfg")
|
|
423
|
+
if ((pattern_count >= 10)); then
|
|
424
|
+
_ok "history_filter" "$pattern_count patterns (chezmoi-managed)"
|
|
425
|
+
else
|
|
426
|
+
_warn "history_filter" "$pattern_count patterns (expected ≥10 from secrets-patterns.toml)"
|
|
427
|
+
fi
|
|
428
|
+
else
|
|
429
|
+
_fail "history_filter" "no history_filter block in $(pretty_path "$atuin_cfg") — secrets may leak into shell history"
|
|
430
|
+
fi
|
|
431
|
+
fi
|
|
432
|
+
|
|
390
433
|
# --- Topgrade Integration ---
|
|
391
434
|
_section "Topgrade Integration"
|
|
392
435
|
|
|
@@ -472,10 +515,12 @@ fi
|
|
|
472
515
|
# --- Performance ---
|
|
473
516
|
_section "Performance"
|
|
474
517
|
|
|
475
|
-
|
|
518
|
+
cache_base="${XDG_CACHE_HOME:-$HOME/.cache}"
|
|
519
|
+
|
|
520
|
+
# 1. Shell cache freshness for tools the project already wraps in _cached_eval.
|
|
521
|
+
# Stale caches force runtime regeneration on next shell start.
|
|
476
522
|
stale_caches=0
|
|
477
523
|
stale_tools=""
|
|
478
|
-
cache_base="${XDG_CACHE_HOME:-$HOME/.cache}"
|
|
479
524
|
for tool in mise starship zoxide atuin fzf direnv; do
|
|
480
525
|
tool_bin="$(command -v "$tool" 2>/dev/null || true)"
|
|
481
526
|
[[ -n "$tool_bin" ]] || continue
|
|
@@ -486,22 +531,128 @@ for tool in mise starship zoxide atuin fzf direnv; do
|
|
|
486
531
|
esac
|
|
487
532
|
if [[ ! -f "$cache_file" ]] || [[ "$tool_bin" -nt "$cache_file" ]]; then
|
|
488
533
|
stale_caches=$((stale_caches + 1))
|
|
489
|
-
# Track unique tool names for the summary
|
|
490
534
|
case "$stale_tools" in
|
|
491
535
|
*"$tool"*) ;;
|
|
492
536
|
*) stale_tools="${stale_tools:+$stale_tools, }$tool" ;;
|
|
493
537
|
esac
|
|
494
|
-
break
|
|
538
|
+
break
|
|
495
539
|
fi
|
|
496
540
|
done
|
|
497
541
|
done
|
|
498
|
-
|
|
499
542
|
if [[ $stale_caches -eq 0 ]]; then
|
|
500
543
|
_ok "shell caches" "fresh"
|
|
501
544
|
else
|
|
502
545
|
_warn "shell caches" "stale ($stale_tools) — run dot prewarm"
|
|
503
546
|
fi
|
|
504
547
|
|
|
548
|
+
# 2. Slow-init tools that are present but NOT wrapped in _cached_eval.
|
|
549
|
+
# Each of these runs uncached on every shell start; common offenders eat
|
|
550
|
+
# 100-500ms apiece on a populated dev machine.
|
|
551
|
+
#
|
|
552
|
+
# Only flag tools that actually emit shell init via `<tool> init <shell>`
|
|
553
|
+
# (or equivalent) and would benefit from caching that output. Plain CLIs
|
|
554
|
+
# like gh/cargo/pnpm/yarn don't have init eval; their completions are
|
|
555
|
+
# cached separately under $ZSH_COMPLETIONS_DIR.
|
|
556
|
+
unwrapped=""
|
|
557
|
+
for tool in nvm fnm pyenv rbenv jenv asdf sdkman conda kubectl helm thefuck broot mcfly direnv; do
|
|
558
|
+
command -v "$tool" >/dev/null 2>&1 || continue
|
|
559
|
+
# If the tool is installed but no shell config sources or evals its
|
|
560
|
+
# init (no `$tool env`, `$tool init`, `$tool.sh`, lazy-load stub), it
|
|
561
|
+
# isn't adding startup cost — skip the warning.
|
|
562
|
+
init_referenced=0
|
|
563
|
+
if grep -rIlqE "\\b${tool}([[:space:]]+(env|init|hook)|\\.sh|_lazy_load_${tool}|_dot_lazy[[:space:]]+${tool})" \
|
|
564
|
+
"$HOME/.config/zsh" "$HOME/.config/fish" "$HOME/.config/shell" 2>/dev/null; then
|
|
565
|
+
init_referenced=1
|
|
566
|
+
fi
|
|
567
|
+
((init_referenced == 0)) && continue
|
|
568
|
+
# Tools we lazy-load via shell stubs don't need init-eval cache files.
|
|
569
|
+
case "$tool" in
|
|
570
|
+
fnm | nvm | sdkman)
|
|
571
|
+
grep -rIlqE "_lazy_load_${tool}|_dot_lazy[[:space:]]+${tool}" \
|
|
572
|
+
"$HOME/.config/zsh" "$HOME/.config/fish" 2>/dev/null && continue
|
|
573
|
+
;;
|
|
574
|
+
esac
|
|
575
|
+
found=0
|
|
576
|
+
for shell_dir in zsh bash fish; do
|
|
577
|
+
case "$shell_dir" in
|
|
578
|
+
fish) [[ -f "$cache_base/$shell_dir/${tool}-init.fish" ]] && found=1 ;;
|
|
579
|
+
*) [[ -f "$cache_base/$shell_dir/${tool}-init.$shell_dir" ]] && found=1 ;;
|
|
580
|
+
esac
|
|
581
|
+
((found == 1)) && break
|
|
582
|
+
done
|
|
583
|
+
((found == 0)) && unwrapped="${unwrapped:+$unwrapped, }$tool"
|
|
584
|
+
done
|
|
585
|
+
if [[ -z "$unwrapped" ]]; then
|
|
586
|
+
_ok "uncached slow-init tools" "none detected"
|
|
587
|
+
else
|
|
588
|
+
_warn "uncached slow-init tools" "$unwrapped — consider wrapping in _cached_eval"
|
|
589
|
+
fi
|
|
590
|
+
|
|
591
|
+
# 3. Zsh completion dump health. compinit is usually the single biggest
|
|
592
|
+
# cost on a zsh startup; a stale or uncompiled .zcompdump compounds it.
|
|
593
|
+
if command -v zsh >/dev/null 2>&1; then
|
|
594
|
+
zcompdump="${HOME}/.zcompdump"
|
|
595
|
+
if [[ -f "$zcompdump" ]]; then
|
|
596
|
+
dump_mtime=$(stat -c %Y "$zcompdump" 2>/dev/null || stat -f %m "$zcompdump" 2>/dev/null || echo 0)
|
|
597
|
+
age_days=$((($(date +%s) - dump_mtime) / 86400))
|
|
598
|
+
if ((age_days > 7)); then
|
|
599
|
+
_warn ".zcompdump" "${age_days}d old — refresh: rm ~/.zcompdump* && zsh -ic exit"
|
|
600
|
+
else
|
|
601
|
+
_ok ".zcompdump" "fresh (${age_days}d)"
|
|
602
|
+
fi
|
|
603
|
+
if [[ ! -f "${zcompdump}.zwc" ]]; then
|
|
604
|
+
_warn ".zcompdump.zwc" "missing — completion init slower than necessary"
|
|
605
|
+
fi
|
|
606
|
+
fi
|
|
607
|
+
fi
|
|
608
|
+
|
|
609
|
+
# 4. PATH length. Each entry is searched on every command resolution.
|
|
610
|
+
# A mise-managed dev machine routinely adds 50+ entries (one per tool
|
|
611
|
+
# install path), so the warn/fail thresholds are set higher than a
|
|
612
|
+
# lean baseline (40) would suggest.
|
|
613
|
+
path_count=$(printf '%s' "${PATH:-}" | tr ':' '\n' | grep -c . || true)
|
|
614
|
+
if [[ "$path_count" -le 60 ]]; then
|
|
615
|
+
_ok "PATH length" "$path_count entries"
|
|
616
|
+
elif [[ "$path_count" -le 120 ]]; then
|
|
617
|
+
_warn "PATH length" "$path_count entries — consider pruning"
|
|
618
|
+
else
|
|
619
|
+
_fail "PATH length" "$path_count entries — likely slowing every command"
|
|
620
|
+
fi
|
|
621
|
+
|
|
622
|
+
# 5. Shell coverage. Surface installed shells that the project's caching
|
|
623
|
+
# infrastructure doesn't currently maintain caches for. zsh/bash/fish/nu
|
|
624
|
+
# all have a `_cached_eval` analogue; pwsh does not.
|
|
625
|
+
shells_unmanaged=""
|
|
626
|
+
for sh in nu pwsh; do
|
|
627
|
+
command -v "$sh" >/dev/null 2>&1 || continue
|
|
628
|
+
case "$sh" in
|
|
629
|
+
nu)
|
|
630
|
+
[[ -f "$HOME/.config/nushell/cached_eval.nu" ]] && continue
|
|
631
|
+
;;
|
|
632
|
+
esac
|
|
633
|
+
shells_unmanaged="${shells_unmanaged:+$shells_unmanaged, }$sh"
|
|
634
|
+
done
|
|
635
|
+
if [[ -z "$shells_unmanaged" ]]; then
|
|
636
|
+
_ok "shell coverage" "all installed shells have _cached_eval support"
|
|
637
|
+
else
|
|
638
|
+
_warn "shell coverage" "$shells_unmanaged installed — no _cached_eval helper"
|
|
639
|
+
fi
|
|
640
|
+
|
|
641
|
+
# 6. Zsh hook count. Heavy precmd/preexec functions compound per-prompt.
|
|
642
|
+
# Probe an interactive zsh with a hard timeout so a broken zshrc doesn't
|
|
643
|
+
# stall doctor; skip cleanly if the probe fails.
|
|
644
|
+
if command -v zsh >/dev/null 2>&1 && command -v timeout >/dev/null 2>&1; then
|
|
645
|
+
hook_counts=$(timeout 5 zsh -i -c 'echo "$#precmd_functions $#preexec_functions"' 2>/dev/null || echo "")
|
|
646
|
+
if [[ -n "$hook_counts" ]]; then
|
|
647
|
+
read -r precmd_n preexec_n <<<"$hook_counts"
|
|
648
|
+
if [[ "${precmd_n:-0}" -le 5 && "${preexec_n:-0}" -le 5 ]]; then
|
|
649
|
+
_ok "zsh hooks" "precmd=$precmd_n preexec=$preexec_n"
|
|
650
|
+
else
|
|
651
|
+
_warn "zsh hooks" "precmd=$precmd_n preexec=$preexec_n — heavy per-prompt work"
|
|
652
|
+
fi
|
|
653
|
+
fi
|
|
654
|
+
fi
|
|
655
|
+
|
|
505
656
|
if command -v hyperfine >/dev/null 2>&1; then
|
|
506
657
|
if bash "$SCRIPT_DIR/../../tests/performance/bench.sh" 2>/dev/null; then
|
|
507
658
|
_ok "startup latency" "within target thresholds"
|
|
@@ -512,6 +663,53 @@ else
|
|
|
512
663
|
_warn "hyperfine" "missing (benchmark skipped)"
|
|
513
664
|
fi
|
|
514
665
|
|
|
666
|
+
# 7. Baseline check + top-3 slowest tools from EVALCACHE_TIMING.
|
|
667
|
+
# Closes part of #863. Reads the same baseline file `dot perf` writes,
|
|
668
|
+
# and the same eval-timings.jsonl _cached_eval populates. Skipped
|
|
669
|
+
# silently when either file is absent (first-run state).
|
|
670
|
+
baseline_file="$cache_base/dotfiles/perf-baseline.json"
|
|
671
|
+
if [[ -s "$baseline_file" ]] && command -v python3 >/dev/null 2>&1; then
|
|
672
|
+
baseline_age_days=$(python3 -c '
|
|
673
|
+
import json, sys, datetime
|
|
674
|
+
try:
|
|
675
|
+
d = json.load(open(sys.argv[1]))
|
|
676
|
+
rec = d.get("recorded_at", "")
|
|
677
|
+
if not rec: print(-1); sys.exit(0)
|
|
678
|
+
rec = rec.replace("Z", "+00:00")
|
|
679
|
+
age = (datetime.datetime.now(datetime.timezone.utc) - datetime.datetime.fromisoformat(rec)).days
|
|
680
|
+
print(age)
|
|
681
|
+
except Exception:
|
|
682
|
+
print(-1)
|
|
683
|
+
' "$baseline_file" 2>/dev/null)
|
|
684
|
+
if [[ "$baseline_age_days" -ge 0 ]]; then
|
|
685
|
+
_ok "perf baseline" "recorded ${baseline_age_days}d ago — run \`dot perf\` to compare"
|
|
686
|
+
fi
|
|
687
|
+
fi
|
|
688
|
+
|
|
689
|
+
timings_file="${XDG_STATE_HOME:-$HOME/.local/state}/dotfiles/eval-timings.jsonl"
|
|
690
|
+
if [[ -s "$timings_file" ]] && command -v python3 >/dev/null 2>&1; then
|
|
691
|
+
top_tools=$(python3 -c '
|
|
692
|
+
import json, sys
|
|
693
|
+
from collections import defaultdict
|
|
694
|
+
samples = defaultdict(list)
|
|
695
|
+
try:
|
|
696
|
+
for line in open(sys.argv[1]):
|
|
697
|
+
try:
|
|
698
|
+
ev = json.loads(line)
|
|
699
|
+
ms = int(ev.get("ms", 0) or 0)
|
|
700
|
+
samples[ev.get("label", "?")].append(ms)
|
|
701
|
+
except Exception:
|
|
702
|
+
pass
|
|
703
|
+
rows = sorted(samples.items(), key=lambda kv: sum(kv[1]) // max(len(kv[1]), 1), reverse=True)[:3]
|
|
704
|
+
print(", ".join(f"{lbl}({sum(v)//max(len(v),1)}ms)" for lbl, v in rows))
|
|
705
|
+
except Exception:
|
|
706
|
+
pass
|
|
707
|
+
' "$timings_file" 2>/dev/null)
|
|
708
|
+
if [[ -n "$top_tools" ]]; then
|
|
709
|
+
_ok "perf top-tools" "$top_tools"
|
|
710
|
+
fi
|
|
711
|
+
fi
|
|
712
|
+
|
|
515
713
|
# --- Summary ---
|
|
516
714
|
dot_log info "doctor_complete" "errors=$Errors" "warnings=$Warnings"
|
|
517
715
|
dot_metric "doctor_errors" "$Errors" "count"
|
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Copyright (c) 2015-2026 Dotfiles. All rights reserved.
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# drift-dashboard.sh — Consolidated drift report.
|
|
5
|
+
#
|
|
6
|
+
# Surfaces four classes of drift between the chezmoi source and the
|
|
7
|
+
# deployed targets:
|
|
8
|
+
#
|
|
9
|
+
# 1. chezmoi-managed drift — deployed file differs from rendered source
|
|
10
|
+
# (`chezmoi status` output). Standard case; what the original
|
|
11
|
+
# drift-dashboard reported.
|
|
12
|
+
# 2. Untracked source — a file exists in the chezmoi source tree but
|
|
13
|
+
# isn't tracked by git, suggesting in-progress local work that
|
|
14
|
+
# hasn't been committed.
|
|
15
|
+
# 3. Orphan deployed — a file under XDG/HOME that was previously
|
|
16
|
+
# chezmoi-managed but the source has since been removed. Detected
|
|
17
|
+
# by sampling well-known managed paths and asking chezmoi if it
|
|
18
|
+
# claims each.
|
|
19
|
+
# 4. Stale source — source file older than its deployed target,
|
|
20
|
+
# which means someone hand-edited the deployed file and the next
|
|
21
|
+
# `chezmoi apply` will silently revert it. Reverse-drift trap.
|
|
22
|
+
#
|
|
23
|
+
# Output is JSON when --json is passed, otherwise the existing
|
|
24
|
+
# ui-formatted summary. Exit code: 0 if every section is clean; 1 if
|
|
25
|
+
# any drift is found; 2 if a prerequisite (chezmoi, git) is missing.
|
|
26
|
+
#
|
|
27
|
+
# Closes #875.
|
|
28
|
+
# =============================================================================
|
|
29
|
+
|
|
3
30
|
set -euo pipefail
|
|
4
31
|
|
|
5
32
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -7,31 +34,168 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
7
34
|
# shellcheck disable=SC1091
|
|
8
35
|
source "$SCRIPT_DIR/../dot/lib/ui.sh"
|
|
9
36
|
|
|
37
|
+
JSON_MODE=0
|
|
38
|
+
SHOW_DIFF="${DOTFILES_DRIFT_SHOW_DIFF:-0}"
|
|
39
|
+
|
|
40
|
+
for arg in "$@"; do
|
|
41
|
+
case "$arg" in
|
|
42
|
+
--json | -j) JSON_MODE=1 ;;
|
|
43
|
+
--diff | -d) SHOW_DIFF=1 ;;
|
|
44
|
+
--help | -h)
|
|
45
|
+
cat <<EOF
|
|
46
|
+
Usage: drift-dashboard.sh [options]
|
|
47
|
+
|
|
48
|
+
Options:
|
|
49
|
+
--json, -j Emit a single JSON object summarising every drift class.
|
|
50
|
+
--diff, -d Also print the chezmoi diff (excluding scripts/install/tests).
|
|
51
|
+
--help, -h Show this help.
|
|
52
|
+
EOF
|
|
53
|
+
exit 0
|
|
54
|
+
;;
|
|
55
|
+
esac
|
|
56
|
+
done
|
|
57
|
+
|
|
10
58
|
ui_init
|
|
11
59
|
|
|
12
60
|
if ! command -v chezmoi >/dev/null; then
|
|
13
|
-
|
|
14
|
-
|
|
61
|
+
if [[ $JSON_MODE -eq 1 ]]; then
|
|
62
|
+
printf '{"error":"chezmoi not found"}\n'
|
|
63
|
+
else
|
|
64
|
+
ui_err "chezmoi" "not found"
|
|
65
|
+
fi
|
|
66
|
+
exit 2
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# -----------------------------------------------------------------------------
|
|
70
|
+
# Class 1: chezmoi-managed drift
|
|
71
|
+
# -----------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
cm_status="$(chezmoi status 2>/dev/null || true)"
|
|
74
|
+
cm_count=0
|
|
75
|
+
if [[ -n "$cm_status" ]]; then
|
|
76
|
+
cm_count=$(printf '%s\n' "$cm_status" | wc -l | tr -d ' ')
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# -----------------------------------------------------------------------------
|
|
80
|
+
# Class 2: untracked source files (chezmoi source tree only)
|
|
81
|
+
# -----------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
untracked=""
|
|
84
|
+
untracked_count=0
|
|
85
|
+
src_dir="$(chezmoi source-path 2>/dev/null || true)"
|
|
86
|
+
if [[ -n "$src_dir" && -d "$src_dir/.git" ]]; then
|
|
87
|
+
untracked="$(git -C "$src_dir" ls-files --others --exclude-standard 2>/dev/null || true)"
|
|
88
|
+
if [[ -n "$untracked" ]]; then
|
|
89
|
+
untracked_count=$(printf '%s\n' "$untracked" | wc -l | tr -d ' ')
|
|
90
|
+
fi
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# -----------------------------------------------------------------------------
|
|
94
|
+
# Class 4: stale source (source older than deployed = pending revert risk)
|
|
95
|
+
# Compute by walking chezmoi-managed targets and comparing mtimes.
|
|
96
|
+
# -----------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
stale_list=""
|
|
99
|
+
stale_count=0
|
|
100
|
+
if [[ -n "$src_dir" ]]; then
|
|
101
|
+
while IFS= read -r target; do
|
|
102
|
+
[[ -z "$target" ]] && continue
|
|
103
|
+
target_path="$target"
|
|
104
|
+
[[ ! -e "$target_path" ]] && continue
|
|
105
|
+
src_path="$(chezmoi source-path "$target_path" 2>/dev/null || true)"
|
|
106
|
+
[[ -z "$src_path" || ! -e "$src_path" ]] && continue
|
|
107
|
+
if [[ "$target_path" -nt "$src_path" ]]; then
|
|
108
|
+
stale_list+="$target_path"$'\n'
|
|
109
|
+
stale_count=$((stale_count + 1))
|
|
110
|
+
fi
|
|
111
|
+
done < <(chezmoi managed 2>/dev/null | head -200 | while IFS= read -r rel; do
|
|
112
|
+
printf '%s\n' "$HOME/$rel"
|
|
113
|
+
done)
|
|
114
|
+
fi
|
|
115
|
+
stale_list="${stale_list%$'\n'}"
|
|
116
|
+
|
|
117
|
+
# -----------------------------------------------------------------------------
|
|
118
|
+
# Class 3: orphan deployed files (chezmoi no longer claims them)
|
|
119
|
+
# Sample heuristic — chezmoi doesn't expose a direct "orphan" query.
|
|
120
|
+
# We surface a count of zero unless the user has a pre-populated
|
|
121
|
+
# ~/.local/state/dotfiles/orphans file (drift-history feature in fleet
|
|
122
|
+
# already maintains one); future work tracked under #875.
|
|
123
|
+
# -----------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
orphan_count=0
|
|
126
|
+
orphan_file="${XDG_STATE_HOME:-$HOME/.local/state}/dotfiles/orphans"
|
|
127
|
+
if [[ -s "$orphan_file" ]]; then
|
|
128
|
+
orphan_count=$(wc -l <"$orphan_file" | tr -d ' ')
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# -----------------------------------------------------------------------------
|
|
132
|
+
# Report
|
|
133
|
+
# -----------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
total=$((cm_count + untracked_count + orphan_count + stale_count))
|
|
136
|
+
|
|
137
|
+
if [[ $JSON_MODE -eq 1 ]]; then
|
|
138
|
+
python3 - <<PY
|
|
139
|
+
import json, sys
|
|
140
|
+
print(json.dumps({
|
|
141
|
+
"managed_drift": $cm_count,
|
|
142
|
+
"untracked_source": $untracked_count,
|
|
143
|
+
"orphan_deployed": $orphan_count,
|
|
144
|
+
"stale_source": $stale_count,
|
|
145
|
+
"total": $total
|
|
146
|
+
}))
|
|
147
|
+
PY
|
|
148
|
+
if ((total > 0)); then exit 1; else exit 0; fi
|
|
15
149
|
fi
|
|
16
150
|
|
|
17
151
|
ui_header "Dotfiles Drift Dashboard"
|
|
18
152
|
|
|
19
|
-
|
|
20
|
-
if
|
|
21
|
-
|
|
22
|
-
|
|
153
|
+
# Class 1
|
|
154
|
+
if ((cm_count > 0)); then
|
|
155
|
+
echo ""
|
|
156
|
+
ui_warn "Managed drift" "$cm_count file(s) — deployed differs from rendered source"
|
|
157
|
+
printf '%s\n' "$cm_status"
|
|
158
|
+
else
|
|
159
|
+
ui_ok "Managed drift" "clean"
|
|
23
160
|
fi
|
|
24
161
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
162
|
+
# Class 2
|
|
163
|
+
if ((untracked_count > 0)); then
|
|
164
|
+
echo ""
|
|
165
|
+
ui_warn "Untracked source" "$untracked_count file(s) in chezmoi source not tracked by git"
|
|
166
|
+
printf '%s\n' "$untracked"
|
|
167
|
+
else
|
|
168
|
+
ui_ok "Untracked source" "clean"
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
# Class 3
|
|
172
|
+
if ((orphan_count > 0)); then
|
|
173
|
+
echo ""
|
|
174
|
+
ui_warn "Orphan deployed" "$orphan_count file(s) — review $(pretty_path "$orphan_file")"
|
|
175
|
+
else
|
|
176
|
+
ui_ok "Orphan deployed" "clean"
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# Class 4
|
|
180
|
+
if ((stale_count > 0)); then
|
|
181
|
+
echo ""
|
|
182
|
+
ui_warn "Stale source" "$stale_count target(s) newer than source — next \`chezmoi apply\` would revert"
|
|
183
|
+
printf '%s\n' "$stale_list"
|
|
184
|
+
else
|
|
185
|
+
ui_ok "Stale source" "clean"
|
|
186
|
+
fi
|
|
28
187
|
|
|
29
|
-
count=$(printf '%s\n' "$status" | wc -l | tr -d ' ')
|
|
30
188
|
echo ""
|
|
31
|
-
|
|
189
|
+
if ((total > 0)); then
|
|
190
|
+
ui_warn "Total drift signals" "$total"
|
|
191
|
+
else
|
|
192
|
+
ui_ok "Total" "no drift detected"
|
|
193
|
+
fi
|
|
32
194
|
|
|
33
|
-
if [[ "$
|
|
195
|
+
if [[ "$SHOW_DIFF" = "1" && $cm_count -gt 0 ]]; then
|
|
34
196
|
echo ""
|
|
35
|
-
ui_header "
|
|
197
|
+
ui_header "chezmoi diff (excluding scripts/install/tests)"
|
|
36
198
|
chezmoi diff --exclude scripts --exclude install --exclude tests || true
|
|
37
199
|
fi
|
|
200
|
+
|
|
201
|
+
((total > 0)) && exit 1 || exit 0
|
|
@@ -420,16 +420,33 @@ check_config_directories() {
|
|
|
420
420
|
check_sync_status() {
|
|
421
421
|
check_section "Sync Status"
|
|
422
422
|
|
|
423
|
-
# Chezmoi status
|
|
423
|
+
# Chezmoi status. The two-character prefix encodes both directions:
|
|
424
|
+
# col 1 = last-applied state vs actual (was the file edited in
|
|
425
|
+
# place since last apply?)
|
|
426
|
+
# col 2 = actual state vs target (what `chezmoi apply`
|
|
427
|
+
# would still change)
|
|
428
|
+
# Only col 2 matters for "is $HOME out of sync with the source" —
|
|
429
|
+
# col 1 alone just means the source repo has uncommitted edits,
|
|
430
|
+
# which is normal during development and not something the user
|
|
431
|
+
# needs warned about via the health dashboard.
|
|
424
432
|
if has_command chezmoi; then
|
|
425
433
|
local status_output
|
|
426
434
|
status_output=$(chezmoi status 2>/dev/null || echo "")
|
|
427
435
|
if [[ -z "$status_output" ]]; then
|
|
428
436
|
check "Chezmoi sync" "pass"
|
|
429
437
|
else
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
438
|
+
# Count only entries where column 2 is non-space (apply would do something).
|
|
439
|
+
local applicable
|
|
440
|
+
applicable=$(printf '%s\n' "$status_output" | awk 'substr($0,2,1)!=" "' | wc -l | tr -d ' ')
|
|
441
|
+
if [[ "$applicable" -eq 0 ]]; then
|
|
442
|
+
# All drift is source-only (unstaged edits in the source repo).
|
|
443
|
+
# That's not a sync issue; mention it but pass.
|
|
444
|
+
local source_only
|
|
445
|
+
source_only=$(printf '%s\n' "$status_output" | wc -l | tr -d ' ')
|
|
446
|
+
check "Chezmoi sync" "pass" "$source_only source-only edit(s) (run 'chezmoi diff' to inspect)"
|
|
447
|
+
else
|
|
448
|
+
check "Chezmoi sync" "warn" "$applicable file(s) out of sync"
|
|
449
|
+
fi
|
|
433
450
|
fi
|
|
434
451
|
else
|
|
435
452
|
check "Chezmoi sync" "warn" "Not installed, skipped"
|