fix(windows): skip install if Tailscale already present (avoid MSI 1603)

A prior .exe(NSIS) install leaves Tailscale on disk but not on PATH; the MSI
then fails with error 1603 trying to install over it. Now detect the existing
binary at C:\Program Files\Tailscale\tailscale.exe and skip installation, and
treat a nonzero msiexec exit as success when the binary is already present.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
PharmQ Admin
2026-05-31 01:17:07 +00:00
parent 32118e7c6c
commit 99fd031d5a
2 changed files with 43 additions and 16 deletions

View File

@@ -100,12 +100,20 @@ function Test-Requirements {
function Install-Tailscale { function Install-Tailscale {
Write-Status "Checking Tailscale installation..." Write-Status "Checking Tailscale installation..."
# Check existing installation # Check existing installation (PATH or the default install directory).
# IMPORTANT: a prior run may have installed Tailscale via the .exe (NSIS) even
# though it wasn't on PATH. Re-installing over it with the MSI fails with
# error 1603. So if the binary already exists, skip installation entirely and
# just make sure it's on PATH for this session.
$tailscaleExe = "C:\Program Files\Tailscale\tailscale.exe"
$tailscalePath = Get-Command "tailscale" -ErrorAction SilentlyContinue $tailscalePath = Get-Command "tailscale" -ErrorAction SilentlyContinue
if ($tailscalePath) { if ($tailscalePath -or (Test-Path $tailscaleExe)) {
$version = & tailscale version 2>$null | Select-Object -First 1 Write-Info "Tailscale is already installed. Skipping installation."
Write-Info "Tailscale is already installed." if (-not $tailscalePath -and ($env:Path -notlike "*Tailscale*")) {
Write-Info "Current version: $version" $env:Path = "$env:Path;C:\Program Files\Tailscale"
}
$version = & $tailscaleExe version 2>$null | Select-Object -First 1
if ($version) { Write-Info "Current version: $version" }
return return
} }
@@ -133,14 +141,20 @@ function Install-Tailscale {
-Wait -PassThru -Wait -PassThru
# 0 = success, 3010 = success but reboot required # 0 = success, 3010 = success but reboot required
if ($proc.ExitCode -ne 0 -and $proc.ExitCode -ne 3010) { if ($proc.ExitCode -ne 0 -and $proc.ExitCode -ne 3010) {
throw "msiexec returned exit code $($proc.ExitCode). See log: $logPath" # 1603 etc. often means a conflicting/partial install already exists.
# If the binary is nonetheless present, treat it as installed and move on;
# otherwise surface the failure with the log path.
if (Test-Path $tailscaleExe) {
Write-Warning "msiexec exit code $($proc.ExitCode), but Tailscale is already present. Continuing."
} else {
throw "msiexec returned exit code $($proc.ExitCode). See log: $logPath"
}
} }
# Refresh PATH environment variable # Refresh PATH environment variable
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
# Make sure the install dir is on PATH for this session # Make sure the install dir is on PATH for this session
$tailscaleExe = "C:\Program Files\Tailscale\tailscale.exe"
if (Test-Path $tailscaleExe) { if (Test-Path $tailscaleExe) {
$currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine") $currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
if ($currentPath -notlike "*Tailscale*") { if ($currentPath -notlike "*Tailscale*") {

View File

@@ -106,12 +106,19 @@ function Test-Requirements {
function Install-Tailscale { function Install-Tailscale {
Write-Status "Tailscale 클라이언트 확인 중..." Write-Status "Tailscale 클라이언트 확인 중..."
# 기존 설치 확인 # 기존 설치 확인 (PATH 또는 기본 설치 경로).
# 중요: 이전 실행에서 .exe(NSIS)로 이미 설치됐는데 PATH엔 안 잡혀 있을 수
# 있다. 그 위에 MSI로 덮어쓰면 오류 1603으로 실패한다. 그래서 바이너리가
# 이미 있으면 설치를 통째로 건너뛰고 이번 세션 PATH에만 추가한다.
$tailscaleExe = "C:\Program Files\Tailscale\tailscale.exe"
$tailscalePath = Get-Command "tailscale" -ErrorAction SilentlyContinue $tailscalePath = Get-Command "tailscale" -ErrorAction SilentlyContinue
if ($tailscalePath) { if ($tailscalePath -or (Test-Path $tailscaleExe)) {
$version = & tailscale version 2>$null | Select-Object -First 1 Write-Info "Tailscale이 이미 설치되어 있습니다. 설치를 건너뜁니다."
Write-Info "Tailscale이 이미 설치되어 있습니다." if (-not $tailscalePath -and ($env:Path -notlike "*Tailscale*")) {
Write-Info "현재 버전: $version" $env:Path = "$env:Path;C:\Program Files\Tailscale"
}
$version = & $tailscaleExe version 2>$null | Select-Object -First 1
if ($version) { Write-Info "현재 버전: $version" }
return return
} }
@@ -138,14 +145,20 @@ function Install-Tailscale {
-Wait -PassThru -Wait -PassThru
# 0 = 성공, 3010 = 성공(재부팅 필요) # 0 = 성공, 3010 = 성공(재부팅 필요)
if ($proc.ExitCode -ne 0 -and $proc.ExitCode -ne 3010) { if ($proc.ExitCode -ne 0 -and $proc.ExitCode -ne 3010) {
throw "msiexec 종료 코드 $($proc.ExitCode). 로그: $logPath" # 1603 등은 충돌하는/불완전한 기존 설치가 있을 때 자주 난다.
# 그래도 바이너리가 있으면 설치된 것으로 보고 계속 진행하고,
# 없으면 로그 경로와 함께 실패를 알린다.
if (Test-Path $tailscaleExe) {
Write-Warning "msiexec 종료 코드 $($proc.ExitCode) 이지만 Tailscale이 이미 존재합니다. 계속 진행합니다."
} else {
throw "msiexec 종료 코드 $($proc.ExitCode). 로그: $logPath"
}
} }
# PATH 환경변수 새로고침 # PATH 환경변수 새로고침
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
# 이번 세션 PATH에 설치 경로 보장 # 이번 세션 PATH에 설치 경로 보장
$tailscaleExe = "C:\Program Files\Tailscale\tailscale.exe"
if (Test-Path $tailscaleExe) { if (Test-Path $tailscaleExe) {
$currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine") $currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
if ($currentPath -notlike "*Tailscale*") { if ($currentPath -notlike "*Tailscale*") {