Skip to content

Commit 727e085

Browse files
committed
chore(installer): move release scripts to template-only source
Make install.sh.tmpl and install.ps1.tmpl the installer source of truth, render release assets in CI, remove tracked rendered scripts, and update docs/workflows to match the new model. Made-with: Cursor
1 parent e2cc62e commit 727e085

11 files changed

Lines changed: 68 additions & 30 deletions

.github/renovate.json5

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
customManagers: [
1111
{
1212
customType: "regex",
13-
managerFilePatterns: ["/install\\.sh$/"],
13+
managerFilePatterns: ["/install\\.sh\\.tmpl$/"],
1414
matchStrings: [
1515
"CRANE_VERSION=\"(?<currentValue>v[^\"]+)\"",
1616
],

.github/workflows/_build-container.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,20 @@ jobs:
3333
env:
3434
IMAGE_NAME: ${{ inputs.image_registry }}/${{ inputs.image_name }}
3535

36+
- uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4
37+
with:
38+
install: true
39+
40+
- name: Render install.sh from template
41+
env:
42+
DOTFILES_REPO_URL: ${{ github.server_url }}/${{ github.repository }}
43+
DOTFILES_IMAGE_NAME: ${{ steps.image.outputs.name }}
44+
DOTFILES_ATTESTATION_REPO: ${{ github.repository }}
45+
DOTFILES_RELEASE_TAG: ${{ inputs.tag_name }}
46+
run: |
47+
mise x -- chezmoi execute-template --file install.sh.tmpl > install.sh
48+
chmod +x install.sh
49+
3650
- name: Log in to GHCR
3751
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
3852
with:

.github/workflows/_upload-scripts.yml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,21 @@ jobs:
2525
steps:
2626
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
2727

28-
- name: Interpolate repo URL in install scripts
28+
- uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4
29+
with:
30+
install: true
31+
32+
- name: Render install scripts from templates
2933
env:
30-
REPO_URL: ${{ github.server_url }}/${{ github.repository }}
34+
DOTFILES_REPO_URL: ${{ github.server_url }}/${{ github.repository }}
3135
IMAGE_NAME: ${{ inputs.image_registry }}/${{ inputs.image_name }}
32-
REPOSITORY: ${{ github.repository }}
36+
DOTFILES_ATTESTATION_REPO: ${{ github.repository }}
37+
DOTFILES_RELEASE_TAG: ${{ inputs.tag_name }}
3338
run: |
3439
IMAGE_NAME_LC="${IMAGE_NAME,,}"
35-
sed -i "s|repo_url=\"https://github.com/chipwolf/dotfiles\"|repo_url=\"${REPO_URL}\"|" install.sh
36-
sed -i "s|ghcr.io/chipwolf/dotfiles|${IMAGE_NAME_LC}|g" install.sh
37-
sed -i "s|--repo chipwolf/dotfiles|--repo ${REPOSITORY}|g" install.sh
38-
sed -i "s|\\\$repoUrl = \"https://github.com/chipwolf/dotfiles\"|\$repoUrl = \"${REPO_URL}\"|" install.ps1
39-
sed -i "s|https://github.com/chipwolf/dotfiles/releases/download/|${REPO_URL}/releases/download/|g" install.ps1
40+
DOTFILES_IMAGE_NAME="${IMAGE_NAME_LC}" mise x -- chezmoi execute-template --file install.sh.tmpl > install.sh
41+
DOTFILES_IMAGE_NAME="${IMAGE_NAME_LC}" mise x -- chezmoi execute-template --file install.ps1.tmpl > install.ps1
42+
chmod +x install.sh
4043
4144
- name: Upload install scripts to release
4245
env:

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@ reviews.local/
55
home/dot_config/opencode/private_mcp-atlassian.env
66
home/dot_config/opencode/private_slack-mcp.env
77
home/dot_config/opencode/skills/onw-monthly-tickets/SKILL.md
8+
install.sh
9+
install.ps1
10+
home/Brewfile
811

AGENTS.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ See [Concepts](https://www.chezmoi.io/reference/concepts/).
3434
The file [.chezmoiroot](.chezmoiroot) at the repo root contains `home`. So the **source state** is read from the `home/` directory. All managed targets and special files (e.g. config template, scripts) are under `home/`.
3535

3636
- [.chezmoiroot](https://www.chezmoi.io/reference/special-files/chezmoiroot/) is read first; it sets the path used for the rest of the source state.
37-
- The working tree (git repo) is the parent of that path; `install.sh`, `.macos`, `.gitignore`, and `README.md` live at repo root and are **not** part of the source state.
37+
- The working tree (git repo) is the parent of that path; `install.sh.tmpl`, `install.ps1.tmpl`, `.macos`, `.gitignore`, and `README.md` live at repo root and are **not** part of the source state.
3838

3939
### Naming: source state attributes
4040

@@ -124,9 +124,9 @@ Deleting a file from the chezmoi source does **not** remove it from the target (
124124
- **Other config**`home/dot_config/` includes tmux, mise, finicky; `home/private_dot_gnupg/` for GnuPG (private permissions).
125125
- **Executable**`home/dot_scripts/executable_brew-review` (→ `~/.scripts/brew-review`) is the Homebrew drift review script. `home/dot_scripts/executable_7zw` (→ `~/.scripts/7zw`) is a 7-zip wrapper. Both live in `dot_scripts/` — not `dot_zfunctions/` (see Brew section below).
126126
- **Bootstrap**`home/.chezmoiscripts/run_once_before_bootstrap.sh.tmpl` runs once before other updates (install deps, brew bundle, oh-my-zsh, mise, etc.). It is OS-aware (darwin/linux) and sets Codespaces overrides when `codespaces` is true.
127-
- **Root-level (not in source state)**`install.sh` runs `chezmoi init --apply --source=...` to bootstrap; `.macos` holds macOS defaults; `.gitignore` excludes local/private artifacts (e.g. `*.local.*`, vim swap/undo). Do not add ignored patterns to the source state.
127+
- **Root-level (not in source state)**`install.sh.tmpl` and `install.ps1.tmpl` are installer templates for release assets; `.macos` holds macOS defaults; `.gitignore` excludes local/private artifacts (e.g. `*.local.*`, vim swap/undo). Do not add ignored patterns to the source state.
128128
- **README "What you get" table** – The tool table in `README.md` is sorted by category: shell/prompt, terminal emulators, multiplexer, editor, dev tools, version control, security, package/runtime management, then platform-specific utilities. When adding or removing a managed tool, update this table and preserve the sort order. Platform columns (macOS, Linux, Windows, Codespaces) must reflect what `.chezmoiignore` actually deploys.
129-
- **Install script repo URL**`install.sh` and `install.ps1` hardcode `repo_url` pointing at `chipwolf/dotfiles`. The release workflow (`release.yml`) interpolates this with `github.server_url/github.repository` before uploading to the release, so forks get correct URLs automatically. Do not remove the hardcoded values from the source files; they are needed for local clone execution.
129+
- **Install script templates**`install.sh.tmpl` and `install.ps1.tmpl` are the source of truth for release installers. The release workflow renders `install.sh` and `install.ps1` from templates with repository/image/tag values before uploading release assets.
130130
- **Template readability** – Keep chezmoi template source files readable: use clear indentation, split complex logic into understandable blocks, and avoid flattening everything to the left margin with aggressive whitespace trimming unless required for output correctness.
131131

132132
---
@@ -137,13 +137,13 @@ Deleting a file from the chezmoi source does **not** remove it from the target (
137137
- **OS-conditional ignores** in `home/.chezmoiignore` use `{{ if eq .chezmoi.os "windows" }}` and `{{ if ne .chezmoi.os "windows" }}` blocks to control which targets are deployed per platform.
138138
- **Bash chezmoiscripts** (`run_onchange_after_bootstrap.sh.tmpl`, `run_onchange_after_brew_review.sh.tmpl`, `run_onchange_after_tmux_symlinks.sh.tmpl`) are wrapped in `{{ if ne .chezmoi.os "windows" }}` guards so they render to empty on Windows (chezmoi skips empty scripts).
139139
- **Windows bootstrap**`home/.chezmoiscripts/run_onchange_after_bootstrap_windows.ps1.tmpl` installs packages via Chocolatey (`choco install -y`), runs `mise install`, and syncs Neovim plugins. Runs only on Windows.
140-
- **`install.ps1`** at the repo root is the Windows equivalent of `install.sh`: installs Chocolatey, chezmoi, and git, then runs `chezmoi init --apply`.
140+
- **`install.ps1.tmpl`** at the repo root is the Windows installer template equivalent of `install.sh.tmpl`: installs Chocolatey, chezmoi, and git, then runs `chezmoi init --apply`.
141141
- **WezTerm**`home/dot_config/wezterm/wezterm.lua` (→ `~/.config/wezterm/wezterm.lua`). Windows terminal emulator with kitty graphics protocol support. Ignored on non-Windows via `.chezmoiignore`.
142142
- **OpenCode**`home/dot_config/opencode/opencode.jsonc.tmpl` renders MCP and permission data from `home/.chezmoidata/mcps/*.yaml` and `home/.chezmoidata/agent-permissions/*.yaml`. Atlassian entries are gated by `conditions` in those overlays (for example `private: false`), so they are excluded on personal machines. Since Windows is always personal, this also covers Windows.
143143
- **Package manager** — Windows uses Chocolatey (`choco`), not winget or scoop.
144144
- **When adding new configs**, decide if the target is cross-platform, Unix-only, or Windows-only, and update `home/.chezmoiignore` accordingly.
145145
- **When adding new chezmoiscripts**, bash scripts (`.sh.tmpl`) must be guarded with `{{ if ne .chezmoi.os "windows" }}` and PowerShell scripts (`.ps1.tmpl`) with `{{ if eq .chezmoi.os "windows" }}` so they render to empty on the wrong OS.
146-
- **WSL** — The Windows bootstrap script provisions WSL Ubuntu non-interactively via cloud-init. It writes a cloud-config to `~/.cloud-init/Ubuntu.user-data` (using the Windows username), installs Ubuntu with `--no-launch`, then launches and waits for cloud-init to create the user, clone the dotfiles repo, and run `install.sh`. Inside WSL, `chezmoi.os` is `"linux"` so the full Unix config stack (zsh, brew, tmux, etc.) applies without modification.
146+
- **WSL** — The Windows bootstrap script provisions WSL Ubuntu non-interactively via cloud-init. It writes a cloud-config to `~/.cloud-init/Ubuntu.user-data` (using the Windows username), installs Ubuntu with `--no-launch`, then launches and waits for cloud-init to create the user, clone the dotfiles repo, install chezmoi, and run `chezmoi init --apply --source=...`. Inside WSL, `chezmoi.os` is `"linux"` so the full Unix config stack (zsh, brew, tmux, etc.) applies without modification.
147147

148148
---
149149

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ chezmoi uses two boolean flags to adapt behavior per machine. Both are set autom
114114

115115
| Flag | When true | What it gates |
116116
|--------------|---------------------------------|---------------------------------------------------------------------------------------------------------------|
117-
| `codespaces` | `CODESPACES` env var is set | Skips GUI apps and redundant packages in the Brewfile. Uses the overlay fast path in [`install.sh`](install.sh). |
117+
| `codespaces` | `CODESPACES` env var is set | Skips GUI apps and redundant packages in the Brewfile. Uses the overlay fast path in [`install.sh.tmpl`](install.sh.tmpl). |
118118
| `private` | Windows, or `~/.private` exists | Enables personal-machine config: excludes work-specific MCP servers and Atlassian integrations from OpenCode. |
119119

120120
> [!IMPORTANT]
@@ -166,6 +166,7 @@ Change these files first:
166166
- `profile.git.name`
167167
- `profile.git.email`
168168
- `profile.git.githubUser`
169+
- `profile.git.githubRepo` (for WSL bootstrap clone target)
169170
- `profile.git.signingKey` (optional)
170171
- `profile.codespaces.gitName`
171172
- **Git config template**: `home/dot_gitconfig.tmpl` (usually no change needed, only edit if you want different structure)
@@ -190,8 +191,8 @@ Optional removals if not relevant to your setup:
190191

191192
Install scripts note:
192193

193-
- Keep the hardcoded upstream repo values in `install.sh` and `install.ps1` in source.
194-
- Release automation rewrites those values for your fork's release assets, so local source stays stable while published installers point to your fork.
194+
- `install.sh.tmpl` and `install.ps1.tmpl` are the installer source of truth.
195+
- Release automation renders release-ready `install.sh` and `install.ps1` from those templates using the current repository and tag values.
195196

196197
> [!TIP]
197198
> The chezmoi source state lives under [`home/`](home/) (set by [`.chezmoiroot`](.chezmoiroot)). Filenames use chezmoi's attribute prefixes: `dot_` becomes a leading `.`, `private_` restricts permissions, `executable_` adds the execute bit. [`home/dot_config/nvim/`](home/dot_config/nvim/) deploys to `~/.config/nvim/`.
@@ -218,7 +219,7 @@ All release artifacts are built with [SLSA Build L3](https://slsa.dev/spec/v1.0/
218219

219220
This covers:
220221

221-
- **Install scripts** ([`install.sh`](install.sh), [`install.ps1`](install.ps1)): published as [GitHub Release](https://github.com/chipwolf/dotfiles/releases/tag/v1.6.0) assets. <!-- x-release-please-version -->
222+
- **Install scripts** ([`install.sh.tmpl`](install.sh.tmpl), [`install.ps1.tmpl`](install.ps1.tmpl)): published as [GitHub Release](https://github.com/chipwolf/dotfiles/releases/tag/v1.6.0) assets after CI template rendering. <!-- x-release-please-version -->
222223
- **Codespaces overlay image** ([ghcr.io/chipwolf/dotfiles](https://ghcr.io/chipwolf/dotfiles)): published to GHCR.
223224

224225
> [!NOTE]

home/.chezmoidata/profile.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ profile:
33
name: "Chip Wolf"
44
email: "hello@chipwolf.uk"
55
githubUser: "chipwolf"
6+
githubRepo: "dotfiles"
67
# signingKey: "0x4C90101E11349775"
78
codespaces:
89
gitName: "Chip Wolf ‮"

home/.chezmoiscripts/run_onchange_after_bootstrap_windows.ps1.tmpl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
"Wakatime.CLIWakatime"
2727
"Wakatime.DesktopWakatime"
2828
-}}
29+
{{- $dotfilesRepoUser := .profile.git.githubUser -}}
30+
{{- $dotfilesRepoName := .profile.git.githubRepo -}}
31+
{{- $dotfilesRepoUrl := printf "https://github.com/%s/%s" $dotfilesRepoUser $dotfilesRepoName -}}
2932
{{ if eq .chezmoi.os "windows" -}}
3033
# Chocolatey package list hash (triggers re-run when this list changes):
3134
# {{ join " " $chocoPackages | sha256sum }}
@@ -97,8 +100,8 @@ write_files:
97100
default=$wslUser
98101
99102
runcmd:
100-
- sudo -u $wslUser git clone https://github.com/chipwolf/dotfiles /home/$wslUser/.local/share/chezmoi
101-
- sudo -u $wslUser /home/$wslUser/.local/share/chezmoi/install.sh
103+
- sudo -u $wslUser git clone {{ $dotfilesRepoUrl }} /home/$wslUser/.local/share/chezmoi
104+
- sudo -u $wslUser bash -lc 'if ! command -v chezmoi >/dev/null 2>&1; then sh -c "$(curl -fsLS get.chezmoi.io)" -- -b "$HOME/.local/bin"; fi; PATH="$HOME/.local/bin:$PATH"; "$HOME/.local/bin/chezmoi" init --apply --source="$HOME/.local/share/chezmoi"'
102105
"@
103106
[System.IO.File]::WriteAllText("$cloudInitDir\Ubuntu.user-data", $cloudConfig)
104107

@@ -126,10 +129,13 @@ runcmd:
126129
Write-Host "Updating dotfiles in WSL..."
127130
wsl -d Ubuntu -- bash -lc '
128131
if [ ! -d ~/.local/share/chezmoi ]; then
129-
git clone https://github.com/chipwolf/dotfiles ~/.local/share/chezmoi
132+
git clone {{ $dotfilesRepoUrl }} ~/.local/share/chezmoi
130133
fi
131134
cd ~/.local/share/chezmoi && git pull
132-
~/.local/share/chezmoi/install.sh
135+
if ! command -v chezmoi >/dev/null 2>&1; then
136+
sh -c "$(curl -fsLS get.chezmoi.io)" -- -b "$HOME/.local/bin"
137+
fi
138+
PATH="$HOME/.local/bin:$PATH" ~/.local/bin/chezmoi init --apply --source="$HOME/.local/share/chezmoi"
133139
'
134140
}
135141
}

install.ps1 renamed to install.ps1.tmpl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
h# install.ps1 — Bootstrap chezmoi dotfiles on Windows.
1+
{{- $repoURL := env "DOTFILES_REPO_URL" -}}
2+
{{- $releaseTag := env "DOTFILES_RELEASE_TAG" -}}
3+
# install.ps1 — Bootstrap chezmoi dotfiles on Windows.
24
# Forks: update $repoUrl below to point at your fork.
5+
#
6+
# Render source: install.ps1.tmpl
37

48
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'Install script uses Write-Host for coloured terminal output')]
59
param()
610

7-
$repoUrl = "https://github.com/chipwolf/dotfiles"
8-
$rawBase = "https://github.com/chipwolf/dotfiles/releases/download/v1.6.0" # x-release-please-version
11+
$repoUrl = "{{ $repoURL }}"
12+
$rawBase = "{{ $repoURL }}/releases/download/{{ $releaseTag }}" # x-release-please-version
913

1014
$ErrorActionPreference = "Stop"
1115

install.sh renamed to install.sh.tmpl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
{{- $repoURL := env "DOTFILES_REPO_URL" -}}
2+
{{- $imageName := env "DOTFILES_IMAGE_NAME" -}}
3+
{{- $attestationRepo := env "DOTFILES_ATTESTATION_REPO" -}}
4+
{{- $releaseTag := env "DOTFILES_RELEASE_TAG" -}}
15
#!/bin/sh
26
# Forks: update repo_url below to point at your fork.
7+
#
8+
# Render source: install.sh.tmpl
39

410
# -e: exit on error
511
# -u: exit on unset variables
612
set -eu
713

8-
repo_url="https://github.com/chipwolf/dotfiles"
14+
repo_url="{{ $repoURL }}"
915

1016
# --- Codespaces fast path ---
1117
# The GHCR image is a delta-only image (scratch + our overlay layers).
@@ -23,9 +29,9 @@ if [ -n "${CODESPACES:-}" ] && [ -z "${DOTFILES_NO_OVERLAY:-}" ]; then
2329
tar -xzf "/tmp/_crane/${CRANE_TAR}" -C /tmp/_crane crane
2430
CRANE=/tmp/_crane/crane
2531

26-
DOTFILES_IMAGE_OWNER="${DOTFILES_IMAGE_OWNER:-chipwolf}"
27-
DOTFILES_ATTESTATION_REPO="${DOTFILES_ATTESTATION_REPO:-${DOTFILES_IMAGE_OWNER}/dotfiles}"
28-
OUR_IMAGE="ghcr.io/${DOTFILES_IMAGE_OWNER}/dotfiles:v1.6.0" # x-release-please-version
32+
DOTFILES_IMAGE_NAME="{{ $imageName }}"
33+
DOTFILES_ATTESTATION_REPO="{{ $attestationRepo }}"
34+
OUR_IMAGE="${DOTFILES_IMAGE_NAME}:{{ $releaseTag }}" # x-release-please-version
2935

3036
OUR_MANIFEST=$("$CRANE" manifest "$OUR_IMAGE")
3137
OUR_DIGEST=$("$CRANE" digest "$OUR_IMAGE")

0 commit comments

Comments
 (0)