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
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Renderer manifests authenticate exact source bytes. Keep embedded text assets
# on LF across every checkout so Windows builds verify the same bytes.
crates/opsail-refit-codex/assets/*.css text eol=lf
crates/opsail-refit-codex/assets/*.js text eol=lf
crates/opsail-refit-codex/assets/*.json text eol=lf
133 changes: 133 additions & 0 deletions .github/scripts/refit-codex-smoke.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { spawnSync } from "node:child_process";
import process from "node:process";

const binary = process.env.OPSAIL_BINARY;
if (!binary) {
throw new Error("OPSAIL_BINARY must point to the native opsail executable");
}

function run(args) {
const result = spawnSync(binary, args, {
encoding: "utf8",
env: {
...process.env,
NO_COLOR: "1",
RUST_BACKTRACE: "0",
},
});

if (result.error) {
throw result.error;
}

return result;
}

function requireCondition(condition, message, result) {
if (condition) {
return;
}

const details = result
? `\nexit: ${result.status}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`
: "";
throw new Error(`${message}${details}`);
}

function parseDoctorReport(result) {
requireCondition(result.status === 0, "doctor did not return a report", result);
try {
return JSON.parse(result.stdout);
} catch (error) {
throw new Error(
`doctor stdout is not JSON: ${error.message}\nstdout:\n${result.stdout}`,
);
}
}

function check(report, name) {
return report.checks?.find((entry) => entry.name === name);
}

const help = run(["refit", "codex", "--help"]);
requireCondition(help.status === 0, "refit codex --help failed", help);
for (const command of ["enable", "disable", "status", "doctor", "update"]) {
requireCondition(
help.stdout.includes(command),
`refit codex --help does not list ${command}`,
help,
);
}

const doctor = run(["refit", "codex", "doctor"]);

if (process.platform === "win32") {
// GitHub's hosted Windows image does not contain the Microsoft Store app. The
// command must still initialize the Windows adapter and report that absence as
// an application check, rather than falling back to the unsupported platform.
const report = parseDoctorReport(doctor);
const platform = check(report, "platform");
const application = check(report, "application");

requireCondition(
report.supported === true,
"Windows was reported as unsupported",
doctor,
);
requireCondition(
report.ready === false,
"doctor was unexpectedly ready without ChatGPT",
doctor,
);
requireCondition(
platform?.state === "pass",
"Windows platform check did not pass",
doctor,
);
requireCondition(
application?.state === "fail",
"missing Store application was not reported by the application check",
doctor,
);
requireCondition(
application.message?.startsWith("target-not-found:"),
"missing Store application did not use target-not-found",
doctor,
);
} else if (process.platform === "darwin") {
const report = parseDoctorReport(doctor);
requireCondition(
report.supported === true,
"macOS was reported as unsupported",
doctor,
);
requireCondition(
check(report, "platform")?.state === "pass",
"macOS platform check did not pass",
doctor,
);
} else if (process.platform === "linux") {
if (doctor.status === 0) {
const report = parseDoctorReport(doctor);
requireCondition(
report.supported === false,
"Linux was unexpectedly reported as supported",
doctor,
);
requireCondition(
check(report, "platform")?.state === "fail",
"Linux platform check did not fail",
doctor,
);
} else {
requireCondition(
doctor.stderr.includes("[opsail-refit-codex:unsupported]"),
"Linux doctor did not return the bounded unsupported diagnostic",
doctor,
);
}
} else {
throw new Error(`unrecognized CI platform: ${process.platform}`);
}

console.log(`refit-codex native smoke passed on ${process.platform}`);
172 changes: 136 additions & 36 deletions .github/workflows/installers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ on:
pull_request:
paths:
- ".github/workflows/installers.yml"
- "scripts/install.ps1"
- "scripts/install.sh"
- "skills/bootstrap-opsail/scripts/install.ps1"
- "skills/bootstrap-opsail/scripts/install.sh"
push:
branches:
- main
paths:
- ".github/workflows/installers.yml"
- "scripts/install.ps1"
- "scripts/install.sh"
- "skills/bootstrap-opsail/scripts/install.ps1"
- "skills/bootstrap-opsail/scripts/install.sh"

permissions:
contents: read
Expand Down Expand Up @@ -41,17 +41,41 @@ jobs:
persist-credentials: false

- name: Check shell syntax
run: sh -n scripts/install.sh
run: sh -n skills/bootstrap-opsail/scripts/install.sh

- name: Install latest release
- name: Install latest and exact release
env:
OPSAIL_INSTALL_DIR: ${{ runner.temp }}/opsail-bin
shell: sh
run: |
scripts/install.sh
"$OPSAIL_INSTALL_DIR/opsail" --version
set -eu
skills/bootstrap-opsail/scripts/install.sh
latest_version_output="$("$OPSAIL_INSTALL_DIR/opsail" --version)"
case "$latest_version_output" in
"opsail "?*) resolved_version="${latest_version_output#opsail }" ;;
*) echo "unexpected version output: $latest_version_output" >&2; exit 1 ;;
esac
case "$resolved_version" in
*[!0-9A-Za-z.+_-]*) echo "unexpected version output: $latest_version_output" >&2; exit 1 ;;
esac
"$OPSAIL_INSTALL_DIR/opsail" read --help >/dev/null

exact_install_dir="$RUNNER_TEMP/opsail-bin-exact"
(
unset HOME
OPSAIL_INSTALL_DIR="$exact_install_dir"
OPSAIL_VERSION="$resolved_version"
export OPSAIL_INSTALL_DIR OPSAIL_VERSION
skills/bootstrap-opsail/scripts/install.sh
)

exact_version_output="$("$exact_install_dir/opsail" --version)"
if [ "$exact_version_output" != "$latest_version_output" ]; then
echo "exact install version mismatch: $exact_version_output" >&2
exit 1
fi
"$exact_install_dir/opsail" read --help >/dev/null

windows:
name: Install on windows-2025
runs-on: windows-2025
Expand All @@ -65,16 +89,20 @@ jobs:
- name: Check PowerShell syntax
shell: powershell
run: |
$tokens = $null
$errors = $null
[System.Management.Automation.Language.Parser]::ParseFile(
(Resolve-Path "scripts/install.ps1"),
[ref] $tokens,
[ref] $errors
) | Out-Null

if ($errors.Count -gt 0) {
$errors | ForEach-Object { Write-Error $_.Message }
$allErrors = @()
foreach ($scriptPath in @("skills/bootstrap-opsail/scripts/install.ps1")) {
$tokens = $null
$parseErrors = $null
[System.Management.Automation.Language.Parser]::ParseFile(
(Resolve-Path $scriptPath),
[ref] $tokens,
[ref] $parseErrors
) | Out-Null
$allErrors += $parseErrors
}

if ($allErrors.Count -gt 0) {
$allErrors | ForEach-Object { Write-Error $_.Message }
exit 1
}

Expand All @@ -85,17 +113,32 @@ jobs:
run: |
Set-StrictMode -Off
$ErrorActionPreference = "Continue"

Get-Content -Raw "scripts/install.ps1" | Invoke-Expression

if ($ErrorActionPreference -ne "Continue") {
throw "installer leaked ErrorActionPreference"
}
$originalUserPath = [Environment]::GetEnvironmentVariable("Path", "User")
$originalProcessPath = $env:Path
$originalUpdatePath = $env:OPSAIL_UPDATE_PATH

try {
$null = $opsailUndefinedStrictModeProbe
} catch {
throw "installer leaked strict mode"
$env:OPSAIL_UPDATE_PATH = $null
Get-Content -Raw "skills/bootstrap-opsail/scripts/install.ps1" | Invoke-Expression

$currentUserPath = [Environment]::GetEnvironmentVariable("Path", "User")
if ($currentUserPath -cne $originalUserPath) {
throw "installer changed the user PATH without OPSAIL_UPDATE_PATH=1"
}

if ($ErrorActionPreference -ne "Continue") {
throw "installer leaked ErrorActionPreference"
}

try {
$null = $opsailUndefinedStrictModeProbe
} catch {
throw "installer leaked strict mode"
}
} finally {
[Environment]::SetEnvironmentVariable("Path", $originalUserPath, "User")
$env:Path = $originalProcessPath
$env:OPSAIL_UPDATE_PATH = $originalUpdatePath
}

$ErrorActionPreference = "Stop"
Expand All @@ -111,17 +154,74 @@ jobs:
run: |
Set-StrictMode -Off
$ErrorActionPreference = "Continue"

Get-Content -Raw "scripts/install.ps1" | Invoke-Expression

if ($ErrorActionPreference -ne "Continue") {
throw "installer leaked ErrorActionPreference"
}
$originalUserPath = [Environment]::GetEnvironmentVariable("Path", "User")
$originalProcessPath = $env:Path
$originalOpsailVersion = $env:OPSAIL_VERSION
$originalUpdatePath = $env:OPSAIL_UPDATE_PATH

try {
$null = $opsailUndefinedStrictModeProbe
} catch {
throw "installer leaked strict mode"
$env:OPSAIL_UPDATE_PATH = $null
Get-Content -Raw "skills/bootstrap-opsail/scripts/install.ps1" | Invoke-Expression

$userPathAfterDefaultInstall = [Environment]::GetEnvironmentVariable("Path", "User")
if ($userPathAfterDefaultInstall -cne $originalUserPath) {
throw "installer changed the user PATH without OPSAIL_UPDATE_PATH=1"
}

$versionOutput = (& "$env:OPSAIL_INSTALL_DIR\opsail.exe" --version | Out-String).Trim()
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
if ($versionOutput -notmatch '^opsail ([0-9A-Za-z.+_-]+)$') {
throw "unexpected version output: $versionOutput"
}
$resolvedVersion = $Matches[1]

$seededUserPath = @(
$originalUserPath
"$env:OPSAIL_INSTALL_DIR\"
$env:OPSAIL_INSTALL_DIR.ToUpperInvariant()
) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
[Environment]::SetEnvironmentVariable("Path", ($seededUserPath -join ";"), "User")
$env:Path = "$env:Path;$env:OPSAIL_INSTALL_DIR\;$($env:OPSAIL_INSTALL_DIR.ToUpperInvariant())"

& "skills/bootstrap-opsail/scripts/install.ps1" `
-Version $resolvedVersion `
-InstallDir $env:OPSAIL_INSTALL_DIR `
-UpdatePath

$normalizedInstallDir = $env:OPSAIL_INSTALL_DIR.TrimEnd([char[]]"\/")
$matchingUserEntries = @(
[Environment]::GetEnvironmentVariable("Path", "User") -split ";" |
Where-Object { $_.Trim().TrimEnd([char[]]"\/") -ieq $normalizedInstallDir }
)
if ($matchingUserEntries.Count -ne 1) {
throw "installer did not normalize and deduplicate the user PATH entry"
}
if ($matchingUserEntries[0] -cne $env:OPSAIL_INSTALL_DIR) {
throw "installer did not preserve the canonical install directory in user PATH"
}

$matchingProcessEntries = @(
$env:Path -split ";" |
Where-Object { $_.Trim().TrimEnd([char[]]"\/") -ieq $normalizedInstallDir }
)
if ($matchingProcessEntries.Count -ne 1) {
throw "installer did not normalize and deduplicate the process PATH entry"
}

if ($ErrorActionPreference -ne "Continue") {
throw "installer leaked ErrorActionPreference"
}

try {
$null = $opsailUndefinedStrictModeProbe
} catch {
throw "installer leaked strict mode"
}
} finally {
[Environment]::SetEnvironmentVariable("Path", $originalUserPath, "User")
$env:Path = $originalProcessPath
$env:OPSAIL_VERSION = $originalOpsailVersion
$env:OPSAIL_UPDATE_PATH = $originalUpdatePath
}

$ErrorActionPreference = "Stop"
Expand Down
Loading