mirror of
https://gitea.com/gitea/runner.git
synced 2026-11-04 06:25:32 +00:00
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]>
139 lines
4.5 KiB
Go
139 lines
4.5 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package container
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
"unsafe"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
func TestHostEnvironmentTrackingCleanup(t *testing.T) {
|
|
for _, tracking := range []string{"default", "", "shared-helper"} {
|
|
t.Run("tracking="+tracking, func(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
|
|
defer cancel()
|
|
listener, err := net.Listen("tcp4", "127.0.0.1:0")
|
|
require.NoError(t, err)
|
|
defer listener.Close()
|
|
stop := context.AfterFunc(ctx, func() { _ = listener.Close() })
|
|
defer stop()
|
|
workspace := t.TempDir()
|
|
environment := &HostEnvironment{Path: t.TempDir(), TmpDir: workspace, Workdir: workspace, TrackingID: "gitea_test", StdOut: io.Discard}
|
|
env := map[string]string{}
|
|
for _, entry := range os.Environ() {
|
|
if name, value, ok := strings.Cut(entry, "="); ok && !strings.EqualFold(name, "RUNNER_TRACKING_ID") {
|
|
env[name] = value
|
|
}
|
|
}
|
|
env["GITEA_TRACKING_HELPER"] = "step"
|
|
env["GITEA_TRACKING_WORKSPACE"] = workspace
|
|
env["GITEA_TRACKING_ADDRESS"] = listener.Addr().String()
|
|
if tracking == "default" {
|
|
env["RUNNER_TRACKING_ID"] = environment.TrackingID
|
|
env["runner_tracking_id"] = "shared-helper"
|
|
} else {
|
|
env["runner_tracking_id"] = tracking
|
|
}
|
|
result := make(chan error, 1)
|
|
go func() {
|
|
result <- environment.Exec([]string{os.Args[0], "-test.run=^TestHostTrackingHelper$"}, env, "", workspace)(ctx)
|
|
}()
|
|
step, err := listener.Accept()
|
|
require.NoError(t, err)
|
|
defer step.Close()
|
|
require.NoError(t, step.SetDeadline(time.Now().Add(20*time.Second)))
|
|
var stepPID int
|
|
_, err = fmt.Fscanln(step, &stepPID)
|
|
require.NoError(t, err)
|
|
stepHandle, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(stepPID))
|
|
require.NoError(t, err)
|
|
defer func() { _ = windows.CloseHandle(stepHandle) }()
|
|
require.Eventually(t, func() bool {
|
|
var assigned int32
|
|
result, _, _ := windows.NewLazySystemDLL("kernel32.dll").NewProc("IsProcessInJob").Call(uintptr(stepHandle), 0, uintptr(unsafe.Pointer(&assigned)))
|
|
return result != 0 && assigned != 0
|
|
}, 5*time.Second, 10*time.Millisecond)
|
|
_, err = step.Write([]byte{1})
|
|
require.NoError(t, err)
|
|
var helpers []windows.Handle
|
|
for range 2 {
|
|
connection, err := listener.Accept()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { _ = connection.Close() })
|
|
require.NoError(t, connection.SetReadDeadline(time.Now().Add(20*time.Second)))
|
|
var pid int
|
|
_, err = fmt.Fscanln(connection, &pid)
|
|
require.NoError(t, err)
|
|
handle, err := windows.OpenProcess(windows.SYNCHRONIZE|windows.PROCESS_TERMINATE, false, uint32(pid))
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
_ = windows.TerminateProcess(handle, 1)
|
|
_, _ = windows.WaitForSingleObject(handle, 5000)
|
|
_ = windows.CloseHandle(handle)
|
|
})
|
|
helpers = append(helpers, handle)
|
|
}
|
|
select {
|
|
case err := <-result:
|
|
require.NoError(t, err)
|
|
case <-ctx.Done():
|
|
t.Fatal(ctx.Err())
|
|
}
|
|
require.NoError(t, environment.Remove()(ctx))
|
|
for _, handle := range helpers {
|
|
if tracking == "default" {
|
|
result, err := windows.WaitForSingleObject(handle, 5000)
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, windows.WAIT_OBJECT_0, result)
|
|
} else {
|
|
result, err := windows.WaitForSingleObject(handle, 0)
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, windows.WAIT_TIMEOUT, result)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHostTrackingHelper(t *testing.T) {
|
|
mode := os.Getenv("GITEA_TRACKING_HELPER")
|
|
if mode == "" {
|
|
return
|
|
}
|
|
connection, err := net.Dial("tcp4", os.Getenv("GITEA_TRACKING_ADDRESS"))
|
|
require.NoError(t, err)
|
|
_, err = fmt.Fprintln(connection, os.Getpid())
|
|
require.NoError(t, err)
|
|
if mode == "step" {
|
|
_, err := bufio.NewReader(connection).ReadByte()
|
|
require.NoError(t, err)
|
|
}
|
|
if mode != "descendant" {
|
|
child := exec.Command(os.Args[0], "-test.run=^TestHostTrackingHelper$", filepath.Join(os.Getenv("GITEA_TRACKING_WORKSPACE"), "tracking-reference"))
|
|
child.Env = append(os.Environ(), "GITEA_TRACKING_HELPER="+map[string]string{"step": "helper", "helper": "descendant"}[mode])
|
|
require.NoError(t, child.Start())
|
|
if mode == "step" {
|
|
require.NoError(t, connection.Close())
|
|
os.Exit(0)
|
|
}
|
|
}
|
|
_, err = io.Copy(io.Discard, connection)
|
|
require.NoError(t, err)
|
|
require.NoError(t, connection.Close())
|
|
os.Exit(0)
|
|
}
|