Tools/direnv.ps1
|
# Companion for direnv — per-directory .envrc loading. # # Its hook (only when direnv wins project-env) fills in DIRENV_BASH from Git for # Windows' bash when unset (direnv.toml's bash_path still wins), and warns while # the installed direnv has the Windows bug that unloads unrelated variables # (direnv#1488; see docs/external-dependencies.md). # # direnv's pwsh hook attaches to # $ExecutionContext.SessionState.InvokeCommand.LocationChangedAction (fires on # Set-Location), not function:prompt like zoxide/oh-my-posh — so unlike that # pair (see CLAUDE.md's prompt-hook-ordering note), registration order # relative to them doesn't matter here. # # The hook requires PowerShell 7.2+ and throws below that (direnv's own # generated script does `if ($PSVersionTable.PSVersion... -lt 7.2) { throw }`); # guard here so a PS 7.0/7.1 session degrades to a warning instead of a # broken one. Not unit tested: $PSVersionTable is AllScope+Constant, so it # cannot be shadowed or overridden from a test to exercise the else branch # without an actual sub-7.2 PowerShell install. The generated hook text # embeds direnv.exe's own resolved path, so it's still a pure function of # the binary for Get-DFCachedCommandOutput's path+mtime fingerprint (same # pattern as Tools/zoxide.ps1). [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '')] param() # Last direnv release with the Windows variable-unloading bug (direnv#1488); see # docs/external-dependencies.md. Raise it if a release still has the bug. $DFDirenvLastBuggyVersion = [version]'2.37.1' function Find-DFGitBash { <# .SYNOPSIS Returns Git for Windows' bash.exe: the first <ancestor>\bin\bash.exe above git.exe, never one under Windows. #> param([string]$GitPath = (Get-Command git.exe -CommandType Application -ErrorAction Ignore | Select-Object -First 1).Source) if (-not $GitPath) { return } # A scoop shim (shims\git.exe) sits nowhere near Git's own folder; follow it # to the real git.exe first (Resolve-DFExecutableTarget reads the .shim file). $GitPath = Resolve-DFExecutableTarget -Path $GitPath $dir = Split-Path $GitPath -Parent while ($dir) { $bash = Join-Path $dir 'bin\bash.exe' if ((Test-Path -LiteralPath $bash -PathType Leaf) -and -not ($Env:WINDIR -and $bash -like "$Env:WINDIR\*")) { return $bash } $parent = Split-Path $dir -Parent if (-not $parent -or $parent -eq $dir) { return } $dir = $parent } } function Test-DFDirenvBuggyVersion { <# .SYNOPSIS True when direnv's version text is at or below the last release with the Windows unloading bug. #> param([AllowEmptyString()][string]$VersionText) if ($VersionText -notmatch '(\d+\.\d+\.\d+)') { return $false } [version]$Matches[1] -le $DFDirenvLastBuggyVersion } function Initialize-DFRoleProjectEnv { # Called by DotForge only when direnv wins the project-env role. param([PSCustomObject]$Tool, [string]$Role) # direnv.toml's bash_path, when set, still wins over DIRENV_BASH (direnv's own # precedence), and DotForge never writes direnv.toml. if (-not $Env:DIRENV_BASH) { $gitBash = Find-DFGitBash if ($gitBash) { $Env:DIRENV_BASH = $gitBash } else { Write-Warning "DotForge: direnv needs Git for Windows' bash; set bash_path in direnv.toml or DIRENV_BASH." } } $direnvVersion = Get-DFCachedCommandOutput -Name 'direnv-version' -Executable 'direnv' -Generate { direnv version | Out-String } if (Test-DFDirenvBuggyVersion -VersionText $direnvVersion) { Write-Warning ("DotForge: direnv $($direnvVersion.Trim()) on Windows unloads environment variables it didn't set (direnv#1488). " + "Consider ps-dotenv: `$DFConfig.Defaults = @{ 'project-env' = 'ps-dotenv' }") } if ($PSVersionTable.PSVersion -ge [version]'7.2') { Invoke-Expression (Get-DFCachedCommandOutput -Name 'direnv-hook' -Executable 'direnv' -Generate { direnv hook pwsh | Out-String }) } else { Write-Warning "DotForge: direnv requires PowerShell 7.2+ (found $($PSVersionTable.PSVersion)) — hook not installed." } } |