Managing Chezmoi Packages
Use this skill when managing packages, external dependencies, binaries, or CLI tools in a chezmoi dotfiles repository. Handles adding/updating/removing packages in .chezmoidata/packages.yaml, creating .chezmoiexternals/ organized files, pinning versions with Renovate automation, and selecting the correct package ecosystem (Homebrew, mise, Python, Docker, chezmoi externals).
npx skills add github:pantheon-org/tekhne --skill specialized--chezmoi--managing-chezmoi-packagesThis skill handles all aspects of package and dependency management in chezmoi dotfiles repositories, with emphasis on security, reproducibility, and automated updates.
Core Principles
- Version Pinning: Always pin to immutable references (commit SHAs, specific versions, digests)
- Renovate Automation: Ensure all dependencies have corresponding Renovate rules
- Organization: Group externals by primary program using
.chezmoiexternals/directory - Minimal Edits: Make focused changes without unrelated modifications
- Preview First: Show proposed changes before applying
When to Use This Skill
Invoke this skill when the user requests:
- Adding/updating/removing packages or tools
- Managing chezmoi external dependencies
- Pinning versions or updating SHAs
- Organizing external files by program
- Setting up Renovate automation for dependencies
- Choosing between package ecosystems (Homebrew, mise, etc.)
Workflow
1. Identify the Request
Determine what the user wants:
- Add: New package or external dependency
- Update: Change version/SHA of existing package
- Remove: Delete package or external
- Organize: Migrate externals to
.chezmoiexternals/structure - Pin: Add immutable version/SHA reference
2. Select the Ecosystem
Choose the appropriate package manager (see ./ecosystem-guide.md for details):
| Use Case | Ecosystem | Location |
|---|---|---|
| macOS packages (Homebrew) | brew/cask/mas | home/.chezmoidata/packages.yaml |
| CLI developer tools | mise (Aqua) | .mise.toml or home/dot_config/mise/config.toml |
| Python tools | pip | home/dot_config/dotfiles/requirements.txt |
| Docker/devcontainer | docker | home/dot_config/docker-compose/*.yml, .devcontainer/ |
| External files/repos | chezmoi externals | home/.chezmoiexternals/*.toml[.tmpl] |
| Install script CLIs | cli-versions | home/dot_config/dotfiles/cli-versions.toml |
3. For Chezmoi Externals: Use .chezmoiexternals/ Directory
IMPORTANT: Organize externals by primary program using the .chezmoiexternals/ directory structure.
Directory Structure
home/.chezmoiexternals/
├── zsh.externals.toml # Zsh plugins and tools
├── bat.externals.toml # Bat themes and syntaxes
├── tmux.externals.toml # Tmux configurations
└── nvim.externals.toml # Neovim plugins
How It Works
- Files in
home/.chezmoiexternals/are treated as.chezmoiexternal.<format>files - Each file defines externals for a specific program
- Use
.tmplsuffix for templating support (OS conditionals, variables) - Paths in each file are relative to the home directory
Example: zsh.externals.toml
{{ if lookPath "zsh" }}
# Zsh plugins and tools
[".zsh/znap/zsh-snap"]
type = "git-repo"
url = "https://github.com/marlonrichert/zsh-snap.git"
revision = "25754a45d9ceafe6d7d082c9ebe40a08cb85a4f0"
refreshPeriod = "168h"
[".zsh/plugins/zsh-autosuggestions"]
{{ end }}type = "git-repo"
url = "https://github.com/zsh-users/zsh-autosuggestions.git"
revision = "85919cd1ffa7d2d5412f6d3fe437ebdbeeec4fc5"
refreshPeriod = "168h"
4. External Types and Patterns
Chezmoi supports four external types (detailed in ./externals-reference.md):
git-repo
For complete Git repositories (plugins, frameworks):
[".zsh/plugins/plugin-name"]
type = "git-repo"
url = "https://github.com/user/repo.git"
revision = "<commit-sha>" # MUST pin to SHA
refreshPeriod = "168h"
file
For single files (themes, configs):
[".config/tool/theme.tmTheme"]
type = "file"
url = "https://github.com/user/repo/raw/<commit-sha>/file.ext"
refreshPeriod = "168h"
archive-file
For extracting single binary from release archive:
[".local/bin/tool"]
type = "archive-file"
url = "https://github.com/user/repo/releases/download/v1.2.3/tool.tar.gz"
executable = true
path = "tool"
checksum = "sha256:abc123..."
archive
For extracting entire archive into directory:
[".config/tool"]
type = "archive"
url = "https://github.com/user/repo/archive/<commit-sha>.tar.gz"
exact = true
stripComponents = 1
refreshPeriod = "168h"
5. Version Pinning Requirements
CRITICAL: Always use immutable references:
| Type | Pinning Method | Example |
|---|---|---|
| Git repo | Commit SHA in revision |
revision = "abc123..." |
| GitHub file | Commit SHA in URL path | raw/<sha>/file.ext |
| Release binary | Version tag + checksum | v1.2.3 + checksum = "sha256:..." |
| Docker image | Digest | image:tag@sha256:... |
NEVER use:
latesttags- Mutable branch names without SHA pinning
- Version ranges or wildcards
- URLs without commit SHAs or checksums
6. Ensure Renovate Integration
For each external dependency, verify or add corresponding Renovate rule in renovate.json5.
See ./renovate-integration.md for detailed patterns.
Quick reference:
{
customType: 'regex',
fileMatch: ['^home/\\.chezmoiexternals/.*\\.toml(\\.tmpl)?$'],
matchStrings: [
'revision = "(?<currentDigest>[a-f0-9]{40})".*?# renovate: repo=(?<depName>.*?) branch=(?<currentValue>.*?)\\n',
],
datasourceTemplate: 'git-refs',
}
7. Apply Changes
For .chezmoiexternals/ files
- Create or update the appropriate
.externals.tomlfile - Add comments explaining each dependency
- Include Renovate annotations for tracking:
# renovate: repo=user/repo branch=main [".path/to/dependency"] type = "git-repo" url = "https://github.com/user/repo.git" revision = "<commit-sha>"
For packages.yaml
- Edit
home/.chezmoidata/packages.yaml - Add to appropriate section (darwin/linux, brews/casks/mas)
- Follow existing patterns
8. Verification Commands
Preview changes:
chezmoi diff
Check external URLs:
# Verify latest commit SHA
gh api repos/USER/REPO/commits/BRANCH --jq .sha
# Check latest release tag
gh api repos/USER/REPO/releases/latest --jq .tag_name
# Verify file exists at SHA
curl -fsSL https://github.com/USER/REPO/raw/SHA/path/to/file
Apply changes:
chezmoi apply
Test external updates:
chezmoi update --force
Common Workflows
Adding a New Zsh Plugin
# 1. Get latest commit SHA
gh api repos/zsh-users/zsh-history-substring-search/commits/master --jq .sha
# 2. Add to home/.chezmoiexternals/zsh.externals.toml
# 3. Add Renovate rule to renovate.json5
# 4. Preview and apply
chezmoi diff
chezmoi apply
Adding a Homebrew Package
# 1. Edit home/.chezmoidata/packages.yaml
# 2. Add to appropriate section (brews/casks)
# 3. Renovate will track automatically (homebrew datasource)
Migrating Existing Externals to .chezmoiexternals/
- Identify program groupings in
home/.chezmoiexternal.toml - Search for references to the old file before migration:
Common references to fix:grep -r "\.chezmoiexternal\.toml\.tmpl" home/- Scripts using
{{ include ".chezmoiexternal.toml" }}for hashing - Documentation or comments mentioning the file
- Scripts using
- Create program-specific files in
home/.chezmoiexternals/ - Move related externals to appropriate file
- Update any references found in step 2 to point to new files (e.g.,
.chezmoiexternals/bat.externals.toml) - Keep conditionals (e.g.,
{{ if lookPath "zsh" }}) - Verify with
chezmoi diff(will error if references are broken) - Remove old file only after all references are updated and
chezmoi diffsucceeds
Updating External to Latest SHA
# 1. Fetch current SHA
gh api repos/USER/REPO/commits/BRANCH --jq .sha
# 2. Update revision in .chezmoiexternals/*.toml
# 3. Apply
chezmoi apply
Output Format
When making changes, provide:
Decision:
- Ecosystem: <brew|mise|python|docker|chezmoi-external|cli-versions>
- Rationale: <1-2 sentences>
- Organization: <which .chezmoiexternals/ file or other manifest>
Files To Edit:
- <path1>
- <path2>
Proposed Changes:
<concise diff-like hunks showing additions/modifications>
Renovate Integration:
- Rule Status: <exists|needs-addition|needs-update>
- Pattern: <brief description of regex or datasource>
Verification Commands:
- chezmoi diff
- <optional gh api commands to verify versions>
Next Steps:
- [ ] Review proposed changes
- [ ] Preview with chezmoi diff
- [ ] Apply with chezmoi apply
Important Constraints
- Never execute package managers without explicit permission
- Always preview with
chezmoi diffbefore applying - Use minimal edits: Change only what’s necessary
- Maintain comments: Explain why each dependency exists
- Follow patterns: Match existing style and structure
- Version pinning: No mutable references
Platform-Specific Handling
Use Go templates for platform-specific externals:
{{ if eq .chezmoi.os "darwin" }}
[".local/bin/tool-mac"]
{{ else if eq .chezmoi.os "linux" }}
type = "archive-file"
url = "https://github.com/user/tool/releases/download/v1.0.0/tool-darwin-{{ .chezmoi.arch }}.tar.gz"
executable = true
[".local/bin/tool-linux"]
{{ end }}type = "archive-file"
url = "https://github.com/user/tool/releases/download/v1.0.0/tool-linux-{{ .chezmoi.arch }}.tar.gz"
executable = true
Troubleshooting
External fails to download:
- Verify URL accessibility:
curl -fsSL <url> - Check commit SHA exists:
gh api repos/USER/REPO/commits/SHA - Review chezmoi logs:
chezmoi apply -v
Renovate not detecting dependency:
- Verify regex pattern matches your format
- Check fileMatch glob is correct
- Test regex: use online regex tester with actual file content
- Ensure annotations are present if required
Checksum mismatch:
- Download file and compute SHA:
curl -fsSL <url> | shasum -a 256 - Update checksum in external definition
- Verify URL points to correct version
Best Practices
- Group by program: Use
.chezmoiexternals/PROGRAM.externals.toml - Document dependencies: Add comments explaining purpose
- Annotate for Renovate: Include
# renovate:comments - Use templates: Leverage
{{ if }}for conditional installs - Set refreshPeriod: Balance freshness vs. performance (168h = weekly)
- Test on clean machine: Verify externals work from scratch
- Pin everything: No mutable references, ever
Anti-Patterns
NEVER use mutable references (latest, branch names, version ranges) in externals. WHY: Mutable refs make reproductions non-deterministic — the same chezmoi apply produces different results on different days, breaking machine parity.
NEVER execute package managers (brew install, mise use, chezmoi apply) without explicit user permission. WHY: Package installs modify system state; the agent’s role is to propose changes, not apply them unilaterally.
NEVER create a new .chezmoiexternal.toml at the repo root when .chezmoiexternals/ already exists. WHY: Mixing the legacy single-file format with the directory-based format confuses chezmoi and leads to duplicate or shadowed external definitions.
NEVER add a new external ecosystem when an existing one already covers the use case. WHY: Every additional ecosystem (Homebrew + mise + pip + externals + cli-versions) multiplies maintenance burden; always consult ./references/ecosystem-guide.md before introducing a new one.
NEVER omit a Renovate rule when adding a pinned external. WHY: A pinned dependency with no automation becomes stale immediately — security vulnerabilities and breaking changes accumulate silently until someone notices.
NEVER skip chezmoi diff before proposing chezmoi apply. WHY: Externals can overwrite local modifications; showing the diff first lets the user confirm no unexpected files will be changed.
Eval Scenarios
- Scenario 0: Add a Zsh plugin as a chezmoi external
- Scenario 1: Ecosystem selection across three tool types
- Scenario 2: Cross-platform release binary via archive-file external
- Scenario 3: Renovate automation rules for existing externals
- Scenario 4: Migrate legacy .chezmoiexternal.toml to .chezmoiexternals/
References
references/externals-reference.md— Complete external type specificationsreferences/renovate-integration.md— Renovate automation patternsreferences/ecosystem-guide.md— Package manager selection guide
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
No eval scenarios available.
Package Ecosystem Selection Guide
Decision framework for choosing the right package management approach in a chezmoi dotfiles repository.
Decision Tree
Is it a package/tool to install?
├─ YES → Does it need to be installed system-wide?
│ ├─ YES → Is this macOS?
│ │ ├─ YES → Use Homebrew (.chezmoidata/packages.yaml)
│ │ └─ NO → Is this Linux?
│ │ ├─ YES → Use system package manager (apt/dnf) or Homebrew
│ │ └─ NO → Use appropriate OS package manager
│ └─ NO → Is it a CLI developer tool (language version manager, etc.)?
│ ├─ YES → Use mise (.mise.toml)
│ ├─ NO → Is it a Python tool (uv tool install)?
│ │ ├─ YES → Use Python requirements (requirements.txt)
│ │ └─ NO → Is it for containers/development environments?
│ │ ├─ YES → Use Docker/devcontainer configs
│ │ └─ NO → Consider chezmoi externals
└─ NO → Is it a file/directory/repo to download?
└─ YES → Use chezmoi externals (.chezmoiexternals/)
Ecosystem Comparison
| Ecosystem | Best For | Location | Renovate Support | Notes |
|---|---|---|---|---|
| Homebrew | macOS packages, Linux packages | .chezmoidata/packages.yaml |
Built-in | System-wide installation |
| mise | CLI dev tools, language runtimes | .mise.toml |
Custom regex | Per-project or global |
| Python/uv | Python CLI tools | requirements.txt |
Built-in | Isolated Python environments |
| Docker | Container images | docker-compose.yml, devcontainer.json |
Built-in | Development environments |
| Chezmoi Externals | Files, archives, Git repos | .chezmoiexternals/*.toml |
Custom regex | Dotfiles, configs, plugins |
| CLI Versions | Script-installed CLIs | cli-versions.toml |
Custom regex | Install script dependencies |
Detailed Ecosystem Guides
1. Homebrew (brew/cask/mas)
When to use:
- macOS GUI applications (casks)
- macOS/Linux CLI tools available in Homebrew
- Mac App Store applications (mas)
- System-level packages
Location: home/.chezmoidata/packages.yaml
Format:
packages:
darwin:
brews:
- git
- node
- python
casks:
- google-chrome
- visual-studio-code
- docker
mas:
- 497799835 # Xcode (App Store ID)
Installation: Run script run_onchange_darwin-install-packages.sh.tmpl
Renovate: Built-in support via homebrew datasource
Pros:
- Extensive package catalog
- System-wide availability
- Automatic updates via Homebrew
- Well-maintained formulas
Cons:
- macOS/Linux only
- Requires admin permissions
- System-wide (can conflict)
- Slower than binary downloads
2. Mise (Aqua-based)
When to use:
- CLI developer tools (not in Homebrew)
- Language version managers
- Project-specific tool versions
- Tools from GitHub releases
Location: .mise.toml (global) or home/dot_config/mise/config.toml (managed)
Format:
[tools]
"aqua:mikefarah/yq" = "v4.47.1"
"aqua:cli/cli" = "v2.63.2"
node = "20.11.0"
python = "3.12.1"
Renovate: Custom regex manager for aqua-prefixed tools
Pros:
- Fast binary installations
- Per-project versions
- No admin permissions needed
- Extensive tool support via Aqua registry
Cons:
- Requires mise installed
- Less familiar than Homebrew
- Some tools may not be available
3. Python Requirements (uv tool install)
When to use:
- Python CLI tools (uv tool install)
- Python libraries and dependencies
- Tools best installed via pip
Location: home/dot_config/dotfiles/requirements.txt
Format:
black==24.1.1
ruff==0.2.0
poetry==1.7.1
Renovate: Built-in support via pypi datasource
4. Docker / Devcontainer
When to use:
- Container images for development
- Devcontainer features
- Docker Compose services
Location: home/dot_config/docker-compose/*.yml, .devcontainer/devcontainer.json
Renovate: Built-in support for Docker images and devcontainer features
5. Chezmoi Externals
When to use:
- Shell/editor plugins
- Configuration frameworks
- Theme files
- Binaries from GitHub releases
- Any file/directory to download
Location: home/.chezmoiexternals/*.toml.tmpl
Renovate: Custom regex managers for each pattern
Pros:
- No package manager dependencies
- Flexible (files, archives, repos)
- Fast downloads with caching
- Works across all platforms
Cons:
- Manual Renovate setup
- No dependency resolution
- Requires careful version pinning
6. CLI Versions (Install Script)
When to use:
- Tools installed by custom script
- Tools requiring special installation logic
- Cross-platform CLI tools with signature verification
Location: home/dot_config/dotfiles/cli-versions.toml
Format:
cosign = "v2.5.3"
chezmoi = "v2.56.0"
mise = "v2024.1.0"
Renovate: Custom regex manager
Selection Criteria
By Tool Type
| Tool Type | Recommended Ecosystem | Alternative |
|---|---|---|
| System utility (git, curl) | Homebrew | mise |
| GUI application (VS Code, Chrome) | Homebrew (cask) | Manual install |
| CLI dev tool (gh, jq, yq) | mise | Homebrew |
| Language runtime (Node, Python) | mise | Homebrew |
| Python tool (black, poetry) | uv tool install | mise |
| Shell plugin | Chezmoi externals | Manual |
| Editor plugin | Chezmoi externals | Editor package manager |
| Config framework (oh-my-zsh) | Chezmoi externals | Manual |
| Theme file | Chezmoi externals | Manual |
| Container image | Docker | N/A |
By Platform
| Platform | Primary | Secondary | Notes |
|---|---|---|---|
| macOS | Homebrew + mise | Chezmoi externals | Use casks for GUI apps |
| Linux | mise + apt/dnf | Homebrew, Chezmoi externals | System packages for deps |
| Containers | Docker + mise | Chezmoi externals | Minimal base image |
| Cross-platform | mise | Chezmoi externals | Use mise for consistency |
Best Practices
- Minimize ecosystems: Fewer systems = easier maintenance
- Prefer mise for CLI: Fast, flexible, per-project versions
- Use Homebrew for GUI: macOS applications via casks
- Externals for dotfiles: Plugins, themes, configs
- Pin everything: No mutable references anywhere
- Document choices: Comment why each tool uses its ecosystem
- Test cross-platform: Verify on macOS, Linux, containers
- Centralize versions: Use ecosystem-specific manifests
- Automate updates: Configure Renovate for all ecosystems
- Isolate environments: Avoid global installations when possible
Quick Reference
| Need | Use | Command |
|---|---|---|
| Add macOS app | Homebrew cask | Edit packages.yaml |
| Add CLI tool | mise | mise use <tool>@<version> |
| Add zsh plugin | Chezmoi external | Create in .chezmoiexternals/zsh.externals.toml.tmpl |
| Add Python tool | uv tool install | Add to requirements.txt |
| Add binary | Chezmoi external | Create in .chezmoiexternals/ with type=archive-file |
| Update versions | Renovate | Wait for PR or update manually |
| Preview changes | Chezmoi | chezmoi diff |
| Apply changes | Chezmoi | chezmoi apply |
Chezmoi External Format Reference
Complete specification for chezmoi external dependencies. Externals allow you to include files, archives, and Git repositories from external sources in your dotfiles.
Overview
Externals are defined in:
- Legacy:
home/.chezmoiexternal.<format>(single file for all externals) - Recommended:
home/.chezmoiexternals/*.toml[.tmpl](organized by program)
Files in .chezmoiexternals/ are automatically treated as external definitions relative to the source directory.
External Types
1. git-repo
Clones or updates a Git repository to a target directory.
Best for: Plugin managers, frameworks, complete configurations
Required fields:
type = "git-repo"url- Repository URL (HTTPS or SSH)
Optional fields:
revision- Specific commit SHA, tag, or branch (CRITICAL: always use commit SHA)clone.args- Additional git clone argumentspull.args- Additional git pull argumentsrefreshPeriod- How often to check for updates (e.g., “168h”)
Example:
[".zsh/plugins/zsh-autosuggestions"]
type = "git-repo"
url = "https://github.com/zsh-users/zsh-autosuggestions.git"
revision = "85919cd1ffa7d2d5412f6d3fe437ebdbeeec4fc5" # Pin to SHA
refreshPeriod = "168h" # Update weekly
Security: Always pin revision to a commit SHA for reproducibility.
2. file
Downloads a single file from a URL.
Best for: Configuration files, themes, single scripts
Required fields:
type = "file"urlorurls- Download location(s)
Optional fields:
executable- Make file executable (boolean)checksum- SHA-256/384/512 hash for verificationrefreshPeriod- How often to re-downloadencrypted- Handle encrypted content (boolean)
Example:
[".config/bat/themes/Catppuccin-mocha.tmTheme"]
type = "file"
url = "https://github.com/catppuccin/bat/raw/6810349b28055dce54076712fc05fc68da4b8ec0/themes/Catppuccin%20Mocha.tmTheme"
refreshPeriod = "168h"
With checksum:
[".local/bin/tool"]
type = "file"
url = "https://example.com/releases/v1.0.0/tool"
executable = true
checksum = "sha256:abc123def456..."
Security: Use commit SHA in URL path for GitHub files.
3. archive-file
Extracts a specific file from an archive (tar.gz, zip, etc.).
Best for: Release binaries, single files within archives
Required fields:
type = "archive-file"urlorurls- Archive download locationpath- Path to file within archive
Optional fields:
executable- Make extracted file executablechecksum- Archive checksum verificationrefreshPeriod- Update frequency
Example:
[".local/bin/zellij"]
type = "archive-file"
url = "https://github.com/zellij-org/zellij/releases/download/v0.40.0/zellij-x86_64-apple-darwin.tar.gz"
executable = true
path = "zellij"
checksum = "sha256:1234567890abcdef..."
refreshPeriod = "168h"
Security: Always include checksum for release binaries.
4. archive
Extracts an entire archive into a target directory.
Best for: Complete directory structures, multi-file configurations
Required fields:
type = "archive"urlorurls- Archive download location
Optional fields:
exact- Remove files not in archive (boolean)stripComponents- Strip leading path components (integer)include- Only extract matching patterns (array)exclude- Skip matching patterns (array)format- Force specific archive formatchecksum- Archive verificationrefreshPeriod- Update frequency
Example - Full directory:
[".oh-my-zsh"]
type = "archive"
url = "https://github.com/ohmyzsh/ohmyzsh/archive/abc123def456.tar.gz"
exact = true
stripComponents = 1
refreshPeriod = "168h"
Security: Use commit SHA in URL for GitHub archives.
Common Fields
url vs urls
# Single URL
url = "https://github.com/user/repo/archive/sha.tar.gz"
# Multiple URLs (tries in order)
urls = [
"https://cdn.example.com/file.tar.gz",
"https://github.com/user/repo/releases/download/v1.0.0/file.tar.gz",
]
checksum
checksum = "sha256:abc123def456..."
checksum = "sha384:abc123def456..."
checksum = "sha512:abc123def456..."
Generate checksum:
curl -fsSL <url> | shasum -a 256
refreshPeriod
refreshPeriod = "24h" # Daily
refreshPeriod = "168h" # Weekly (recommended for most externals)
refreshPeriod = "720h" # Monthly
Version Pinning Strategies
Git Repositories — ALWAYS use commit SHA in revision
# CORRECT
revision = "abc123def456..." # 40-character SHA
# WRONG - mutable reference
revision = "main"
GitHub Raw Files — Include commit SHA in URL path
# CORRECT
url = "https://github.com/user/repo/raw/abc123def456.../file.ext"
# WRONG
url = "https://github.com/user/repo/raw/main/file.ext"
Release Binaries — Pin version tag AND include checksum
# CORRECT
url = "https://github.com/user/tool/releases/download/v1.2.3/tool.tar.gz"
checksum = "sha256:abc123..."
# WRONG
url = "https://github.com/user/tool/releases/download/latest/tool.tar.gz"
Templating
OS Detection
{{ if eq .chezmoi.os "darwin" }}
[".config/tool-mac"]
type = "file"
url = "https://example.com/tool-mac"
{{ else if eq .chezmoi.os "linux" }}
[".config/tool-linux"]
type = "file"
url = "https://example.com/tool-linux"
{{ end }}
Conditional Installation
{{ if lookPath "zsh" }}
[".zsh/plugins/plugin"]
type = "git-repo"
url = "https://github.com/user/plugin.git"
{{ end }}
Architecture Detection
{{ $arch := .chezmoi.arch -}}
[".local/bin/tool"]
type = "archive-file"
url = "https://github.com/user/tool/releases/download/v1.0.0/tool-{{ .chezmoi.os }}-{{ $arch }}.tar.gz"
executable = true
path = "tool"
Archive Formats
Chezmoi automatically detects: .tar.gz, .tgz, .tar.bz2, .tar.xz, .tar.zst, .zip
Force specific format:
format = "tar.gz"
stripComponents
# Archive structure: repo-abc123/config/file.conf
[".config/tool"]
type = "archive"
url = "https://github.com/user/repo/archive/abc123.tar.gz"
stripComponents = 1 # Removes "repo-abc123/" prefix
include/exclude Patterns
[".config/tool"]
type = "archive"
url = "..."
include = ["config/**", "themes/*.json"]
exclude = ["**/*.md", "docs/**"]
Best Practices
- Always pin versions: Commit SHAs for git repos, checksums for files
- Use .chezmoiexternals/: Organize by program (zsh, bat, tmux, nvim, etc.)
- Set refreshPeriod: 168h recommended for most externals
- Add comments: Explain what each external is and why it’s included
- Template conditionally: Use
{{ if lookPath "tool" }}to skip on missing dependencies - Verify checksums: Always include for release binaries and archives
Troubleshooting
External not downloading
curl -fsSL <url> -o /tmp/test-download
gh api repos/USER/REPO/commits/SHA
chezmoi apply -v
Checksum mismatch
curl -fsSL <url> | shasum -a 256
# Update checksum in external definition
Template errors
chezmoi cat home/.chezmoiexternals/program.externals.toml
chezmoi data
Renovate Integration for Chezmoi Externals
Automated dependency updates for chezmoi external definitions using Renovate’s custom regex managers.
Overview
Renovate can automatically detect and update:
- Git repository commit SHAs
- GitHub release versions and checksums
- File URLs with commit SHAs
- Archive URLs with commit SHAs
Core Concept
For each external dependency, you need:
- Pinned reference in external definition (commit SHA, version tag)
- Renovate rule in
renovate.json5to detect and update that reference - Optional annotation in external file to help Renovate identify the dependency
Pattern Types
1. Git Repository with Revision (git-refs)
External format:
# renovate: repo=marlonrichert/zsh-snap branch=main
[".zsh/znap/zsh-snap"]
type = "git-repo"
url = "https://github.com/marlonrichert/zsh-snap.git"
revision = "25754a45d9ceafe6d7d082c9ebe40a08cb85a4f0"
refreshPeriod = "168h"
Renovate rule:
{
customType: 'regex',
fileMatch: ['^home/\\.chezmoiexternals/.*\\.toml(\\.tmpl)?$'],
matchStrings: [
'# renovate: repo=(?<depName>.*?) branch=(?<currentValue>.*?)\\n.*?\\n.*?revision = "(?<currentDigest>[a-f0-9]{40})"',
],
datasourceTemplate: 'git-refs',
}
2. GitHub Archive with Commit SHA (git-refs)
External format:
# renovate: repo=ohmyzsh/ohmyzsh branch=master
[".oh-my-zsh"]
type = "archive"
url = "https://github.com/ohmyzsh/ohmyzsh/archive/abc123def456.tar.gz"
exact = true
stripComponents = 1
Renovate rule:
{
customType: 'regex',
fileMatch: ['^home/\\.chezmoiexternals/.*\\.toml(\\.tmpl)?$'],
matchStrings: [
'# renovate: repo=(?<depName>.*?) branch=(?<currentValue>.*?)\\n.*?url = "https://github\\.com/[^/]+/[^/]+/archive/(?<currentDigest>[a-f0-9]{40})\\.tar\\.gz"',
],
datasourceTemplate: 'git-refs',
}
3. GitHub Raw File with Commit SHA (git-refs)
External format:
# renovate: repo=catppuccin/bat branch=main
[".config/bat/themes/Catppuccin-mocha.tmTheme"]
type = "file"
url = "https://github.com/catppuccin/bat/raw/6810349b28055dce54076712fc05fc68da4b8ec0/themes/Catppuccin%20Mocha.tmTheme"
Renovate rule:
{
customType: 'regex',
fileMatch: ['^home/\\.chezmoiexternals/.*\\.toml(\\.tmpl)?$'],
matchStrings: [
'# renovate: repo=(?<depName>.*?) branch=(?<currentValue>.*?)\\n.*?url = "https://github\\.com/[^/]+/[^/]+/raw/(?<currentDigest>[a-f0-9]{40})/',
],
datasourceTemplate: 'git-refs',
}
4. GitHub Release with Version Tag (github-releases)
External format:
[".local/bin/zellij"]
type = "archive-file"
url = "https://github.com/zellij-org/zellij/releases/download/v0.40.0/zellij-x86_64-apple-darwin.tar.gz"
executable = true
path = "zellij"
checksum = "sha256:abc123..."
Renovate rule:
{
customType: 'regex',
fileMatch: ['^home/\\.chezmoiexternals/.*\\.toml(\\.tmpl)?$'],
matchStrings: [
'url = "https://github\\.com/(?<depName>[^/]+/[^/]+)/releases/download/(?<currentValue>v?[0-9.]+)/',
],
datasourceTemplate: 'github-releases',
}
5. Multiple Files from Same Repo (shared annotation)
External format:
# renovate: repo=catppuccin/bat branch=main sha=6810349b28055dce54076712fc05fc68da4b8ec0
[".config/bat/themes/Catppuccin Latte.tmTheme"]
type = "file"
url = "https://github.com/catppuccin/bat/raw/6810349b28055dce54076712fc05fc68da4b8ec0/themes/Catppuccin%20Latte.tmTheme"
[".config/bat/themes/Catppuccin Mocha.tmTheme"]
type = "file"
url = "https://github.com/catppuccin/bat/raw/6810349b28055dce54076712fc05fc68da4b8ec0/themes/Catppuccin%20Mocha.tmTheme"
Single annotation at top; Renovate updates all matching SHAs in the file.
File Matching Patterns
// All .chezmoiexternals/ files
fileMatch: ["^home/\\.chezmoiexternals/.*\\.toml(\\.tmpl)?$"]
// Specific program file
fileMatch: ["^home/\\.chezmoiexternals/zsh\\.externals\\.toml(\\.tmpl)?$"]
// Legacy single-file format
fileMatch: ["^home/\\.chezmoiexternal\\.toml(\\.tmpl)?$"]
Annotation Patterns
Inline (Recommended)
Place immediately before external definition:
# renovate: repo=user/repo branch=main
[".path/to/file"]
type = "git-repo"
url = "https://github.com/user/repo.git"
revision = "<commit-sha>"
URL Extraction (No Annotation)
For consistent URL formats, Renovate can extract info directly:
[".local/bin/tool"]
type = "archive-file"
url = "https://github.com/user/tool/releases/download/v1.0.0/tool.tar.gz"
executable = true
Datasource Types
| Datasource | Best For |
|---|---|
git-refs |
Commit SHAs (revisions, archive/file URL SHAs) |
github-releases |
Release binary URLs with version tags |
github-tags |
Git tags (alternative to github-releases) |
Complete Example Configuration
{
extends: ['config:base'],
customManagers: [
// Git repos with revision field
{
customType: 'regex',
fileMatch: ['^home/\\.chezmoiexternals/.*\\.toml(\\.tmpl)?$'],
matchStrings: [
'# renovate: repo=(?<depName>.*?) branch=(?<currentValue>.*?)\\n.*?\\n.*?revision = "(?<currentDigest>[a-f0-9]{40})"',
],
datasourceTemplate: 'git-refs',
},
// GitHub archives with commit SHA
{
customType: 'regex',
fileMatch: ['^home/\\.chezmoiexternals/.*\\.toml(\\.tmpl)?$'],
matchStrings: [
'# renovate: repo=(?<depName>.*?) branch=(?<currentValue>.*?)\\n.*?url = "https://github\\.com/[^/]+/[^/]+/archive/(?<currentDigest>[a-f0-9]{40})\\.tar\\.gz"',
],
datasourceTemplate: 'git-refs',
},
// GitHub raw files with commit SHA
{
customType: 'regex',
fileMatch: ['^home/\\.chezmoiexternals/.*\\.toml(\\.tmpl)?$'],
matchStrings: [
'# renovate: repo=(?<depName>.*?) branch=(?<currentValue>.*?)\\n.*?url = "https://github\\.com/[^/]+/[^/]+/raw/(?<currentDigest>[a-f0-9]{40})/',
],
datasourceTemplate: 'git-refs',
},
// GitHub releases
{
customType: 'regex',
fileMatch: ['^home/\\.chezmoiexternals/.*\\.toml(\\.tmpl)?$'],
matchStrings: [
'url = "https://github\\.com/(?<depName>[^/]+/[^/]+)/releases/download/(?<currentValue>v?[0-9.]+)/',
],
datasourceTemplate: 'github-releases',
},
],
}
Testing Renovate Rules
- Validate regex: Use regex101.com with your external file content as test string
- Dry-run locally:
LOG_LEVEL=debug renovate --dry-run --require-config=false - Test in PR: Commit outdated SHA, wait for Renovate to propose update
Common Issues
Renovate Not Detecting Dependency
- Check
fileMatchpattern includes your file - Verify regex named capture groups exist and match
- Ensure datasource is appropriate
- Check Renovate logs for parse errors
Updates Break Checksums
Solutions:
- Accept manual checksum updates in Renovate PRs
- Run a post-update script to compute and update checksums
- Use
autoReplaceStringTemplateto preserve file structure
Branch Not Found
Verify annotation’s branch name matches the repository default branch (main vs master).
Reference Commands
# Get latest commit SHA
gh api repos/USER/REPO/commits/BRANCH --jq .sha
# Get latest release
gh api repos/USER/REPO/releases/latest --jq .tag_name
# Compute checksum
curl -fsSL <url> | shasum -a 256
# Force update externals
chezmoi update --force
Best Practices
- Use annotations: Add
# renovate:comments for clarity and maintainability - Group by program: Organize externals in
.chezmoiexternals/PROGRAM.externals.toml - Consistent formatting: Use same pattern across similar externals
- Document patterns: Add comments in
renovate.json5explaining each rule - Test thoroughly: Validate rules before relying on automation
- Pin everything: Always use immutable references