Skip to content

Strengthen PR test trust baseline #81

Strengthen PR test trust baseline

Strengthen PR test trust baseline #81

Workflow file for this run

name: CI — dotnet
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
build-and-test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: '10.0.x'
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build --no-restore -c Release
- name: Test (unit + MCP, excluding Integration)
run: |
dotnet test tests/Unityctl.Shared.Tests --no-build -c Release --verbosity normal
dotnet test tests/Unityctl.Core.Tests --no-build -c Release --verbosity normal
dotnet test tests/Unityctl.Cli.Tests --no-build -c Release --verbosity normal
dotnet test tests/Unityctl.Mcp.Tests --no-build -c Release --verbosity normal
- name: Publish CLI
run: >
dotnet publish src/Unityctl.Cli/Unityctl.Cli.csproj
-c Release
--self-contained false
-o publish/cli
- name: Pack CLI tool
run: >
dotnet pack src/Unityctl.Cli/Unityctl.Cli.csproj
-c Release
--no-build
-o publish/packages
- name: Smoke published CLI (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
function Invoke-UnityctlSmoke {
param(
[string]$Exe,
[string[]]$Arguments,
[int[]]$AllowedExitCodes,
[string]$OutputPath,
[string]$Name
)
$psi = [System.Diagnostics.ProcessStartInfo]::new()
$psi.FileName = $Exe
foreach ($argument in $Arguments) {
[void]$psi.ArgumentList.Add($argument)
}
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.UseShellExecute = $false
$process = [System.Diagnostics.Process]::Start($psi)
$stdout = $process.StandardOutput.ReadToEnd()
$stderr = $process.StandardError.ReadToEnd()
$process.WaitForExit()
if ($OutputPath) {
$stdout | Set-Content -Path $OutputPath -Encoding utf8
} elseif ($stdout) {
Write-Host $stdout
}
if ($stderr) {
Write-Host $stderr
}
if ($AllowedExitCodes -notcontains $process.ExitCode) {
throw "$Name exited with unexpected code $($process.ExitCode)"
}
}
Invoke-UnityctlSmoke -Exe "./publish/cli/unityctl.exe" -Arguments @("--help") -AllowedExitCodes @(0, 1) -Name "published CLI help smoke"
Invoke-UnityctlSmoke -Exe "./publish/cli/unityctl.exe" -Arguments @("schema", "--format", "json") -AllowedExitCodes @(0) -OutputPath "publish/schema.json" -Name "published CLI schema smoke"
Invoke-UnityctlSmoke -Exe "./publish/cli/unityctl.exe" -Arguments @("tools", "--json") -AllowedExitCodes @(0) -OutputPath "publish/tools.json" -Name "published CLI tools smoke"
$schema = Get-Content publish/schema.json -Raw | ConvertFrom-Json
$tools = Get-Content publish/tools.json -Raw | ConvertFrom-Json
$schemaNames = @($schema.commands | ForEach-Object { $_.name } | Sort-Object)
$toolNames = @($tools | ForEach-Object { $_.name } | Sort-Object)
if (($schemaNames -join "`n") -ne ($toolNames -join "`n")) {
throw "schema and tools command names drifted"
}
foreach ($required in @("doctor", "check", "workflow-verify", "scene-snapshot", "scene-diff", "player-settings")) {
if ($schemaNames -notcontains $required) {
throw "published CLI schema is missing required command '$required'"
}
}
New-Item -ItemType Directory -Force publish/smoke-project/Packages, publish/smoke-project/ProjectSettings | Out-Null
'{"dependencies":{}}' | Set-Content -Path publish/smoke-project/Packages/manifest.json -Encoding utf8
'm_EditorVersion: 6000.0.64f1' | Set-Content -Path publish/smoke-project/ProjectSettings/ProjectVersion.txt -Encoding utf8
Invoke-UnityctlSmoke -Exe "./publish/cli/unityctl.exe" -Arguments @("doctor", "--project", "publish/smoke-project", "--json") -AllowedExitCodes @(0, 1) -OutputPath "publish/doctor.json" -Name "doctor smoke"
$doctor = Get-Content publish/doctor.json -Raw | ConvertFrom-Json
foreach ($property in @("editor", "plugin", "ipc", "summary")) {
if (-not $doctor.PSObject.Properties[$property]) {
throw "doctor JSON is missing '$property'"
}
}
Invoke-UnityctlSmoke -Exe "./publish/cli/unityctl.exe" -Arguments @("check", "--project", "publish/smoke-project", "--type", "compile", "--json") -AllowedExitCodes @(0, 1) -OutputPath "publish/check.json" -Name "check smoke"
$check = Get-Content publish/check.json -Raw | ConvertFrom-Json
foreach ($property in @("statusCode", "success", "message")) {
if (-not $check.PSObject.Properties[$property]) {
throw "check JSON is missing '$property'"
}
}
'{"name":"smoke","steps":[{"id":"validate","kind":"projectValidate"}]}' | Set-Content -Path publish/smoke-verify.json -Encoding utf8
Invoke-UnityctlSmoke -Exe "./publish/cli/unityctl.exe" -Arguments @("workflow", "verify", "--file", "publish/smoke-verify.json", "--project", "publish/smoke-project", "--artifacts-dir", "publish/smoke-artifacts", "--json") -AllowedExitCodes @(0, 1) -OutputPath "publish/workflow.json" -Name "workflow verify smoke"
$workflow = Get-Content publish/workflow.json -Raw | ConvertFrom-Json
foreach ($property in @("passed", "summary", "steps", "artifacts")) {
if (-not $workflow.PSObject.Properties[$property]) {
throw "workflow verify JSON is missing '$property'"
}
}
- name: Smoke local dotnet tool install (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
function Invoke-UnityctlSmoke {
param(
[string]$Exe,
[string[]]$Arguments,
[int[]]$AllowedExitCodes,
[string]$OutputPath,
[string]$Name
)
$psi = [System.Diagnostics.ProcessStartInfo]::new()
$psi.FileName = $Exe
foreach ($argument in $Arguments) {
[void]$psi.ArgumentList.Add($argument)
}
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.UseShellExecute = $false
$process = [System.Diagnostics.Process]::Start($psi)
$stdout = $process.StandardOutput.ReadToEnd()
$stderr = $process.StandardError.ReadToEnd()
$process.WaitForExit()
if ($OutputPath) {
$stdout | Set-Content -Path $OutputPath -Encoding utf8
} elseif ($stdout) {
Write-Host $stdout
}
if ($stderr) {
Write-Host $stderr
}
if ($AllowedExitCodes -notcontains $process.ExitCode) {
throw "$Name exited with unexpected code $($process.ExitCode)"
}
}
$package = Get-ChildItem publish/packages/unityctl.*.nupkg | Select-Object -First 1
if (-not ($package.BaseName -match '^unityctl\.(.+)$')) {
throw "Could not infer unityctl package version from $($package.Name)"
}
$version = $Matches[1]
dotnet tool install unityctl --tool-path publish/tool --add-source publish/packages --version $version --no-cache
Invoke-UnityctlSmoke -Exe "./publish/tool/unityctl.exe" -Arguments @("--help") -AllowedExitCodes @(0, 1) -Name "installed tool help smoke"
Invoke-UnityctlSmoke -Exe "./publish/tool/unityctl.exe" -Arguments @("schema", "--format", "json") -AllowedExitCodes @(0) -OutputPath "publish/tool-schema.json" -Name "installed tool schema smoke"
Invoke-UnityctlSmoke -Exe "./publish/tool/unityctl.exe" -Arguments @("tools", "--json") -AllowedExitCodes @(0) -OutputPath "publish/tool-tools.json" -Name "installed tool tools smoke"
Get-Content publish/tool-schema.json -Raw | ConvertFrom-Json | Out-Null
Get-Content publish/tool-tools.json -Raw | ConvertFrom-Json | Out-Null
Invoke-UnityctlSmoke -Exe "./publish/tool/unityctl.exe" -Arguments @("doctor", "--project", "publish/smoke-project", "--json") -AllowedExitCodes @(0, 1) -OutputPath "publish/tool-doctor.json" -Name "installed tool doctor smoke"
Get-Content publish/tool-doctor.json -Raw | ConvertFrom-Json | Out-Null
Invoke-UnityctlSmoke -Exe "./publish/tool/unityctl.exe" -Arguments @("check", "--project", "publish/smoke-project", "--type", "compile", "--json") -AllowedExitCodes @(0, 1) -OutputPath "publish/tool-check.json" -Name "installed tool check smoke"
Get-Content publish/tool-check.json -Raw | ConvertFrom-Json | Out-Null
Invoke-UnityctlSmoke -Exe "./publish/tool/unityctl.exe" -Arguments @("workflow", "verify", "--file", "publish/smoke-verify.json", "--project", "publish/smoke-project", "--artifacts-dir", "publish/smoke-artifacts-tool", "--json") -AllowedExitCodes @(0, 1) -OutputPath "publish/tool-workflow.json" -Name "installed tool workflow verify smoke"
Get-Content publish/tool-workflow.json -Raw | ConvertFrom-Json | Out-Null
- name: Smoke published CLI (Unix)
if: runner.os != 'Windows'
shell: bash
run: |
chmod +x ./publish/cli/unityctl
./publish/cli/unityctl --help
./publish/cli/unityctl schema --format json > publish/schema.json
./publish/cli/unityctl tools --json > publish/tools.json
python3 - <<'PY'
import json
with open("publish/schema.json", encoding="utf-8") as f:
schema = json.load(f)
with open("publish/tools.json", encoding="utf-8") as f:
tools = json.load(f)
schema_names = sorted(command["name"] for command in schema["commands"])
tool_names = sorted(command["name"] for command in tools)
if schema_names != tool_names:
raise SystemExit("schema and tools command names drifted")
for required in ("doctor", "check", "workflow-verify", "scene-snapshot", "scene-diff", "player-settings"):
if required not in schema_names:
raise SystemExit(f"published CLI schema is missing required command '{required}'")
PY
mkdir -p publish/smoke-project/Packages publish/smoke-project/ProjectSettings
printf '{"dependencies":{}}\n' > publish/smoke-project/Packages/manifest.json
printf 'm_EditorVersion: 6000.0.64f1\n' > publish/smoke-project/ProjectSettings/ProjectVersion.txt
set +e
doctor_json="$(./publish/cli/unityctl doctor --project publish/smoke-project --json)"
doctor_exit=$?
set -e
if [ "$doctor_exit" -ne 0 ] && [ "$doctor_exit" -ne 1 ]; then
echo "doctor smoke exited with unexpected code $doctor_exit" >&2
exit 1
fi
DOCTOR_JSON="$doctor_json" python3 - <<'PY'
import json
import os
doctor = json.loads(os.environ["DOCTOR_JSON"])
for key in ("editor", "plugin", "ipc", "summary"):
if key not in doctor:
raise SystemExit(f"doctor JSON is missing '{key}'")
PY
set +e
check_json="$(./publish/cli/unityctl check --project publish/smoke-project --type compile --json)"
check_exit=$?
set -e
if [ "$check_exit" -ne 0 ] && [ "$check_exit" -ne 1 ]; then
echo "check smoke exited with unexpected code $check_exit" >&2
exit 1
fi
CHECK_JSON="$check_json" python3 - <<'PY'
import json
import os
check = json.loads(os.environ["CHECK_JSON"])
for key in ("statusCode", "success", "message"):
if key not in check:
raise SystemExit(f"check JSON is missing '{key}'")
PY
printf '{"name":"smoke","steps":[{"id":"validate","kind":"projectValidate"}]}\n' > publish/smoke-verify.json
set +e
workflow_json="$(./publish/cli/unityctl workflow verify --file publish/smoke-verify.json --project publish/smoke-project --artifacts-dir publish/smoke-artifacts --json)"
workflow_exit=$?
set -e
if [ "$workflow_exit" -ne 0 ] && [ "$workflow_exit" -ne 1 ]; then
echo "workflow verify smoke exited with unexpected code $workflow_exit" >&2
exit 1
fi
WORKFLOW_JSON="$workflow_json" python3 - <<'PY'
import json
import os
workflow = json.loads(os.environ["WORKFLOW_JSON"])
for key in ("passed", "summary", "steps", "artifacts"):
if key not in workflow:
raise SystemExit(f"workflow verify JSON is missing '{key}'")
PY
- name: Smoke local dotnet tool install (Unix)
if: runner.os != 'Windows'
shell: bash
run: |
package="$(find publish/packages -maxdepth 1 -name 'unityctl.*.nupkg' | head -n 1)"
version="$(basename "$package" .nupkg)"
version="${version#unityctl.}"
dotnet tool install unityctl --tool-path publish/tool --add-source publish/packages --version "$version" --no-cache
./publish/tool/unityctl --help >/dev/null
./publish/tool/unityctl schema --format json >/dev/null
./publish/tool/unityctl tools --json >/dev/null
set +e
doctor_json="$(./publish/tool/unityctl doctor --project publish/smoke-project --json)"
doctor_exit=$?
set -e
if [ "$doctor_exit" -ne 0 ] && [ "$doctor_exit" -ne 1 ]; then
echo "installed tool doctor smoke exited with unexpected code $doctor_exit" >&2
exit 1
fi
DOCTOR_JSON="$doctor_json" python3 - <<'PY'
import json
import os
json.loads(os.environ["DOCTOR_JSON"])
PY
set +e
check_json="$(./publish/tool/unityctl check --project publish/smoke-project --type compile --json)"
check_exit=$?
set -e
if [ "$check_exit" -ne 0 ] && [ "$check_exit" -ne 1 ]; then
echo "installed tool check smoke exited with unexpected code $check_exit" >&2
exit 1
fi
CHECK_JSON="$check_json" python3 - <<'PY'
import json
import os
json.loads(os.environ["CHECK_JSON"])
PY
set +e
workflow_json="$(./publish/tool/unityctl workflow verify --file publish/smoke-verify.json --project publish/smoke-project --artifacts-dir publish/smoke-artifacts-tool --json)"
workflow_exit=$?
set -e
if [ "$workflow_exit" -ne 0 ] && [ "$workflow_exit" -ne 1 ]; then
echo "installed tool workflow verify smoke exited with unexpected code $workflow_exit" >&2
exit 1
fi
WORKFLOW_JSON="$workflow_json" python3 - <<'PY'
import json
import os
json.loads(os.environ["WORKFLOW_JSON"])
PY