Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ if ($env:CAPA_PRINT_QUIET -eq "1") {
}

# Colors
$ColorReset = "`e[0m"
$ColorRed = "`e[31m"
$ColorGreen = "`e[32m"
$ColorYellow = "`e[33m"
$ColorBlue = "`e[34m"
$ColorReset = "$([char]27)[0m"
$ColorRed = "$([char]27)[31m"
$ColorGreen = "$([char]27)[32m"
$ColorYellow = "$([char]27)[33m"
$ColorBlue = "$([char]27)[34m"

function Show-Usage {
Write-Host @"
Expand Down Expand Up @@ -108,7 +108,7 @@ function Write-Info {
function Write-Success {
param([string]$Message)
if (-not $Quiet) {
Write-Host "${ColorGreen}✓${ColorReset} $Message"
Write-Host "${ColorGreen}OK${ColorReset} $Message"
}
}

Expand Down Expand Up @@ -250,9 +250,9 @@ function Install-Capa {
$bannerPad = [Math]::Max(1, $bannerInner - $bannerTitle.Length)
$bannerSpaces = " " * $bannerPad
Write-Host ""
Write-Host "${ColorGreen}╔═══════════════════════════════════════╗${ColorReset}"
Write-Host "${ColorGreen}║${bannerTitle}${bannerSpaces}║${ColorReset}"
Write-Host "${ColorGreen}╚═══════════════════════════════════════╝${ColorReset}"
Write-Host "${ColorGreen}+---------------------------------------+${ColorReset}"
Write-Host "${ColorGreen}|${bannerTitle}${bannerSpaces}|${ColorReset}"
Write-Host "${ColorGreen}+---------------------------------------+${ColorReset}"
Write-Host ""

# Detect architecture
Expand Down Expand Up @@ -321,13 +321,13 @@ function Install-Capa {
# Replace existing binary (fixes #19). On Windows a running .exe is locked
# and cannot be overwritten, but it *can* be renamed within the same
# directory while it runs (the OS keeps the file handle regardless of name).
# So move the old binary aside first — that frees $destPath — then drop the
# So move the old binary aside first (that frees $destPath), then drop the
# new binary in. This makes the upgrade succeed even if the previous capa
# process hasn't fully exited yet, instead of relying on a timing race.
$backupPath = "$destPath.old"
if (Test-Path $destPath) {
# Remove any leftover backup from a prior upgrade (best-effort; harmless
# if it's still locked — it'll be cleaned on the next run).
# if it's still locked; it'll be cleaned on the next run).
if (Test-Path $backupPath) {
Remove-Item -LiteralPath $backupPath -Force -ErrorAction SilentlyContinue
}
Expand Down Expand Up @@ -375,7 +375,7 @@ function Install-Capa {
}

# New binary is in place; clean up the old one. This will fail silently if
# the previous capa process is still running (the file stays locked) — the
# the previous capa process is still running (the file stays locked); the
# leftover .old is then removed by the next upgrade.
if (Test-Path $backupPath) {
Remove-Item -LiteralPath $backupPath -Force -ErrorAction SilentlyContinue
Expand All @@ -388,9 +388,9 @@ function Install-Capa {

# Print success message
Write-Host ""
Write-Host "${ColorGreen}╔═══════════════════════════════════════╗${ColorReset}"
Write-Host "${ColorGreen}║ CAPA installed successfully! 🎉 ║${ColorReset}"
Write-Host "${ColorGreen}╚═══════════════════════════════════════╝${ColorReset}"
Write-Host "${ColorGreen}+---------------------------------------+${ColorReset}"
Write-Host "${ColorGreen}| CAPA installed successfully! |${ColorReset}"
Write-Host "${ColorGreen}+---------------------------------------+${ColorReset}"
Write-Host ""
Write-Host "To get started, run:"
Write-Host " ${ColorBlue}capa init${ColorReset} # Initialize a new project"
Expand Down
9 changes: 9 additions & 0 deletions src/cli/commands/__tests__/upgrade-plan.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { describe, it, expect } from 'bun:test';
import { createHash } from 'crypto';
import { readFileSync } from 'fs';
import { join } from 'path';
import {
assertSha256Match,
formatUpgradePreview,
Expand Down Expand Up @@ -131,4 +133,11 @@ describe('capa upgrade planning', () => {
const map = parseSha256Sums(`${digest} install.sh\n`);
expect(map.get('install.sh')).toBe(digest);
});

// `capa upgrade` runs install.ps1 via Windows PowerShell 5.1, which reads a
// BOM-less file as Windows-1252; any non-ASCII byte can break parsing.
it('keeps install.ps1 ASCII-only so Windows PowerShell 5.1 can parse it', () => {
const bytes = readFileSync(join(import.meta.dir, '../../../../install.ps1'));
expect(bytes.findIndex((b) => b > 0x7f)).toBe(-1);
});
});
38 changes: 8 additions & 30 deletions src/cli/commands/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { getServerStatus, stopServer } from '../utils/server-manager';
import {
header,
footer,
success,
info,
error,
isYes,
Expand Down Expand Up @@ -89,35 +88,14 @@ export async function upgradeCommand(): Promise<void> {
CAPA_VERSION: plan.version,
};

if (process.platform === 'win32') {
const proc = Bun.spawn(
[
'powershell.exe',
'-NoProfile',
'-ExecutionPolicy',
'Bypass',
'-File',
installerPath,
],
{
stdout: 'ignore',
stderr: 'ignore',
stdin: 'ignore',
detached: true,
env,
},
);
proc.unref();
success(
'Verified installer started. This window will close; capa will update in the background.',
);
info(
'Restart your terminal when the installer finishes, then run `capa --version` to confirm.',
);
process.exit(0);
}

const proc = Bun.spawn(['bash', installerPath], {
// Run the installer in the foreground on every platform so failures are
// visible. On Windows the installer renames this running capa.exe aside
// before dropping the new one in, so it doesn't need us to exit first.
const cmd =
process.platform === 'win32'
? ['powershell.exe', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', installerPath]
: ['bash', installerPath];
const proc = Bun.spawn(cmd, {
stdout: 'inherit',
stderr: 'inherit',
stdin: 'inherit',
Expand Down
Loading