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:
silverwind committed 2026-11-03 01:26:10 +00:00
1 parent 6e42621b4a
commit d6f5b68a0d
85 files changed
+2578 -620

No files matched your search

+2 -2
View File
@@ -327,8 +327,8 @@ health_check:
# Minimum free space required on the filesystem holding runner workspaces.
# Defaults to 1024 MiB when omitted or set to zero.
#min_free_disk_space_mb: 1024
# Optional additional executable. A non-zero exit, timeout, or startup failure
# marks the runner unavailable.
# Optional additional executable, or a .bat or .cmd file on Windows. A non-zero
# exit, timeout, or startup failure marks the runner unavailable.
#script: ''
# How long a script result is cached and its maximum execution time.
#interval: 30s
+6 -6
View File
@@ -109,7 +109,7 @@ cache:
endpoint: "http://minio:9000"
bucket: "caches"
prefix: "/runners/"
access_key_file: "` + accessPath + `"
access_key_file: '` + accessPath + `'
secret_key: "secret"
`
require.NoError(t, os.WriteFile(path, []byte(content), 0o600))
@@ -341,20 +341,20 @@ func TestLoadDefault_ExternalCacheSecret(t *testing.T) {
{name: "without secret", yaml: externalCache, wantErr: "external_secret"},
{name: "inline secret", yaml: externalCache + " external_secret: \"shh\"\n"},
{
name: "from file", yaml: externalCache + " external_secret_file: \"$DIR/cache.secret\"\n",
name: "from file", yaml: externalCache + " external_secret_file: '$DIR/cache.secret'\n",
fileContent: " s3cr3t\n", wantSecret: "s3cr3t",
},
{
name: "from file when disabled", yaml: "\ncache:\n enabled: false\n external_secret_file: \"$DIR/cache.secret\"\n",
name: "from file when disabled", yaml: "\ncache:\n enabled: false\n external_secret_file: '$DIR/cache.secret'\n",
fileContent: "s3cr3t", wantSecret: "s3cr3t",
},
{
name: "inline and file conflict", yaml: externalCache + " external_secret: \"inline\"\n external_secret_file: \"$DIR/cache.secret\"\n",
name: "inline and file conflict", yaml: externalCache + " external_secret: \"inline\"\n external_secret_file: '$DIR/cache.secret'\n",
fileContent: "s3cr3t", wantErr: "both set",
},
{name: "missing file", yaml: externalCache + " external_secret_file: \"$DIR/absent.secret\"\n", wantErr: "read cache.external_secret_file"},
{name: "missing file", yaml: externalCache + " external_secret_file: '$DIR/absent.secret'\n", wantErr: "read cache.external_secret_file"},
{
name: "empty file", yaml: externalCache + " external_secret_file: \"$DIR/cache.secret\"\n",
name: "empty file", yaml: externalCache + " external_secret_file: '$DIR/cache.secret'\n",
fileContent: "\n \n", wantErr: "contains no secret",
},
} {