@sebastienrousseau/dotfiles 0.2.518 → 0.2.520
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 +191 -0
- package/README.md +1164 -164
- package/docs/ARCHITECTURE.md +117 -0
- package/docs/COPYRIGHT +1 -1
- package/docs/ECOSYSTEM.md +220 -0
- package/docs/GOLD-STANDARD-AUDIT.md +352 -0
- package/docs/GOVERNANCE.md +1 -1
- package/docs/MAINTAINERS.md +1 -1
- package/docs/MINIMUM-TOOLCHAIN.md +100 -0
- package/docs/README.md +1 -1
- package/docs/STRUCTURE.md +1 -1
- package/docs/architecture/ARCHITECTURE.md +11 -108
- package/docs/architecture/REPO_LAYOUT.md +4 -4
- package/docs/guides/MACOS_ICLOUD_SYMLINKS.md +121 -0
- package/docs/index.md +3 -3
- package/docs/manual/00-introduction.md +1 -1
- package/docs/manual/03-reference/01-dot-cli.md +28 -3
- package/docs/manual/command-index.md +24 -4
- package/docs/manual/concept-index.md +2 -0
- package/docs/migration/README.md +81 -0
- package/docs/migration/from-bare-git-repo.md +156 -0
- package/docs/migration/from-gnu-stow.md +165 -0
- package/docs/migration/from-plain-chezmoi.md +148 -0
- package/docs/migration/from-yadm.md +187 -0
- package/docs/operations/PERFORMANCE_BUDGETS.md +196 -0
- package/docs/operations/REGISTRY.md +1 -1
- package/docs/operations/RELEASE_PIPELINE.md +4 -4
- package/docs/operations/TESTING.md +3 -3
- package/docs/operations/TRACEABILITY.md +1 -0
- package/docs/packaging.md +222 -0
- package/docs/reference/FEATURE-MATRIX.md +646 -0
- package/docs/reference/TOOLS.md +1 -1
- package/docs/reference/UTILS.md +1 -0
- package/docs/security/COMPLIANCE.md +1 -1
- package/docs/security/DISCLOSURE.md +4 -4
- package/docs/security/FUZZING.md +113 -18
- package/docs/security/KEY_ROTATION.md +1 -1
- package/docs/security/SCORECARD.md +3 -3
- package/docs/security/VERIFY_RELEASE.md +2 -2
- package/install.sh +6 -6
- package/package.json +2 -2
- package/scripts/ci/check-copyright-headers.sh +1 -1
- package/scripts/ci/check-shell-preamble.sh +1 -1
- package/scripts/ci/guard-gitleaks-checkout.sh +1 -1
- package/scripts/demo/record.sh +1 -1
- package/scripts/diagnostics/a2a-conformance.sh +1 -1
- package/scripts/diagnostics/alias-governance.sh +30 -3
- package/scripts/diagnostics/aliases-cheatsheet.sh +1 -1
- package/scripts/diagnostics/aliases-manifest.sh +1 -1
- package/scripts/diagnostics/attest-verify.sh +147 -0
- package/scripts/diagnostics/benchmark.sh +1 -1
- package/scripts/diagnostics/conflicts.sh +1 -1
- package/scripts/diagnostics/doctor-unified.sh +6 -2
- package/scripts/diagnostics/doctor.sh +56 -10
- package/scripts/diagnostics/drift-dashboard.sh +3 -2
- package/scripts/diagnostics/health.sh +43 -10
- package/scripts/diagnostics/history-analysis.sh +1 -1
- package/scripts/diagnostics/mcp-doctor.sh +2 -2
- package/scripts/diagnostics/perf.sh +1 -1
- package/scripts/diagnostics/scorecard.sh +3 -2
- package/scripts/diagnostics/secret-governance.sh +1 -1
- package/scripts/diagnostics/security-score.sh +1 -1
- package/scripts/diagnostics/smoke-test.sh +1 -1
- package/scripts/diagnostics/snapshot.sh +1 -1
- package/scripts/diagnostics/verify.sh +1 -1
- package/scripts/diagnostics/verify_state.sh +1 -1
- package/scripts/diagnostics/version-locks.sh +1 -1
- package/scripts/diagnostics/workstation-attestation.sh +26 -1
- package/scripts/dot/commands/agent.sh +64 -14
- package/scripts/dot/commands/agents.sh +24 -8
- package/scripts/dot/commands/ai.sh +20 -7
- package/scripts/dot/commands/aliases.sh +1 -1
- package/scripts/dot/commands/appearance.sh +1 -1
- package/scripts/dot/commands/completion.sh +42 -5
- package/scripts/dot/commands/core.sh +1 -1
- package/scripts/dot/commands/diagnostics.sh +1 -1
- package/scripts/dot/commands/env-emit.sh +1 -1
- package/scripts/dot/commands/fleet.sh +41 -18
- package/scripts/dot/commands/init.sh +1 -1
- package/scripts/dot/commands/lint.sh +1 -1
- package/scripts/dot/commands/manual.sh +1 -1
- package/scripts/dot/commands/meta.sh +115 -10
- package/scripts/dot/commands/patterns.sh +1 -1
- package/scripts/dot/commands/registry.sh +43 -7
- package/scripts/dot/commands/restore.sh +1 -1
- package/scripts/dot/commands/secrets.sh +1 -1
- package/scripts/dot/commands/security.sh +1 -1
- package/scripts/dot/commands/tools.sh +21 -7
- package/scripts/fonts/install-nerd-fonts.sh +1 -1
- package/scripts/fonts/patch-fonts.sh +1 -1
- package/scripts/git-hooks/install.sh +1 -1
- package/scripts/git-hooks/pre-commit-audit.sh +2 -2
- package/scripts/lib/secrets_provider.sh +19 -4
- package/scripts/nvim/headless-upgrade.lua +81 -0
- package/scripts/ops/ai-setup.sh +1 -1
- package/scripts/ops/bundle.sh +1 -1
- package/scripts/ops/chaos.sh +1 -1
- package/scripts/ops/chezmoi-apply.sh +1 -1
- package/scripts/ops/chezmoi-diff.sh +1 -1
- package/scripts/ops/chezmoi-remove.sh +3 -3
- package/scripts/ops/chezmoi-update.sh +6 -2
- package/scripts/ops/heal-chezmoi.sh +1 -1
- package/scripts/ops/heal-system.sh +1 -1
- package/scripts/ops/heal-tools.sh +1 -1
- package/scripts/ops/heal.sh +1 -1
- package/scripts/ops/post-apply-repair.sh +1 -1
- package/scripts/ops/prewarm.sh +1 -1
- package/scripts/ops/release.sh +1 -1
- package/scripts/ops/rollback.sh +11 -2
- package/scripts/ops/setup.sh +1 -1
- package/scripts/ops/teleport.sh +1 -1
- package/scripts/qa/check-feature-matrix.sh +296 -0
- package/scripts/qa/check-version-consistency.sh +7 -119
- package/scripts/qa/coverage-baseline.sh +1 -1
- package/scripts/qa/docs-coverage.sh +9 -3
- package/scripts/qa/examples-coverage.sh +1 -1
- package/scripts/qa/reliability-audit.sh +1 -1
- package/scripts/qa/scorecard-snapshot.sh +1 -1
- package/scripts/qa/traceability-coverage.sh +9 -2
- package/scripts/qa/validate-examples.sh +66 -3
- package/scripts/qa/wsl-contract.sh +1 -1
- package/scripts/secrets/age-init.sh +1 -1
- package/scripts/secrets/create-secrets-file.sh +1 -1
- package/scripts/secrets/encrypt-ssh-key.sh +1 -1
- package/scripts/security/backup.sh +1 -1
- package/scripts/security/check-disclosure-key-expiry.sh +1 -1
- package/scripts/security/dns-doh.sh +1 -1
- package/scripts/security/encryption-check.sh +1 -1
- package/scripts/security/enforce-policies.sh +315 -98
- package/scripts/security/firewall.sh +1 -1
- package/scripts/security/lock-configs.sh +1 -1
- package/scripts/security/lock-screen.sh +1 -1
- package/scripts/security/manage-secrets.sh +1 -1
- package/scripts/security/ssh-cert.sh +1 -1
- package/scripts/security/telemetry-kill.sh +1 -1
- package/scripts/security/usb-safety.sh +1 -1
- package/scripts/theme/apply-gnome-theme.sh +1 -1
- package/scripts/theme/extract-heic-frames.sh +1 -1
- package/scripts/theme/extract-theme.py +284 -6
- package/scripts/theme/install-boot-logo.sh +1 -1
- package/scripts/theme/install-catppuccin-themes.sh +1 -1
- package/scripts/theme/install-cursors.sh +1 -1
- package/scripts/theme/install-file-icons.sh +1 -1
- package/scripts/theme/install-grub-theme.sh +1 -1
- package/scripts/theme/install-lock-icon.sh +1 -1
- package/scripts/theme/merge-wallpaper.sh +1 -1
- package/scripts/theme/rebuild-themes.sh +64 -5
- package/scripts/theme/switch.sh +46 -19
- package/scripts/theme/wallpaper-rotate.sh +1 -1
- package/scripts/theme/wallpaper-sync.sh +1 -1
- package/scripts/tools/cmatrix.sh +1 -1
- package/scripts/tools/detect-collisions.py +1 -1
- package/scripts/tools/emoji-picker.sh +1 -1
- package/scripts/tools/figlet-banner.sh +1 -1
- package/scripts/tools/log-rotate.sh +1 -1
- package/scripts/tools/lolcat-wrap.sh +1 -1
- package/scripts/tools/pipes.sh +1 -1
- package/scripts/tuning/linux.sh +1 -1
- package/scripts/tuning/macos.sh +1 -1
- package/scripts/uninstall.sh +1 -1
- package/scripts/verify-release-versions +156 -0
- package/scripts/version-sync.sh +63 -3
- package/LICENSE +0 -21
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
render_with_liquid: false
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<!-- SPDX-License-Identifier: Apache-2.0 OR MIT -->
|
|
6
|
+
<!-- Copyright (c) 2015-2026 Sebastien Rousseau -->
|
|
7
|
+
|
|
8
|
+
# Migrating from GNU Stow
|
|
9
|
+
|
|
10
|
+
Stow builds a symlink farm: `~/.bashrc` is a symlink into
|
|
11
|
+
`~/dotfiles/bash/.bashrc`. This framework renders real files into
|
|
12
|
+
`$HOME` from a source tree. That difference is the whole migration —
|
|
13
|
+
everything else follows from it.
|
|
14
|
+
|
|
15
|
+
Be honest about whether you want this. If your setup is "symlink a
|
|
16
|
+
dozen files on one machine", Stow is doing that job well and the
|
|
17
|
+
migration will not repay itself. The reason to move is templating,
|
|
18
|
+
multiple machines, or the tooling around the files.
|
|
19
|
+
|
|
20
|
+
## Concept mapping
|
|
21
|
+
|
|
22
|
+
| Stow | Here |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `~/dotfiles/<package>/` | `defaults/` (one tree, not per-package) |
|
|
25
|
+
| `stow bash` | `dot sync` (applies everything) |
|
|
26
|
+
| `stow -D bash` | `chezmoi forget <file>`, then `dot sync` |
|
|
27
|
+
| `stow -n` (simulate) | `dot diff` or `dot sync --check` |
|
|
28
|
+
| `.stow-local-ignore` | `.chezmoiignore` |
|
|
29
|
+
| Symlinks in `$HOME` | Real files in `$HOME` |
|
|
30
|
+
| Per-host: separate packages or branches | `.tmpl` files with `{{ if }}` |
|
|
31
|
+
|
|
32
|
+
## 1. Record what Stow currently owns
|
|
33
|
+
|
|
34
|
+
Symlinks are self-describing, which makes this the easiest inventory
|
|
35
|
+
of any migration:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
find "$HOME" -maxdepth 3 -type l -lname "*dotfiles*" \
|
|
39
|
+
-printf '%p -> %l\n' 2>/dev/null | tee ~/stow-inventory.txt
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
On macOS (BSD `find` has no `-printf`):
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
find "$HOME" -maxdepth 3 -type l | while read -r l; do
|
|
46
|
+
case "$(readlink "$l")" in *dotfiles*) echo "$l -> $(readlink "$l")";; esac
|
|
47
|
+
done | tee ~/stow-inventory.txt
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Back up the real content, following the links:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
tar -czhf ~/stow-backup-$(date +%F).tar.gz -C "$HOME" \
|
|
54
|
+
$(sed 's| ->.*||; s|^'"$HOME"'/||' ~/stow-inventory.txt)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`-h` is the important flag: it dereferences symlinks and archives the
|
|
58
|
+
actual files.
|
|
59
|
+
|
|
60
|
+
## 2. Unstow before installing
|
|
61
|
+
|
|
62
|
+
This is the step people skip, and it is the one that matters. If you
|
|
63
|
+
install over a live symlink farm, `chezmoi apply` will try to replace
|
|
64
|
+
symlinks with regular files and the result is confusing.
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
cd ~/dotfiles
|
|
68
|
+
stow -D */ # remove every package's links
|
|
69
|
+
find "$HOME" -maxdepth 3 -type l -lname "*dotfiles*" 2>/dev/null # expect empty
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Your `~/dotfiles` directory still holds the real files. Nothing is lost.
|
|
73
|
+
|
|
74
|
+
## 3. Install
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
bash -c "$(curl -fsSL https://raw.githubusercontent.com/sebastienrousseau/dotfiles/main/install.sh)"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 4. Import your files
|
|
81
|
+
|
|
82
|
+
Stow packages flatten into a single tree. Copy each package's contents
|
|
83
|
+
into `$HOME` and add them:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
cd ~/dotfiles
|
|
87
|
+
for pkg in */; do
|
|
88
|
+
pkg="${pkg%/}"
|
|
89
|
+
echo "== $pkg"
|
|
90
|
+
( cd "$pkg" && find . -type f -print0 ) | while IFS= read -r -d '' rel; do
|
|
91
|
+
rel="${rel#./}"
|
|
92
|
+
mkdir -p "$(dirname "$HOME/$rel")"
|
|
93
|
+
cp "$pkg/$rel" "$HOME/$rel"
|
|
94
|
+
dot add "$HOME/$rel"
|
|
95
|
+
done
|
|
96
|
+
done
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Review before committing anything — this is a good moment to drop the
|
|
100
|
+
files you have not opened in three years:
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
dot status
|
|
104
|
+
cd "$(dot cd)" && git status
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 5. Replace per-host packages with templates
|
|
108
|
+
|
|
109
|
+
If you had `bash-work/` and `bash-home/` packages, collapse them:
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
{{ if eq .chezmoi.hostname "work-laptop" }}
|
|
113
|
+
export HTTP_PROXY=http://proxy.corp:3128
|
|
114
|
+
{{ end }}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Available variables: `.chezmoi.os`, `.chezmoi.arch`, `.chezmoi.hostname`,
|
|
118
|
+
`.chezmoi.username`, `.chezmoi.osRelease.id`, plus this project's
|
|
119
|
+
`.profile` and `.features.*` from `defaults/.chezmoidata.toml`.
|
|
120
|
+
|
|
121
|
+
Test a template without applying:
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
chezmoi execute-template < defaults/dot_bashrc.tmpl
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## 6. Apply and verify
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
dot diff
|
|
131
|
+
dot sync
|
|
132
|
+
dot doctor
|
|
133
|
+
|
|
134
|
+
# Nothing should be a symlink into ~/dotfiles any more:
|
|
135
|
+
find "$HOME" -maxdepth 3 -type l -lname "*dotfiles*" 2>/dev/null
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## 7. Retire the old tree
|
|
139
|
+
|
|
140
|
+
After a few days:
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
mv ~/dotfiles ~/dotfiles.stow.bak # rename, do not delete
|
|
144
|
+
# delete ~/dotfiles.stow.bak once you are sure
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Rolling back
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
bash ~/.dotfiles/scripts/uninstall.sh --force
|
|
151
|
+
mv ~/dotfiles.stow.bak ~/dotfiles # if you renamed it
|
|
152
|
+
cd ~/dotfiles && stow */
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## What you gain, what you lose
|
|
156
|
+
|
|
157
|
+
**Gain:** templating (the thing Stow structurally cannot do); one tree
|
|
158
|
+
instead of a package-per-tool layout; the `dot` CLI; multi-shell
|
|
159
|
+
parity; secrets management; signed releases.
|
|
160
|
+
|
|
161
|
+
**Lose:** the elegance of the symlink model — editing `~/.bashrc` no
|
|
162
|
+
longer edits your repo directly, so you edit the source and run
|
|
163
|
+
`dot sync` (or `dot add` after the fact). Stow is ~5k lines of Perl
|
|
164
|
+
with essentially no runtime dependencies; this pulls in chezmoi. And
|
|
165
|
+
Stow's mental model fits in a paragraph, which has real value.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
render_with_liquid: false
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<!-- SPDX-License-Identifier: Apache-2.0 OR MIT -->
|
|
6
|
+
<!-- Copyright (c) 2015-2026 Sebastien Rousseau -->
|
|
7
|
+
|
|
8
|
+
# Migrating from plain chezmoi
|
|
9
|
+
|
|
10
|
+
The shortest migration here, because there is no engine change: this
|
|
11
|
+
framework *is* chezmoi, plus a source-tree layout, a CLI, and the
|
|
12
|
+
tooling around them. Your templates, your `.chezmoidata`, and your
|
|
13
|
+
muscle memory all carry over.
|
|
14
|
+
|
|
15
|
+
You can also stop after step 2 and keep using `chezmoi` commands
|
|
16
|
+
directly forever. Nothing here replaces chezmoi; `dot` shells out to it.
|
|
17
|
+
|
|
18
|
+
## What actually changes
|
|
19
|
+
|
|
20
|
+
| Plain chezmoi | Here |
|
|
21
|
+
|---|---|
|
|
22
|
+
| Source at `~/.local/share/chezmoi` | Source at `~/.dotfiles`, with `.chezmoiroot` pointing chezmoi at `defaults/` |
|
|
23
|
+
| Everything at the source root | `defaults/` = your files; `bin/`, `lib/`, `scripts/`, `docs/`, `tests/` = the framework |
|
|
24
|
+
| `chezmoi apply` | `dot sync` (which runs `chezmoi apply` with sensible exclusions) |
|
|
25
|
+
| `chezmoi diff` / `status` | `dot diff` / `dot status` — same thing, prettier |
|
|
26
|
+
| — | `dot doctor`, `dot health`, `dot heal`, `dot rollback`, `dot attest` |
|
|
27
|
+
| `chezmoi secret` | `dot secrets` (age + sops) |
|
|
28
|
+
| `run_onchange_` scripts | same, plus `install/provision/` |
|
|
29
|
+
|
|
30
|
+
**`.chezmoiroot` is the key idea.** It contains `defaults`, so chezmoi
|
|
31
|
+
treats `~/.dotfiles/defaults/` as the source root. Framework code sits
|
|
32
|
+
outside that directory and is therefore never deployed to `$HOME`.
|
|
33
|
+
|
|
34
|
+
## 1. Back up and record
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
chezmoi source-path
|
|
38
|
+
chezmoi managed > ~/chezmoi-managed.txt
|
|
39
|
+
wc -l ~/chezmoi-managed.txt
|
|
40
|
+
|
|
41
|
+
cp -a "$(chezmoi source-path)" ~/chezmoi-source-backup-$(date +%F)
|
|
42
|
+
tar -czf ~/chezmoi-backup-$(date +%F).tar.gz -C "$HOME" -T ~/chezmoi-managed.txt
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Commit and push your existing source repo if it has a remote. That is
|
|
46
|
+
the real backup.
|
|
47
|
+
|
|
48
|
+
## 2. Two paths
|
|
49
|
+
|
|
50
|
+
**A. Keep your own repo** (you have a dotfiles repo you want to keep
|
|
51
|
+
as the source of truth): install the framework, then move your files
|
|
52
|
+
into `defaults/`.
|
|
53
|
+
|
|
54
|
+
**B. Adopt this repo's defaults** and layer your changes on top:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
bash -c "$(curl -fsSL https://raw.githubusercontent.com/sebastienrousseau/dotfiles/main/install.sh)"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Path A continues below.
|
|
61
|
+
|
|
62
|
+
## 3. Install and relocate your source tree
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
old_source="$(chezmoi source-path)"
|
|
66
|
+
|
|
67
|
+
bash -c "$(curl -fsSL https://raw.githubusercontent.com/sebastienrousseau/dotfiles/main/install.sh)"
|
|
68
|
+
|
|
69
|
+
# Copy your source files under defaults/, preserving chezmoi's naming.
|
|
70
|
+
rsync -av --exclude '.git' "$old_source/" ~/.dotfiles/defaults/
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Then reconcile the chezmoi-metadata files, which now exist in both
|
|
74
|
+
places — merge yours into the framework's rather than overwriting:
|
|
75
|
+
|
|
76
|
+
| File | What to do |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `.chezmoidata.toml` | **Merge.** The framework's carries `dotfiles_version`, `profile`, and `features`; keep those and add your keys. |
|
|
79
|
+
| `.chezmoiignore` | Merge; the framework's entries are feature-flag-gated. |
|
|
80
|
+
| `.chezmoi.toml.tmpl` | Merge your prompts into the framework's. |
|
|
81
|
+
| `.chezmoitemplates/` | Merge; name collisions are the only risk. |
|
|
82
|
+
| `.chezmoiscripts/`, `run_*` | Copy across as-is. |
|
|
83
|
+
|
|
84
|
+
Sanity-check the merge before applying anything:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
cd ~/.dotfiles
|
|
88
|
+
chezmoi --source "$PWD" data | head -40
|
|
89
|
+
chezmoi --source "$PWD" managed | head
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 4. Preview, then apply
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
dot diff # or: chezmoi diff — identical underneath
|
|
96
|
+
dot sync --check # dry run
|
|
97
|
+
dot sync
|
|
98
|
+
dot doctor
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Confirm nothing was dropped:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
while read -r f; do
|
|
105
|
+
[ -e "$f" ] || echo "MISSING: $f"
|
|
106
|
+
done < ~/chezmoi-managed.txt
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 5. Optional: adopt the extras
|
|
110
|
+
|
|
111
|
+
None of these are required; adopt them when you want them.
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
dot secrets-init # age-based secrets
|
|
115
|
+
dot theme list # wallpaper-driven theming
|
|
116
|
+
dot profile show # profiles and feature flags
|
|
117
|
+
dot completion fish > ~/.config/fish/completions/dot.fish
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Rolling back
|
|
121
|
+
|
|
122
|
+
The gentlest rollback of any of these guides, because chezmoi is still
|
|
123
|
+
the engine:
|
|
124
|
+
|
|
125
|
+
```sh
|
|
126
|
+
chezmoi --source ~/chezmoi-source-backup-<date> apply
|
|
127
|
+
bash ~/.dotfiles/scripts/uninstall.sh --force
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Or simply point chezmoi back at your old source and carry on:
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
chezmoi init --source ~/chezmoi-source-backup-<date>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## What you gain, what you lose
|
|
137
|
+
|
|
138
|
+
**Gain:** the `dot` CLI (diagnostics, repair, rollback, attestation,
|
|
139
|
+
fleet); a curated multi-shell configuration with parity across bash,
|
|
140
|
+
zsh, fish, nushell and PowerShell; secrets, theming and provisioning
|
|
141
|
+
already wired; a test suite and CI you can run against your own
|
|
142
|
+
changes; signed, attested releases.
|
|
143
|
+
|
|
144
|
+
**Lose:** simplicity. Plain chezmoi is one binary and your files;
|
|
145
|
+
this adds a source-tree convention, a CLI layer, and opinions about
|
|
146
|
+
shell configuration you may not share. If you already have a chezmoi
|
|
147
|
+
setup you are happy with, "it works and I understand every line" is a
|
|
148
|
+
perfectly good reason to stay.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
---
|
|
2
|
+
render_with_liquid: false
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<!-- SPDX-License-Identifier: Apache-2.0 OR MIT -->
|
|
6
|
+
<!-- Copyright (c) 2015-2026 Sebastien Rousseau -->
|
|
7
|
+
|
|
8
|
+
# Migrating from yadm
|
|
9
|
+
|
|
10
|
+
yadm is the closest neighbour: both track real files in `$HOME` with
|
|
11
|
+
git, both template per host, both encrypt secrets. The migration is
|
|
12
|
+
mostly a mechanical re-home of files plus a translation of yadm's
|
|
13
|
+
alternate-file suffixes into chezmoi templates.
|
|
14
|
+
|
|
15
|
+
## Concept mapping
|
|
16
|
+
|
|
17
|
+
| yadm | Here | Note |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `yadm add` / `yadm commit` | `dot add` then a normal `git commit` in the source dir | Source tree is a plain git repo |
|
|
20
|
+
| `$HOME` **is** the work tree | Files live in `defaults/` and are *rendered* into `$HOME` | The big conceptual change |
|
|
21
|
+
| `##os.Linux`, `##hostname.foo` suffixes | `{{ if eq .chezmoi.os "linux" }}` in a `.tmpl` | One file with branches, not N files |
|
|
22
|
+
| `yadm alt` | `chezmoi apply` | Automatic; no separate step |
|
|
23
|
+
| `yadm encrypt` (GPG) | `dot secrets` (age + sops) | Different crypto; see below |
|
|
24
|
+
| `yadm bootstrap` | `install/provision/run_onchange_*` | Runs on apply, idempotent |
|
|
25
|
+
| `yadm status` / `diff` | `dot status` / `dot diff` | Same intent |
|
|
26
|
+
|
|
27
|
+
## 1. Inventory and back up
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
yadm list -a > ~/yadm-inventory.txt
|
|
31
|
+
wc -l ~/yadm-inventory.txt
|
|
32
|
+
tar -czf ~/yadm-backup-$(date +%F).tar.gz -C "$HOME" -T ~/yadm-inventory.txt
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Keep `yadm-inventory.txt`: it is your checklist, and step 5 diffs
|
|
36
|
+
against it.
|
|
37
|
+
|
|
38
|
+
## 2. Install alongside yadm
|
|
39
|
+
|
|
40
|
+
The installer does not touch yadm's repo (`~/.local/share/yadm/repo.git`)
|
|
41
|
+
or its config, so both can coexist while you migrate.
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
bash -c "$(curl -fsSL https://raw.githubusercontent.com/sebastienrousseau/dotfiles/main/install.sh)"
|
|
45
|
+
dot version
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If you want to bring your *own* dotfiles repo rather than the
|
|
49
|
+
maintainer's defaults, use `dot init` instead:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
dot init yourusername --dry-run # prints the resolved URL and target dir
|
|
53
|
+
dot init yourusername
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 3. Move files across
|
|
57
|
+
|
|
58
|
+
Plain (non-alternate) files first:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
while read -r f; do
|
|
62
|
+
case "$f" in
|
|
63
|
+
*##*) continue ;; # alternates handled in step 4
|
|
64
|
+
esac
|
|
65
|
+
dot add "$f"
|
|
66
|
+
done < ~/yadm-inventory.txt
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`dot add` wraps `chezmoi add`, so a file in `~/.config/foo/bar` lands
|
|
70
|
+
at `defaults/dot_config/foo/bar`, and `~/.gitconfig` at
|
|
71
|
+
`defaults/dot_gitconfig`.
|
|
72
|
+
|
|
73
|
+
## 4. Translate alternates into templates
|
|
74
|
+
|
|
75
|
+
For each `##`-suffixed file, replace the family with one `.tmpl`.
|
|
76
|
+
|
|
77
|
+
yadm:
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
~/.gitconfig##os.Darwin
|
|
81
|
+
~/.gitconfig##os.Linux
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Here — `defaults/dot_gitconfig.tmpl`:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
[user]
|
|
88
|
+
name = {{ .name }}
|
|
89
|
+
email = {{ .email }}
|
|
90
|
+
{{ if eq .chezmoi.os "darwin" }}
|
|
91
|
+
[credential]
|
|
92
|
+
helper = osxkeychain
|
|
93
|
+
{{ else if eq .chezmoi.os "linux" }}
|
|
94
|
+
[credential]
|
|
95
|
+
helper = cache --timeout=3600
|
|
96
|
+
{{ end }}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The common yadm conditions map as:
|
|
100
|
+
|
|
101
|
+
| yadm suffix | chezmoi expression |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `##os.Darwin` | `eq .chezmoi.os "darwin"` |
|
|
104
|
+
| `##os.Linux` | `eq .chezmoi.os "linux"` |
|
|
105
|
+
| `##hostname.foo` | `eq .chezmoi.hostname "foo"` |
|
|
106
|
+
| `##user.alice` | `eq .chezmoi.username "alice"` |
|
|
107
|
+
| `##distro.Ubuntu` | `eq .chezmoi.osRelease.id "ubuntu"` |
|
|
108
|
+
| `##default` | the `{{ else }}` branch |
|
|
109
|
+
|
|
110
|
+
Verify before applying — templates fail loudly, but only when rendered:
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
chezmoi execute-template < defaults/dot_gitconfig.tmpl
|
|
114
|
+
dot diff
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## 5. Migrate encrypted files
|
|
118
|
+
|
|
119
|
+
yadm uses GPG; this framework uses age via sops. There is no automatic
|
|
120
|
+
converter, and that is deliberate — re-encrypting secrets is a step
|
|
121
|
+
you should perform consciously.
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
# Decrypt with yadm, in a directory that is not the repo.
|
|
125
|
+
mkdir -p /tmp/secrets-migration && cd /tmp/secrets-migration
|
|
126
|
+
yadm decrypt
|
|
127
|
+
|
|
128
|
+
# Set up age and re-encrypt.
|
|
129
|
+
dot secrets-init # generates the age key
|
|
130
|
+
dot secrets-create # creates the encrypted store
|
|
131
|
+
dot secrets set GITHUB_TOKEN # one per secret, prompts for the value
|
|
132
|
+
dot secrets list
|
|
133
|
+
|
|
134
|
+
cd - && rm -rf /tmp/secrets-migration
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Details and the provider model: [`../security/SECRETS.md`](../security/SECRETS.md).
|
|
138
|
+
|
|
139
|
+
## 6. Apply and verify
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
dot diff # review every pending change
|
|
143
|
+
dot sync # apply
|
|
144
|
+
dot doctor # health check
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Then diff against the inventory to catch anything missed:
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
while read -r f; do
|
|
151
|
+
[ -e "$HOME/$f" ] || echo "MISSING: $f"
|
|
152
|
+
done < ~/yadm-inventory.txt
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## 7. Retire yadm
|
|
156
|
+
|
|
157
|
+
Only after a full working day on the new setup:
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
yadm list -a > ~/yadm-final-check.txt # last snapshot, keep it
|
|
161
|
+
rm -rf ~/.local/share/yadm
|
|
162
|
+
rm -rf ~/.config/yadm
|
|
163
|
+
# and remove the yadm package via your package manager
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Rolling back
|
|
167
|
+
|
|
168
|
+
At any point before step 7, yadm is untouched:
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
bash ~/.dotfiles/scripts/uninstall.sh --force
|
|
172
|
+
tar -xzf ~/yadm-backup-<date>.tar.gz -C "$HOME"
|
|
173
|
+
yadm status
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## What you gain, what you lose
|
|
177
|
+
|
|
178
|
+
**Gain:** one templated file instead of alternate families; the `dot`
|
|
179
|
+
CLI (`doctor`, `health`, `heal`, `rollback`, `attest`); multi-shell
|
|
180
|
+
parity including fish, nushell and PowerShell; signed and attested
|
|
181
|
+
releases; fleet commands for more than one machine.
|
|
182
|
+
|
|
183
|
+
**Lose:** `$HOME` is no longer a git work tree, so `git status` in your
|
|
184
|
+
home directory stops being meaningful — you use `dot status` instead.
|
|
185
|
+
GPG-encrypted files become age/sops. yadm is a single ~2k-line script;
|
|
186
|
+
this is a larger surface. And yadm's bootstrap is one file, whereas
|
|
187
|
+
provisioning here is spread across `install/provision/`.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
---
|
|
2
|
+
render_with_liquid: false
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Performance Budgets
|
|
6
|
+
|
|
7
|
+
Every operation this repo owns falls into one of four performance tiers. Each tier has a **hard budget** enforced by `benches/test_perf_budgets.sh`. If a change pushes an operation past its budget's headroom, CI fails.
|
|
8
|
+
|
|
9
|
+
## The tiers
|
|
10
|
+
|
|
11
|
+
| Tier | Budget | What belongs here |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| **INSTANT** | ≤ **500ms** | Anything a human sees at a shell prompt |
|
|
14
|
+
| **FAST** | ≤ **2000ms** | Heavier gates + sandboxed CLI reads |
|
|
15
|
+
| **MEDIUM** | ≤ **5000ms** | Full diagnostic runs |
|
|
16
|
+
| **ACCEPTED-SLOW** | documented | Multi-second ops that are legitimately slow (see below) |
|
|
17
|
+
| **OUT-OF-SCOPE** | not gated | Multi-minute ops we don't gate per-run |
|
|
18
|
+
|
|
19
|
+
## Reference baselines (2026-08-30, `rousseau-cachyos-geekom-a9`, Ryzen AI 9 HX 370)
|
|
20
|
+
|
|
21
|
+
All medians in milliseconds. Budget = 2× median (or the tier ceiling, whichever is larger).
|
|
22
|
+
|
|
23
|
+
### INSTANT tier
|
|
24
|
+
|
|
25
|
+
| Operation | Baseline | Budget | Headroom |
|
|
26
|
+
|---|---:|---:|---:|
|
|
27
|
+
| `dot version` | 10 | 500 | 50× |
|
|
28
|
+
| `dot help` | 13 | 500 | 38× |
|
|
29
|
+
| `dot help <cmd>` | 15 | 500 | 33× |
|
|
30
|
+
| `dot search <keyword>` | 15 | 500 | 33× |
|
|
31
|
+
| iCloud script single-run | 15 | 500 | 33× |
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### FAST tier
|
|
35
|
+
|
|
36
|
+
Currently empty.
|
|
37
|
+
|
|
38
|
+
`dot status` and `dot diff` were placed here at 2000ms against reference
|
|
39
|
+
medians of 1510 ms and 1420 ms — 1.3× and 1.4× headroom, short of the ≥ 2×
|
|
40
|
+
this document requires of every budget. They have since moved to MEDIUM; see
|
|
41
|
+
that section for the measurements that prompted it.
|
|
42
|
+
|
|
43
|
+
> The baselines of 28 ms and 32 ms recorded before those were not real. The
|
|
44
|
+
> perf sandbox never created chezmoi's source directory, so both commands
|
|
45
|
+
> aborted immediately with *"no such file or directory"* — and `_measure`
|
|
46
|
+
> discarded exit codes, so the gate timed the failure path and called it
|
|
47
|
+
> excellent. The sandbox now links the repo into `XDG_DATA_HOME`.
|
|
48
|
+
|
|
49
|
+
The QA gates and test suites that used to sit in this tier moved to GATES
|
|
50
|
+
below: they are not interactive operations, so a ceiling defined by
|
|
51
|
+
human-perceived latency never described them.
|
|
52
|
+
|
|
53
|
+
### MEDIUM tier
|
|
54
|
+
|
|
55
|
+
| Operation | Baseline | Budget | Headroom |
|
|
56
|
+
|---|---:|---:|---:|
|
|
57
|
+
| `dot status` (sandbox) | 2329 | 5000 | 2.1× |
|
|
58
|
+
| `dot diff` (sandbox) | 2157 | 5000 | 2.3× |
|
|
59
|
+
| `dot doctor` | 3892 | 5000 | 1.3× |
|
|
60
|
+
| `bench.sh --quick` | 826 | 5000 | 6× |
|
|
61
|
+
|
|
62
|
+
The `dot status` and `dot diff` baselines are the **worst** of the two hosted
|
|
63
|
+
runners, not the reference machine: macOS measured 2290 ms / 2157 ms and Ubuntu
|
|
64
|
+
2329 ms / 2136 ms, against 1139 ms / 1174 ms locally. Two unrelated platforms
|
|
65
|
+
agreeing within 8% is a property of the commands rather than of one slow
|
|
66
|
+
runner — both shell out to chezmoi, which walks the whole source tree, and that
|
|
67
|
+
is disk-bound. A budget taken from the faster machine would have been a gate
|
|
68
|
+
that only ever fired on other people's hardware.
|
|
69
|
+
|
|
70
|
+
`dot doctor` and `bench.sh --quick` are diagnostics that report findings through
|
|
71
|
+
their exit status — `dot doctor` exits 1 whenever it finds issues, and `bench.sh` exits 1
|
|
72
|
+
when a shell breaches its own startup threshold. They are gated with
|
|
73
|
+
`_gate_diag`, which permits exit 1 but still fails on 2+ (not-found,
|
|
74
|
+
permission, signal, syntax error). That is far narrower than the blanket
|
|
75
|
+
`|| true` it replaced.
|
|
76
|
+
|
|
77
|
+
### GATES tier (CI quality gates and test suites)
|
|
78
|
+
|
|
79
|
+
Budgets are 2× the median measured on the **slowest supported platform**,
|
|
80
|
+
not the fastest. Medians below: `rousseau-mbp-m1`, macOS 26 (Darwin 25.6),
|
|
81
|
+
2026-08-30.
|
|
82
|
+
|
|
83
|
+
| Operation | macOS median | Linux median | Budget | Headroom |
|
|
84
|
+
|---|---:|---:|---:|---:|
|
|
85
|
+
| `check-version-consistency.sh` | 68 | 14 | 500 | 7.4× |
|
|
86
|
+
| `docs-coverage.sh` | 969 | 148 | 2000 | 2.1× |
|
|
87
|
+
| iCloud regression test (12 assertions) | 494 | 94 | 1000 | 2.0× |
|
|
88
|
+
| iCloud unit test (29 assertions) | 1743 | 498 | 3500 | 2.0× |
|
|
89
|
+
| iCloud manifest test (11 assertions) | 1575 | — † | 5000 | 3.2× |
|
|
90
|
+
| `traceability-coverage.sh` | 2389 | 623 | 5000 | 2.1× |
|
|
91
|
+
| `test_dot_subcommand_smoke.sh` | 3785 | 1308 | 7500 | 2.0× |
|
|
92
|
+
| `test_dot_help_registry_symmetry.sh` | 4449 | 1381 | 9000 | 2.0× |
|
|
93
|
+
|
|
94
|
+
† The manifest test hashes an entire sandbox tree before and after every
|
|
95
|
+
scenario, so it is disk-bound in a way the other gates are not. Its budget is
|
|
96
|
+
set at 3.2× the macOS median rather than the 2.0× used above, deliberately:
|
|
97
|
+
`dot status` and `dot diff` were first budgeted at 1.3× and 1.4×, and both
|
|
98
|
+
breached on the first CI run that measured them. The Linux median is left
|
|
99
|
+
unfilled until CI reports one — guessing it would defeat the point of a table
|
|
100
|
+
of measurements.
|
|
101
|
+
|
|
102
|
+
These gates run **3–6× slower on macOS than on Linux** — fork/exec is markedly
|
|
103
|
+
more expensive there and every one of them is fork-heavy shell. CI covers both
|
|
104
|
+
platforms, so the original Linux-only calibration could not hold, and five of
|
|
105
|
+
these sat red as a result. Re-capture the Linux column when convenient; it is
|
|
106
|
+
carried over from the 2026-08-30 Ryzen baseline and is not the binding
|
|
107
|
+
constraint.
|
|
108
|
+
|
|
109
|
+
### ACCEPTED-SLOW (documented, not gated per-run)
|
|
110
|
+
|
|
111
|
+
| Operation | Baseline | Reason |
|
|
112
|
+
|---|---:|---|
|
|
113
|
+
| `test_dot_help_flag_universal.sh` | ~11s | Invokes `dot help --help` on ~100 commands via subshell each. The coverage it provides justifies the cost; regression is caught by `benches/test_help_gates_wall_clock.sh` at the suite level. |
|
|
114
|
+
|
|
115
|
+
### OUT-OF-SCOPE (not gated per-run)
|
|
116
|
+
|
|
117
|
+
| Operation | Why we don't gate |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `chezmoi apply` | Fresh macOS: minutes. Depends on iCloud sync + package installs. Gated at suite level only. |
|
|
120
|
+
| `install.sh` full | Downloads + installs packages. Network-bound. |
|
|
121
|
+
| `dot upgrade` | Runs `mise upgrade`, `chezmoi apply`, package manager upgrades. |
|
|
122
|
+
| Full test suite | 15+ minutes on CI. Gated by workflow timeout, not per-run assertion. |
|
|
123
|
+
|
|
124
|
+
## Ratchet vs aspiration
|
|
125
|
+
|
|
126
|
+
The budgets above are **regression gates**, not aspirations. If a real optimisation lowers a baseline, edit the doc + the perf test to lower the budget too. If a change pushes something over the budget, the test fails and CI blocks the merge.
|
|
127
|
+
|
|
128
|
+
The aspirational shell-startup target (`<30ms`) is tracked separately in `benches/bench.sh` — that's a bench, not a budget.
|
|
129
|
+
|
|
130
|
+
## Adding a new operation
|
|
131
|
+
|
|
132
|
+
When you add a new script that runs at a shell prompt:
|
|
133
|
+
|
|
134
|
+
1. Time it 5 runs on a warm system: `for _ in {1..5}; do time bash your-script; done`
|
|
135
|
+
2. Take the median.
|
|
136
|
+
3. Place it in the tier where `budget ≥ 2 × median`. If a median is 400ms it goes in FAST (500ms is uncomfortably tight); if it's 300ms, INSTANT is fine.
|
|
137
|
+
4. Add it to `benches/test_perf_budgets.sh` in the correct tier section.
|
|
138
|
+
5. Add its baseline to this doc.
|
|
139
|
+
|
|
140
|
+
## Where the enforcement lives
|
|
141
|
+
|
|
142
|
+
- **Per-op budget test**: `benches/test_perf_budgets.sh`
|
|
143
|
+
- **Suite-level wall-clock ratchet**: `benches/test_help_gates_wall_clock.sh`
|
|
144
|
+
- **CI wiring**: `.github/workflows/ci.yml`, job `quality-performance` — runs on
|
|
145
|
+
ubuntu-latest and macos-latest for every PR, with no `|| true` and no budget
|
|
146
|
+
scaling. On the gates the hosted macOS runner is comparable to or faster than
|
|
147
|
+
the reference machine — docs-coverage 679 ms vs 969, traceability 1775 vs
|
|
148
|
+
2389, help-registry 3318 vs 4449, `dot doctor` 1746 vs 4423. The exception is
|
|
149
|
+
anything that drives chezmoi over the whole source tree: `dot status` and
|
|
150
|
+
`dot diff` measured ~2× the local median on **both** hosted platforms, which
|
|
151
|
+
is why they sit in MEDIUM rather than FAST. Budget a new operation against the
|
|
152
|
+
slowest platform it will run on, not against this machine.
|
|
153
|
+
|
|
154
|
+
### How the time is measured
|
|
155
|
+
|
|
156
|
+
`_measure` uses the `time` keyword with `TIMEFORMAT='%3R'`, not `date +%s%N`.
|
|
157
|
+
|
|
158
|
+
`%N` is a GNU extension. BSD `date` — macOS 14 and earlier — copies the literal
|
|
159
|
+
`N` through, so every arithmetic conversion failed, `_measure` returned nothing,
|
|
160
|
+
and an empty median compares as `0` against any budget. The gate reported every
|
|
161
|
+
budget met on those machines, for any command, including one that could not
|
|
162
|
+
parse. `time` is a shell builtin, is millisecond-accurate in bash 3.2, and needs
|
|
163
|
+
no external clock at all.
|
|
164
|
+
|
|
165
|
+
Two consequences worth keeping:
|
|
166
|
+
|
|
167
|
+
- `_gate_max_rc` rejects a non-numeric median outright rather than comparing it.
|
|
168
|
+
Both fields go through `-gt` / `-le`, where bash reads a non-numeric operand
|
|
169
|
+
as `0` — so a gate that cannot measure would otherwise report success.
|
|
170
|
+
- `tests/regression/test_gate_integrity.sh` puts a `date` without `%N` first on
|
|
171
|
+
`PATH` and requires the same verdicts. A future timing rewrite that
|
|
172
|
+
reintroduces the dependency fails there rather than going quiet.
|
|
173
|
+
|
|
174
|
+
## Environment knobs
|
|
175
|
+
|
|
176
|
+
| Variable | Default | Effect |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| `PERF_BUDGET_PERCENT` | `100` | Scales every budget. `0` makes them all impossible — that is how `test_gate_integrity.sh` proves the gate actually fires. |
|
|
179
|
+
| `PERF_GATE_FILTER` | *(unset)* | Runs only gates whose label contains this substring. Skipped gates never call `test_start`, so `TESTS_RUN == PASSED + FAILED` still holds. |
|
|
180
|
+
| `DOT_CLI` | `bin/dot` | Points the `dot` gates at another binary, so breakage detection can be exercised against a deliberately corrupted CLI. |
|
|
181
|
+
|
|
182
|
+
## When a budget fires
|
|
183
|
+
|
|
184
|
+
The error looks like:
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
✗ instant_dot_help: median=612ms EXCEEDS budget=500ms
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Steps:
|
|
191
|
+
|
|
192
|
+
1. Bisect the change that pushed it over.
|
|
193
|
+
2. Fix the regression, or
|
|
194
|
+
3. If the increase is legitimate (real new work), move the operation to the next tier + update this doc + `test_perf_budgets.sh` in the same PR.
|
|
195
|
+
|
|
196
|
+
**Never silently bump the budget.** The tier a thing lives in is a promise to users.
|