diff --git a/README.md b/README.md index 2acff13..31f31b5 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ This repository uses [chezmoi](https://www.chezmoi.io/) to deploy regular dotfiles on Windows and Linux. Repository support files stay outside the `home/` source state. +Fresh bootstrap installations keep the Git working tree at `~/.dotfiles`. The generated chezmoi configuration persists that directory, so regular commands such as `chezmoi update` and `chezmoi cd` use it without extra flags. Because `.chezmoiroot` points to `home`, `chezmoi source-path` resolves to `~/.dotfiles/home`. Existing installations are not moved automatically. + ## Quick installation ### Windows @@ -42,6 +44,7 @@ For dotfiles plus installer-managed package/application/module updates, use `upd ## State details +- `home/.chezmoi.toml.tmpl` persists the source directory selected during `chezmoi init`; fresh bootstraps explicitly select `~/.dotfiles`. - `home/.chezmoiroot` is represented by the repository-level `.chezmoiroot`, pointing to `home`. - Windows-only `.wslconfig` and Linux-only Zsh/Sheldon state are filtered by `home/.chezmoiignore`. - `~/.gitconfig.local`, `~/.codex/config.toml`, and `~/.codex/rules/default.rules` use chezmoi create-only semantics and are not overwritten after creation. diff --git a/bootstrap.cmd b/bootstrap.cmd index 861aeca..7163445 100644 --- a/bootstrap.cmd +++ b/bootstrap.cmd @@ -121,7 +121,7 @@ for /f "delims=" %%R in ('chezmoi.exe source-path 2^>nul') do set "SOURCE_ROOT=% if defined SOURCE_ROOT if exist "%SOURCE_ROOT%\.chezmoiroot" goto existing_chezmoi if defined SOURCE_ROOT for %%P in ("%SOURCE_ROOT%\..") do if exist "%%~fP\.chezmoiroot" goto existing_chezmoi -chezmoi.exe init "%REPO_URL%" +chezmoi.exe --source "%USERPROFILE%\.dotfiles" init "%REPO_URL%" exit /b %ERRORLEVEL% :existing_chezmoi diff --git a/bootstrap.sh b/bootstrap.sh old mode 100644 new mode 100755 index d57e69b..8d4b579 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -82,7 +82,7 @@ else if [[ -f "$SOURCE_ROOT/.chezmoiroot" || -f "$(dirname "$SOURCE_ROOT")/.chezmoiroot" ]]; then existing_source=true else - chezmoi init "$REPO_URL" + chezmoi --source "$HOME/.dotfiles" init "$REPO_URL" fi fi diff --git a/home/.chezmoi.toml.tmpl b/home/.chezmoi.toml.tmpl new file mode 100644 index 0000000..edbecd8 --- /dev/null +++ b/home/.chezmoi.toml.tmpl @@ -0,0 +1 @@ +sourceDir = {{ joinPath .chezmoi.homeDir ".dotfiles" | quote }} diff --git a/tests/linux/assert-state.sh b/tests/linux/assert-state.sh old mode 100644 new mode 100755 index c4e0c67..c4bfa04 --- a/tests/linux/assert-state.sh +++ b/tests/linux/assert-state.sh @@ -14,6 +14,11 @@ printf 'HOME: %s\n' "$HOME" printf 'chezmoi: %s\n' "$(chezmoi --version)" printf 'chezmoi source-path: %s\n' "$(chezmoi source-path)" +source_path="$(chezmoi source-path)" +working_tree="$(chezmoi execute-template '{{ .chezmoi.workingTree }}')" +[[ "$working_tree" == "$HOME/.dotfiles" ]] || fail "chezmoi working tree is $working_tree, expected $HOME/.dotfiles" +[[ "$source_path" == "$HOME/.dotfiles/home" ]] || fail "chezmoi source path is $source_path, expected $HOME/.dotfiles/home" + case "$expected_distro" in debian) [[ -f /etc/debian_version ]] || fail "Debian detection marker is missing" ;; arch) [[ -f /etc/arch-release ]] || fail "Arch detection marker is missing" ;; diff --git a/tests/static/validate-architecture.py b/tests/static/validate-architecture.py old mode 100644 new mode 100755 index 19da9f6..31dd57b --- a/tests/static/validate-architecture.py +++ b/tests/static/validate-architecture.py @@ -33,6 +33,7 @@ def read_text(path: str) -> str: fail(".chezmoiroot must contain 'home'") required_paths = ( + "home/.chezmoi.toml.tmpl", "home", "scripts/linux/packages-debian.txt", "scripts/linux/cleanup-broken-managed-links.sh", diff --git a/tests/windows/assert-allsigned.ps1 b/tests/windows/assert-allsigned.ps1 index 274c408..60052ac 100644 --- a/tests/windows/assert-allsigned.ps1 +++ b/tests/windows/assert-allsigned.ps1 @@ -211,6 +211,17 @@ function Assert-CleanChezMoi { $sourcePath = ((Invoke-Chezmoi @("source-path")) -join "").Trim() Write-Host "chezmoi source-path: $sourcePath" +$expectedWorkingTree = [IO.Path]::GetFullPath((Join-Path $HOME ".dotfiles")).TrimEnd([IO.Path]::DirectorySeparatorChar) +$workingTree = ((Invoke-Chezmoi @("execute-template", "{{ .chezmoi.workingTree }}")) -join "").Trim() +$actualWorkingTree = [IO.Path]::GetFullPath($workingTree).TrimEnd([IO.Path]::DirectorySeparatorChar) +if (-not $actualWorkingTree.Equals($expectedWorkingTree, [StringComparison]::OrdinalIgnoreCase)) { + Fail "chezmoi working tree is $actualWorkingTree, expected $expectedWorkingTree" +} +$expectedSourcePath = [IO.Path]::GetFullPath((Join-Path $expectedWorkingTree "home")).TrimEnd([IO.Path]::DirectorySeparatorChar) +$actualSourcePath = [IO.Path]::GetFullPath($sourcePath).TrimEnd([IO.Path]::DirectorySeparatorChar) +if (-not $actualSourcePath.Equals($expectedSourcePath, [StringComparison]::OrdinalIgnoreCase)) { + Fail "chezmoi source path is $actualSourcePath, expected $expectedSourcePath" +} $managedAppsPath = Join-Path $RepoRoot "scripts\windows\managed-apps.csv" $coreApps = @(Import-Csv -LiteralPath $managedAppsPath | Where-Object Category -eq "core") foreach ($app in $coreApps) { diff --git a/tests/windows/assert-state.ps1 b/tests/windows/assert-state.ps1 index f68d1e9..e3813aa 100644 --- a/tests/windows/assert-state.ps1 +++ b/tests/windows/assert-state.ps1 @@ -41,6 +41,18 @@ $sourcePath = ((Invoke-Chezmoi @("source-path")) -join "").Trim() Write-Host "chezmoi source-path: $sourcePath" if (-not (Test-Path -LiteralPath $sourcePath)) { Fail "chezmoi source path does not exist: $sourcePath" } +$expectedWorkingTree = [IO.Path]::GetFullPath((Join-Path $HOME ".dotfiles")).TrimEnd([IO.Path]::DirectorySeparatorChar) +$workingTree = ((Invoke-Chezmoi @("execute-template", "{{ .chezmoi.workingTree }}")) -join "").Trim() +$actualWorkingTree = [IO.Path]::GetFullPath($workingTree).TrimEnd([IO.Path]::DirectorySeparatorChar) +if (-not $actualWorkingTree.Equals($expectedWorkingTree, [StringComparison]::OrdinalIgnoreCase)) { + Fail "chezmoi working tree is $actualWorkingTree, expected $expectedWorkingTree" +} +$expectedSourcePath = [IO.Path]::GetFullPath((Join-Path $expectedWorkingTree "home")).TrimEnd([IO.Path]::DirectorySeparatorChar) +$actualSourcePath = [IO.Path]::GetFullPath($sourcePath).TrimEnd([IO.Path]::DirectorySeparatorChar) +if (-not $actualSourcePath.Equals($expectedSourcePath, [StringComparison]::OrdinalIgnoreCase)) { + Fail "chezmoi source path is $actualSourcePath, expected $expectedSourcePath" +} + $requiredFiles = @( (Join-Path $HOME ".gitconfig"), (Join-Path $HOME ".gitconfig.local"),