mirror of
https://gitea.com/gitea/runner.git
synced 2026-11-04 22:37:55 +00:00
enhance: test on Windows and fix what it found (#1348)
Adds a weekly and on-demand workflow that runs the cross-compiled tests on Windows Server 2025 Core in a [dockur/windows](https://github.com/dockur/windows) QEMU VM, and fixes the Windows bugs it found in host and UNC paths, env handling, shell quoting, post-task scripts, health checks, checkout, action symlinks, step summaries, Git client certificates and docker mode from a Windows host. Host-mode steps now emit UTF-8, `bash` falls back to Git's bash instead of the WSL launcher, the runner can run as a native Windows service, steps can keep background helpers with `RUNNER_TRACKING_ID`, and Windows arm64 binaries are released. Fixes: https://gitea.com/gitea/runner/issues/1339 Fixes: https://gitea.com/gitea/runner/issues/637 Fixes: https://gitea.com/gitea/runner/issues/1172 Fixes: https://gitea.com/gitea/runner/issues/347 Reviewed-on: https://gitea.com/gitea/runner/pulls/1348 Reviewed-by: bircni <[email protected]>
This commit is contained in:
1 parent
6e42621b4a
commit
d6f5b68a0d
85 files changed
+2578
-620
No files matched your search
@@ -0,0 +1,29 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
$shared = '\\host.lan\Data'
|
||||
try {
|
||||
Write-Output '==> Setting up Windows'
|
||||
Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem LongPathsEnabled 1
|
||||
foreach ($tool in @(
|
||||
@('git.7z.exe', 'https://github.com/git-for-windows/git/releases/download/v2.56.0.windows.3/PortableGit-2.56.0.3-64-bit.7z.exe', '4fc27b779829a36df77911e3f4dea09c66c790dd6c5f7dfa3b6a5479f1fe354f'),
|
||||
@('node.zip', 'https://nodejs.org/dist/v24.22.1/node-v24.22.1-win-x64.zip', 'd2046d28c2c3dcd211ac5145c0bf30e2047ef9dfb306693c8c6e2eccd8b95fa5'),
|
||||
@('pwsh.zip', 'https://github.com/PowerShell/PowerShell/releases/download/v7.6.8/PowerShell-7.6.8-win-x64.zip', 'b9f074f923c499b51ec99d313da8e9ae90d17a9f3adf00c73d6189f34d908528'))) {
|
||||
Write-Output "==> Downloading $($tool[1])"
|
||||
Invoke-WebRequest -UseBasicParsing $tool[1] -OutFile "$env:TEMP\$($tool[0])"
|
||||
if ((Get-FileHash "$env:TEMP\$($tool[0])").Hash -ne $tool[2]) { throw "checksum mismatch for $($tool[1])" }
|
||||
}
|
||||
Write-Output '==> Extracting git, node and pwsh'
|
||||
New-Item -ItemType Directory C:\pwsh | Out-Null
|
||||
@(
|
||||
Start-Process "$env:TEMP\git.7z.exe" -ArgumentList '-oC:\git', '-y' -PassThru
|
||||
Start-Process tar.exe -ArgumentList '-xf', "$env:TEMP\node.zip", '-C', 'C:\' -PassThru -NoNewWindow
|
||||
Start-Process tar.exe -ArgumentList '-xf', "$env:TEMP\pwsh.zip", '-C', 'C:\pwsh' -PassThru -NoNewWindow
|
||||
) | Wait-Process
|
||||
Rename-Item (Resolve-Path C:\node-*-win-x64).Path C:\node
|
||||
$env:Path = "C:\git\cmd;C:\git\bin;C:\pwsh;C:\node;$env:Path"
|
||||
Get-Command git, bash, node, pwsh | Out-Null
|
||||
} catch {
|
||||
$_ | Out-String | Tee-Object "$shared\error.txt"
|
||||
Set-Content "$shared\exitcode" 1
|
||||
exit 1
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
$results = '\\host.lan\Data'
|
||||
$root = $PSScriptRoot
|
||||
try {
|
||||
New-Item -ItemType Directory "$root\logs" | Out-Null
|
||||
$env:Path = "C:\git\cmd;C:\git\bin;C:\pwsh;C:\node;$env:Path"
|
||||
git config --global --add safe.directory C:/OEM/src # setup copied it as SYSTEM, the tests run as the logged-on user
|
||||
|
||||
$failed = 0
|
||||
Write-Output "==> Running $((Get-Content "$root\tests.txt").Count) test binaries on $([Environment]::ProcessorCount) cores"
|
||||
Get-Content "$root\tests.txt" | ForEach-Object -ThrottleLimit ([Environment]::ProcessorCount) -Parallel {
|
||||
$binary, $package = $_ -split ' ', 2
|
||||
$started = Get-Date
|
||||
Set-Location "$using:root\src\$package"
|
||||
cmd.exe /d /c "$using:root\bin\$binary -test.v -test.timeout=60m -test.parallel=8 > $using:root\logs\$($package -replace '/', '-').log 2>&1"
|
||||
[pscustomobject]@{ Package = $package; ExitCode = $LASTEXITCODE; Seconds = [int]((Get-Date) - $started).TotalSeconds }
|
||||
} | ForEach-Object {
|
||||
$line = "ok $($_.Package) $($_.Seconds)s"
|
||||
if ($_.ExitCode -ne 0) {
|
||||
$line = "FAIL $($_.Package) $($_.Seconds)s exit $($_.ExitCode)"
|
||||
$failed = 1
|
||||
}
|
||||
Add-Content "$results\summary.txt" $line
|
||||
}
|
||||
Copy-Item "$root\logs\*" $results
|
||||
} catch {
|
||||
$_ | Out-String | Tee-Object "$results\error.txt"
|
||||
$failed = 1
|
||||
}
|
||||
Set-Content "$results\exitcode" $failed
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
image="${WINDOWS_TEST_IMAGE:?}"
|
||||
timeout="${WINDOWS_TEST_TIMEOUT:-165}"
|
||||
name="gitea-runner-windows-test-$$"
|
||||
work="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
docker rm -fv "$name" >/dev/null 2>&1 || true
|
||||
rm -rf "$work"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [ "$(docker info -f '{{.Architecture}}')" != x86_64 ]; then
|
||||
echo "error: the Windows test VM needs an amd64 docker host" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Building Windows test binaries"
|
||||
oem="$work/oem"
|
||||
mkdir -p "$oem/bin" "$oem/src" "$work/shared"
|
||||
module="$(go list -m)"
|
||||
packages="$(GOOS=windows go list -f '{{if or .TestGoFiles .XTestGoFiles}}{{.ImportPath}}{{end}}' ./...)"
|
||||
GOOS=windows GOARCH=amd64 go test -c -ldflags='-s -w' -o "$oem/bin/" $packages
|
||||
for package in $packages; do
|
||||
echo "${package##*/}.test.exe ${package#"$module"/}"
|
||||
done > "$oem/tests.txt"
|
||||
git ls-files -z --cached --others --exclude-standard | while IFS= read -r -d '' file; do
|
||||
if [ -e "$file" ]; then printf '%s\0' "$file"; fi
|
||||
done | tar --null -T - -cf - | tar -xf - -C "$oem/src"
|
||||
git -C "$oem/src" init -q -b main # a repository like the CI checkout, for the tests that read the github context from it
|
||||
git -C "$oem/src" add -Af .
|
||||
git -C "$oem/src" -c user.name=test -c user.email=test@localhost -c commit.gpgsign=false -c core.hooksPath=/dev/null commit -qm test
|
||||
git -C "$oem/src" remote add origin https://gitea.com/gitea/runner
|
||||
cp scripts/test-windows-setup.ps1 scripts/test-windows.ps1 "$oem/"
|
||||
sed 's/$/\r/' > "$oem/install.bat" <<'EOF' # a policy turns off real-time scanning, AMSI blocks scripts that do it
|
||||
@echo off
|
||||
set log=\\host.lan\Data\install.log
|
||||
echo ==^> install.bat started at %TIME% >> %log%
|
||||
reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" /v DisableRealtimeMonitoring /t REG_DWORD /d 1 /f > nul
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\OEM\test-windows-setup.ps1 >> %log% 2>&1
|
||||
echo ==^> setup exited with %ERRORLEVEL% >> %log%
|
||||
if %ERRORLEVEL% equ 0 C:\pwsh\pwsh.exe -NoProfile -ExecutionPolicy Bypass -File C:\OEM\test-windows.ps1 >> %log% 2>&1
|
||||
if not exist \\host.lan\Data\exitcode (echo 1)> \\host.lan\Data\exitcode
|
||||
shutdown.exe /s /t 600
|
||||
EOF
|
||||
|
||||
kvm=Y cores="$(nproc)"
|
||||
if ! docker run --rm --privileged --entrypoint sh "$image" -c 'test -w /dev/kvm'; then
|
||||
kvm=N cores=4 # more emulated vCPUs slow Windows down
|
||||
echo "==> The docker host has no usable /dev/kvm, Windows runs emulated and much slower"
|
||||
fi
|
||||
|
||||
echo "==> Installing Windows"
|
||||
docker create --rm --name "$name" --privileged -e VERSION=2025 -e EDITION=core -e KVM="$kvm" -e CPU_CORES="$cores" -e RAM_SIZE=8G \
|
||||
-e DISK_SIZE=32G -e DISK_CACHE=unsafe -e DISK_IO=threads --entrypoint timeout "$image" "$((timeout + 10))m" /usr/bin/tini -s /run/entry.sh >/dev/null # the timeout stops the VM even when this script is killed before its cleanup runs
|
||||
docker cp "$oem" "$name:/oem"
|
||||
docker cp "$work/shared" "$name:/shared"
|
||||
docker start "$name" >/dev/null
|
||||
docker logs -f "$name" &
|
||||
while sleep 10 && docker container inspect "$name" >/dev/null 2>&1; do
|
||||
if ((SECONDS % 300 < 10)); then
|
||||
echo "==> $((SECONDS / 60))m elapsed, VM disk $(docker exec "$name" du -sh /storage | cut -f1)"
|
||||
fi
|
||||
done &
|
||||
docker exec "$name" sh -c 'cd /shared && touch install.log summary.txt && tail -qf install.log summary.txt' &
|
||||
|
||||
if ! timeout "${timeout}m" docker exec "$name" sh -c 'until [ -f /shared/exitcode ]; do sleep 10; done'; then
|
||||
echo "Windows stopped or did not finish the tests within $timeout minutes" >&2
|
||||
exit 1
|
||||
fi
|
||||
results="$work/results"
|
||||
docker cp "$name:/shared" "$results"
|
||||
|
||||
tr -d '\r' < "$results/summary.txt" | while read -r result package _; do
|
||||
if [ "$result" = FAIL ]; then
|
||||
echo "::group::$package"
|
||||
cat "$results/${package//\//-}.log"
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
done
|
||||
if [ -f "$results/error.txt" ]; then
|
||||
cat "$results/error.txt" >&2
|
||||
fi
|
||||
echo "==> Slowest tests"
|
||||
find "$results" -name '*.log' -exec cat {} + | tr -d '\r' | sed -nE 's/^--- (PASS|FAIL): (\S+) \(([0-9.]+)s\)$/\3 \2/p' | sort -rn | awk 'NR <= 20'
|
||||
echo "==> Packages"
|
||||
tr -d '\r' < "$results/summary.txt"
|
||||
exit "$(tr -d '\r\n' < "$results/exitcode")"
|
||||
Reference in new issue
Block a user