From b3c603644a29f06d5f2ad5a666da3feea512af61 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Mon, 15 Sep 2025 15:26:16 +0200 Subject: [PATCH 1/3] chore: set log level to trace for runner tests --- internal/app/run/runner_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/app/run/runner_test.go b/internal/app/run/runner_test.go index b324b0f5..8e3b9707 100644 --- a/internal/app/run/runner_test.go +++ b/internal/app/run/runner_test.go @@ -20,6 +20,10 @@ import ( "github.com/stretchr/testify/require" ) +func init() { + log.SetLevel(log.TraceLevel) +} + func TestExplainFailedGenerateWorkflow(t *testing.T) { logged := "" log := func(message string, args ...any) { From 6877142ba4e3feeccc9e6bfb51900d13202e4de5 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Mon, 15 Sep 2025 15:20:53 +0200 Subject: [PATCH 2/3] chore: minimal integration test for the LXC backend --- .forgejo/workflows/test.yml | 5 +++ internal/app/run/runner_test.go | 65 +++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 5a1dd4d5..28ab84b9 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -185,6 +185,11 @@ jobs: EOF apt --quiet install --yes -qq docker.io make + - name: install LXC + run: | + act/runner/lxc-helpers.sh lxc_prepare_environment + act/runner/lxc-helpers.sh lxc_install_lxc_inside 10.39.28 fdb1 + - run: apt-get -q install -qq -y gcc # required for `-race` - run: make integration-test diff --git a/internal/app/run/runner_test.go b/internal/app/run/runner_test.go index 8e3b9707..634fae96 100644 --- a/internal/app/run/runner_test.go +++ b/internal/app/run/runner_test.go @@ -13,6 +13,7 @@ import ( "code.forgejo.org/forgejo/runner/v11/internal/pkg/labels" "code.forgejo.org/forgejo/runner/v11/internal/pkg/report" "connectrpc.com/connect" + log "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/structpb" "github.com/stretchr/testify/assert" @@ -341,3 +342,67 @@ jobs: runWorkflow(ctx, cancel, checkKey2Yaml, "push", "refs/heads/main", "step 5: push cache should not be polluted by PR") }) } + +func TestRunnerLXC(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + forgejoClient := &forgejoClientMock{} + + forgejoClient.On("Address").Return("https://127.0.0.1:8080") // not expected to be used in this test + forgejoClient.On("UpdateLog", mock.Anything, mock.Anything).Return(nil, nil) + forgejoClient.On("UpdateTask", mock.Anything, mock.Anything). + Return(connect.NewResponse(&runnerv1.UpdateTaskResponse{}), nil) + + runner := NewRunner( + &config.Config{ + Log: config.Log{ + JobLevel: "trace", + }, + Host: config.Host{ + WorkdirParent: t.TempDir(), + }, + }, + &config.Registration{ + Labels: []string{"lxc:lxc://debian:bookworm"}, + }, + forgejoClient) + require.NotNil(t, runner) + + runWorkflow := func(ctx context.Context, cancel context.CancelFunc, yamlContent, eventName, ref, description string) { + task := &runnerv1.Task{ + WorkflowPayload: []byte(yamlContent), + Context: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "token": structpb.NewStringValue("some token here"), + "forgejo_default_actions_url": structpb.NewStringValue("https://data.forgejo.org"), + "repository": structpb.NewStringValue("runner"), + "event_name": structpb.NewStringValue(eventName), + "ref": structpb.NewStringValue(ref), + }, + }, + } + + reporter := report.NewReporter(ctx, cancel, forgejoClient, task, time.Second) + err := runner.run(ctx, task, reporter) + reporter.Close(nil) + require.NoError(t, err, description) + } + + t.Run("OK", func(t *testing.T) { + ctx, cancel := context.WithCancel(t.Context()) + defer cancel() + + workflow := ` +on: + push: +jobs: + job: + runs-on: lxc + steps: + - run: echo OK +` + runWorkflow(ctx, cancel, workflow, "push", "refs/heads/main", "OK") + }) +} From 69df253e4180ca065ea99bf30555e6939cd85d31 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Mon, 15 Sep 2025 17:17:44 +0200 Subject: [PATCH 3/3] fix: ptyWriter.AutoStop is used by multiple goroutines ``` WARNING: DATA RACE Write at 0x00c0008541d8 by goroutine 9324: code.forgejo.org/forgejo/runner/v11/act/container.(*HostEnvironment).exec() /home/debian/.cache/act/37b13738279f9342/hostexecutor/act/container/host_environment.go:368 +0x12dd code.forgejo.org/forgejo/runner/v11/act/runner.(*stepRun).main.func1.(*HostEnvironment).ExecWithCmdLine.1() /home/debian/.cache/act/37b13738279f9342/hostexecutor/act/container/host_environment.go:388 +0x354 Previous read at 0x00c0008541d8 by goroutine 9328: code.forgejo.org/forgejo/runner/v11/act/container.(*ptyWriter).Write() /home/debian/.cache/act/37b13738279f9342/hostexecutor/act/container/host_environment.go:199 +0x57 io.copyBuffer() /home/debian/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.7.linux-amd64/src/io/io.go:431 +0x2ce Goroutine 9324 (running) created at: code.forgejo.org/forgejo/runner/v11/act/runner.(*runnerImpl).NewPlanExecutor.func1.NewParallelExecutor.2() /home/debian/.cache/act/37b13738279f9342/hostexecutor/act/common/executor.go:105 +0x144 code.forgejo.org/forgejo/runner/v11/act/runner.(*runnerImpl).NewPlanExecutor.func1.NewParallelExecutor.3.1() /home/debian/.cache/act/37b13738279f9342/hostexecutor/act/common/executor.go:107 +0x61 code.forgejo.org/forgejo/runner/v11/act/runner.(*runnerImpl).NewPlanExecutor.func1.NewParallelExecutor.3.gowrap1() /home/debian/.cache/act/37b13738279f9342/hostexecutor/act/common/executor.go:109 +0x4f Goroutine 9328 (running) created at: code.forgejo.org/forgejo/runner/v11/act/container.(*HostEnvironment).exec() /home/debian/.cache/act/37b13738279f9342/hostexecutor/act/container/host_environment.go:356 +0x112a code.forgejo.org/forgejo/runner/v11/act/runner.(*stepRun).main.func1.(*HostEnvironment).ExecWithCmdLine.1() /home/debian/.cache/act/37b13738279f9342/hostexecutor/act/container/host_environment.go:388 +0x354 code.forgejo.org/forgejo/runner/v11/act/runner.(*stepRun).main.func1() ``` --- act/container/host_environment.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/act/container/host_environment.go b/act/container/host_environment.go index 0e12fa3a..2a8ee318 100644 --- a/act/container/host_environment.go +++ b/act/container/host_environment.go @@ -13,6 +13,7 @@ import ( "path/filepath" "runtime" "strings" + "sync/atomic" "time" "github.com/go-git/go-billy/v5/helper/polyfill" @@ -191,12 +192,12 @@ func (e *HostEnvironment) Start(_ bool) common.Executor { type ptyWriter struct { Out io.Writer - AutoStop bool + AutoStop atomic.Bool dirtyLine bool } func (w *ptyWriter) Write(buf []byte) (int, error) { - if w.AutoStop && len(buf) > 0 && buf[len(buf)-1] == 4 { + if w.AutoStop.Load() && len(buf) > 0 && buf[len(buf)-1] == 4 { n, err := w.Out.Write(buf[:len(buf)-1]) if err != nil { return n, err @@ -365,7 +366,7 @@ func (e *HostEnvironment) exec(ctx context.Context, commandparam []string, cmdli return fmt.Errorf("RUN %w", err) } if tty != nil { - writer.AutoStop = true + writer.AutoStop.Store(true) if _, err := tty.Write([]byte("\x04")); err != nil { common.Logger(ctx).Debug("Failed to write EOT") }