Repo scripts assume the PM-activated environment, so running one without activation fails much later with a confusing ImportError. Add the two halves covering both invocation paths: - scripts/_activation.py: require_activation() exits immediately, naming the exact command for the caller's shell (source ./activate on POSIX, . .\activate.ps1 on a native Windows host), before any heavy import. - scripts/_hermes-python: the POSIX shebang target. `#!/usr/bin/env -S bash -c '...'` hands itself the target path through bash -c's $0, sources activate, then execs the interpreter on the same file -- so tracebacks and __file__ still point at the real script and ./scripts/foo.py works from any cwd with no manual source. __HERMES_ACTIVATED changes from a bare "1" to the installed-state file the environment was composed against, so one value carries activation, which checkout activated it, and a staleness stamp. The prologue compares that file against uv.lock / pyproject.toml / pm/lock.json with the `-nt` builtin -- no process spawn -- and re-activates once when the inherited environment predates its inputs. pm rewrites that file only on a real sync, so the check settles back to current rather than re-syncing on every run. A legacy "1" keeps working: require_activation() tests non-emptiness, and the prologue's [ -e ] fails on it, so it activates once and upgrades.
78 lines
3.5 KiB
PowerShell
Executable File
78 lines
3.5 KiB
PowerShell
Executable File
# Source this file to sync and apply the PM environment; deactivate restores it.
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$OutputEncoding = [System.Console]::OutputEncoding = [System.Console]::InputEncoding = [System.Text.Encoding]::UTF8
|
|
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
|
|
|
|
$repo = $PSScriptRoot
|
|
$bootstrapSaved = @{}
|
|
foreach ($key in @('PYTHONPATH', 'PYTHONHOME', 'VIRTUAL_ENV')) {
|
|
$bootstrapSaved[$key] = [Environment]::GetEnvironmentVariable($key)
|
|
Remove-Item "env:$key" -ErrorAction SilentlyContinue
|
|
}
|
|
try {
|
|
# Run separately so setup's exit/failure cannot terminate the sourced shell.
|
|
$shell = (Get-Process -Id $PID).Path
|
|
& $shell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "$repo\setup-hermes.ps1" -RuntimeOnly | Out-Host
|
|
if ($LASTEXITCODE -ne 0) { throw 'activate: setup failed; shell environment unchanged' }
|
|
} finally {
|
|
foreach ($key in $bootstrapSaved.Keys) {
|
|
if ($null -eq $bootstrapSaved[$key]) { Remove-Item "env:$key" -ErrorAction SilentlyContinue }
|
|
else { Set-Item "env:$key" $bootstrapSaved[$key] }
|
|
}
|
|
}
|
|
if (Test-Path function:deactivate) { deactivate }
|
|
$py = $null
|
|
foreach ($candidate in @("$repo\.venv\Scripts\python.exe", "$repo\venv\Scripts\python.exe")) {
|
|
if (Test-Path -LiteralPath $candidate) { $py = $candidate; break }
|
|
}
|
|
if (-not $py) {
|
|
$roots = @($env:HERMES_RUNTIME_DIR, "$repo\..\tools")
|
|
$homeRoot = if ($env:HERMES_HOME) { $env:HERMES_HOME } else { "$env:LOCALAPPDATA\hermes" }
|
|
$roots += (Join-Path $homeRoot 'tools')
|
|
foreach ($root in $roots) {
|
|
if (-not $root) { continue }
|
|
foreach ($entry in @(Get-ChildItem -LiteralPath $root -Directory -Filter 'python-*' -ErrorAction SilentlyContinue)) {
|
|
$candidate = Join-Path $entry.FullName 'python.exe'
|
|
if (Test-Path -LiteralPath $candidate) { $py = $candidate; break }
|
|
}
|
|
if ($py) { break }
|
|
}
|
|
}
|
|
if (-not $py) { throw 'activate: no bootstrap Python found; run setup-hermes.ps1' }
|
|
$priorPath = $env:PYTHONPATH
|
|
$priorHome = $env:PYTHONHOME
|
|
try {
|
|
$env:PYTHONPATH = $repo
|
|
Remove-Item env:PYTHONHOME -ErrorAction SilentlyContinue
|
|
$json = (& $py -m hermes_cli.runtime_paths) -join "`n"
|
|
if ($LASTEXITCODE -ne 0) { throw 'activate: could not read the installed environment' }
|
|
$composed = $json | ConvertFrom-Json
|
|
} finally {
|
|
if ($null -eq $priorPath) { Remove-Item env:PYTHONPATH -ErrorAction SilentlyContinue } else { $env:PYTHONPATH = $priorPath }
|
|
if ($null -eq $priorHome) { Remove-Item env:PYTHONHOME -ErrorAction SilentlyContinue } else { $env:PYTHONHOME = $priorHome }
|
|
}
|
|
# __HERMES_ACTIVATED (the sentinel repo scripts and the shebang prologue read)
|
|
# is part of the composed env, so it is saved and restored with the rest.
|
|
$global:_hermesKeys = @($composed.PSObject.Properties.Name)
|
|
$global:_hermesSaved = @{}
|
|
foreach ($key in $global:_hermesKeys) {
|
|
$global:_hermesSaved[$key] = [pscustomobject]@{
|
|
WasSet = (Test-Path "env:$key")
|
|
Value = [Environment]::GetEnvironmentVariable($key)
|
|
}
|
|
}
|
|
foreach ($property in $composed.PSObject.Properties) {
|
|
Set-Item -Path "env:$($property.Name)" -Value ([string]$property.Value)
|
|
}
|
|
function global:deactivate {
|
|
foreach ($key in $global:_hermesKeys) {
|
|
$saved = $global:_hermesSaved[$key]
|
|
if ($saved.WasSet) { Set-Item -Path "env:$key" -Value $saved.Value }
|
|
else { Remove-Item -Path "env:$key" -ErrorAction SilentlyContinue }
|
|
}
|
|
$global:_hermesKeys = $null
|
|
$global:_hermesSaved = $null
|
|
Remove-Item function:deactivate
|
|
}
|