diff --git a/scripts/install/install.ps1 b/scripts/install/install.ps1 index 8ebc4fbb82..6973d482e2 100644 --- a/scripts/install/install.ps1 +++ b/scripts/install/install.ps1 @@ -1,11 +1,18 @@ +[CmdletBinding()] param( - [string]$Release = "latest" + [string]$Release = $env:CODEX_RELEASE ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" +if ([string]::IsNullOrWhiteSpace($Release)) { + $Release = "latest" +} + +$NonInteractive = $env:CODEX_NON_INTERACTIVE -match "^(?i:1|true|yes)$" + function Write-Step { param( [string]$Message @@ -27,6 +34,10 @@ function Prompt-YesNo { [string]$Prompt ) + if ($NonInteractive) { + return $false + } + if ([Console]::IsInputRedirected -or [Console]::IsOutputRedirected) { return $false } @@ -55,6 +66,16 @@ function Normalize-Version { return $RawVersion } +function Assert-ValidReleaseVersion { + param( + [string]$Version + ) + + if ($Version -cne "latest" -and $Version -cnotmatch "^[0-9]+\.[0-9]+\.[0-9]+(?:-(?:alpha|beta)(?:\.[0-9]+)?)?$") { + throw "Invalid Codex release version: $Version. Expected latest or x.y.z[-alpha[.N]|-beta[.N]]." + } +} + function Find-ReleaseAssetMetadata { param( [string]$AssetName, @@ -141,6 +162,22 @@ function Path-Contains { return $false } +function Prepend-PathEntry { + param( + [string]$PathValue, + [string]$Entry + ) + + $needle = $Entry.TrimEnd("\") + $segments = @($Entry) + if (-not [string]::IsNullOrWhiteSpace($PathValue)) { + $segments += $PathValue.Split(";", [System.StringSplitOptions]::RemoveEmptyEntries) | + Where-Object { $_.TrimEnd("\") -ine $needle } + } + + return ($segments -join ";") +} + function Invoke-WithInstallLock { param( [string]$LockPath, @@ -181,6 +218,7 @@ function Remove-StaleInstallArtifacts { function Resolve-Version { $normalizedVersion = Normalize-Version -RawVersion $Release + Assert-ValidReleaseVersion -Version $normalizedVersion if ($normalizedVersion -ne "latest") { return $normalizedVersion } @@ -191,7 +229,9 @@ function Resolve-Version { exit 1 } - return (Normalize-Version -RawVersion $release.tag_name) + $resolvedVersion = Normalize-Version -RawVersion $release.tag_name + Assert-ValidReleaseVersion -Version $resolvedVersion + return $resolvedVersion } function Get-VersionFromBinary { @@ -839,7 +879,16 @@ try { Maybe-HandleConflictingInstall -Conflict $conflictingInstall $userPath = [Environment]::GetEnvironmentVariable("Path", "User") -if (-not (Path-Contains -PathValue $userPath -Entry $visibleBinDir)) { +$prioritizeVisibleBin = $null -ne $conflictingInstall +if ($prioritizeVisibleBin) { + $newUserPath = Prepend-PathEntry -PathValue $userPath -Entry $visibleBinDir + if ($newUserPath -cne $userPath) { + [Environment]::SetEnvironmentVariable("Path", $newUserPath, "User") + Write-Step "PATH updated for future PowerShell sessions." + } else { + Write-Step "$visibleBinDir is already first on PATH." + } +} elseif (-not (Path-Contains -PathValue $userPath -Entry $visibleBinDir)) { if ([string]::IsNullOrWhiteSpace($userPath)) { $newUserPath = $visibleBinDir } else { @@ -854,7 +903,9 @@ if (-not (Path-Contains -PathValue $userPath -Entry $visibleBinDir)) { Write-Step "PATH is already configured for future PowerShell sessions." } -if (-not (Path-Contains -PathValue $env:Path -Entry $visibleBinDir)) { +if ($prioritizeVisibleBin) { + $env:Path = Prepend-PathEntry -PathValue $env:Path -Entry $visibleBinDir +} elseif (-not (Path-Contains -PathValue $env:Path -Entry $visibleBinDir)) { if ([string]::IsNullOrWhiteSpace($env:Path)) { $env:Path = $visibleBinDir } else { diff --git a/scripts/install/install.sh b/scripts/install/install.sh index cc4fc91345..23980b0c5e 100755 --- a/scripts/install/install.sh +++ b/scripts/install/install.sh @@ -2,7 +2,8 @@ set -eu -RELEASE="latest" +RELEASE="${CODEX_RELEASE:-latest}" +NON_INTERACTIVE="${CODEX_NON_INTERACTIVE:-false}" BIN_DIR="${CODEX_INSTALL_DIR:-$HOME/.local/bin}" BIN_PATH="$BIN_DIR/codex" @@ -46,6 +47,19 @@ normalize_version() { esac } +validate_version() { + version="$1" + + if [ "$version" = "latest" ]; then + return + fi + + if ! printf '%s\n' "$version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-(alpha|beta)(\.[0-9]+)?)?$'; then + echo "Invalid Codex release version: $version. Expected latest or x.y.z[-alpha[.N]|-beta[.N]]." >&2 + exit 1 + fi +} + parse_args() { while [ "$#" -gt 0 ]; do case "$1" in @@ -60,6 +74,10 @@ parse_args() { --help | -h) cat </dev/null; then printf '%s [y/N] ' "$prompt" >/dev/tty if ! IFS= read -r answer