From 01766ff4e2f0b9e2fd3b012bc91b4dfa1d4abb05 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 1 Oct 2025 16:38:38 +0000 Subject: [PATCH 01/18] fix: modifying a cache secret does not invalidate cached entries (#1043) Resolves forgejo/runner#1041 - bug fixes - [PR](https://code.forgejo.org/forgejo/runner/pulls/1043): fix: modifying a cache secret does not invalidate cached entries Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1043 Reviewed-by: Mathieu Fenniak Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- internal/pkg/config/config.example.yaml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/internal/pkg/config/config.example.yaml b/internal/pkg/config/config.example.yaml index 4e447e9d..c32526e1 100644 --- a/internal/pkg/config/config.example.yaml +++ b/internal/pkg/config/config.example.yaml @@ -110,25 +110,20 @@ cache: # external_server: "" # - ####################################################################### - # - # Common to the internal and external cache server - # - ####################################################################### - # # The shared cache secret used to secure the communications between # the cache proxy and the cache server. # # If empty, it will be generated to a new secret automatically when # the server starts and it will stay the same until it restarts. # - # Every time the secret is modified, all cache entries that were - # created with it are invalidated. In order to ensure that the cache - # content is reused when the runner restarts, this secret must be - # set, for instance with the output of openssl rand -hex 40. - # secret: "" # + ####################################################################### + # + # Common to the internal and external cache server + # + ####################################################################### + # # The IP or hostname (195.84.20.30 or example.com) to use when constructing # ACTIONS_CACHE_URL which is the URL of the cache proxy. # From 63351343ba07fa11763a03768e615ab6e1c9b39f Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Thu, 2 Oct 2025 22:43:50 +0000 Subject: [PATCH 02/18] fix: improve logging to diagnose mystery job terminations (#1048) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additional logging to support #1044. Manual testing only. Cases tested: Cancel a job from Forgejo UI; this seems like the most likely missing piece in #1044 as two jobs were simultaneously marked as "Failed". There are codepaths in Forgejo that can set this state to both cancelled and failed, but the runner didn't provide log output indicating that's why a job was stopping: ``` time="2025-10-02T13:22:53-06:00" level=info msg="UpdateTask returned task result RESULT_CANCELLED for a task that was in local state RESULT_UNSPECIFIED - beginning local task termination" func="[ReportState]" file="[reporter.go:410]" ``` Host-based executor hits step timeout in exec, or, is cancelled. This occurred but only logged the `err` from `exec`, not the context error indicating whether it was a timeout or a cancellation: ``` [Test Action/job1] this step has been cancelled: ctx: context deadline exceeded, exec: RUN signal: killed [Test Action/job1] this step has been cancelled: ctx: context canceled, exec: RUN signal: killed ``` Unable to `ReportState` due to Forgejo inaccessible. If the runner isn't able to update state to Forgejo a job could be considered a zombie; this would trigger one of the codepaths where the job would be marked as failed. If connectivity was later restored, then the runner could identify it was marked as failed and cancel the job context. (This combination doesn't seem likely, but, I think it's reasonable to consider these failures as warnings because there may be unexpected errors here that we're not aware of). ``` time="2025-10-02T13:27:19-06:00" level=warning msg="ReportState error: unavailable: 502 Bad Gateway" func="[RunDaemon]" file="[reporter.go:207]" ``` Runner shutdown logging; just changed up to `Info` level: ``` time="2025-10-02T13:31:36-06:00" level=info msg="forcing the jobs to shutdown" func="[Shutdown]" file="[poller.go:93]" [Test Action/job1] ❌ Failure - Main sleep 120 [Test Action/job1] this step has been cancelled: ctx: context canceled, exec: RUN signal: killed ``` - bug fixes - [PR](https://code.forgejo.org/forgejo/runner/pulls/1048): fix: improve logging to diagnose mystery job terminations Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1048 Reviewed-by: earl-warren Co-authored-by: Mathieu Fenniak Co-committed-by: Mathieu Fenniak --- act/container/host_environment.go | 2 +- act/runner/run_context.go | 2 +- act/runner/step.go | 6 +++--- internal/app/poll/poller.go | 4 ++-- internal/pkg/report/reporter.go | 24 +++++++++++++++++++++--- 5 files changed, 28 insertions(+), 10 deletions(-) diff --git a/act/container/host_environment.go b/act/container/host_environment.go index 405862a9..c8742008 100644 --- a/act/container/host_environment.go +++ b/act/container/host_environment.go @@ -389,7 +389,7 @@ func (e *HostEnvironment) ExecWithCmdLine(command []string, cmdline string, env if err := e.exec(ctx, command, cmdline, env, user, workdir); err != nil { select { case <-ctx.Done(): - return fmt.Errorf("this step has been cancelled: %w", err) + return fmt.Errorf("this step has been cancelled: ctx: %w, exec: %w", ctx.Err(), err) default: return err } diff --git a/act/runner/run_context.go b/act/runner/run_context.go index f344a046..bb79ad3a 100644 --- a/act/runner/run_context.go +++ b/act/runner/run_context.go @@ -949,7 +949,7 @@ func (rc *RunContext) Executor() (common.Executor, error) { return err } if res { - timeoutctx, cancelTimeOut := evaluateTimeout(ctx, rc.ExprEval, rc.Run.Job().TimeoutMinutes) + timeoutctx, cancelTimeOut := evaluateTimeout(ctx, "job", rc.ExprEval, rc.Run.Job().TimeoutMinutes) defer cancelTimeOut() return executor(timeoutctx) diff --git a/act/runner/step.go b/act/runner/step.go index 322b6f7e..c93291c4 100644 --- a/act/runner/step.go +++ b/act/runner/step.go @@ -177,7 +177,7 @@ func runStepExecutor(step step, stage stepStage, executor common.Executor) commo Mode: 0o666, })(ctx) - timeoutctx, cancelTimeOut := evaluateTimeout(ctx, rc.ExprEval, stepModel.TimeoutMinutes) + timeoutctx, cancelTimeOut := evaluateTimeout(ctx, "step", rc.ExprEval, stepModel.TimeoutMinutes) defer cancelTimeOut() err = executor(timeoutctx) @@ -213,12 +213,12 @@ func runStepExecutor(step step, stage stepStage, executor common.Executor) commo } } -func evaluateTimeout(ctx context.Context, exprEval ExpressionEvaluator, timeoutMinutes string) (context.Context, context.CancelFunc) { +func evaluateTimeout(ctx context.Context, contextType string, exprEval ExpressionEvaluator, timeoutMinutes string) (context.Context, context.CancelFunc) { timeout := exprEval.Interpolate(ctx, timeoutMinutes) if timeout != "" { timeOutMinutes, err := strconv.ParseInt(timeout, 10, 64) if err == nil { - common.Logger(ctx).Debugf("the step will stop in timeout-minutes %s", timeout) + common.Logger(ctx).Debugf("the %s will stop in timeout-minutes %s", contextType, timeout) return context.WithTimeout(ctx, time.Duration(timeOutMinutes)*time.Minute) } common.Logger(ctx).Errorf("timeout-minutes %s cannot be parsed and will be ignored: %w", timeout, err) diff --git a/internal/app/poll/poller.go b/internal/app/poll/poller.go index 7b954cb7..c880bc67 100644 --- a/internal/app/poll/poller.go +++ b/internal/app/poll/poller.go @@ -90,10 +90,10 @@ func (p *poller) Shutdown(ctx context.Context) error { return nil case <-ctx.Done(): - log.Trace("forcing the jobs to shutdown") + log.Info("forcing the jobs to shutdown") p.shutdownJobs() <-p.done - log.Trace("all jobs have been shutdown") + log.Info("all jobs have been shutdown") return ctx.Err() } } diff --git a/internal/pkg/report/reporter.go b/internal/pkg/report/reporter.go index 7e46b8bb..804744cc 100644 --- a/internal/pkg/report/reporter.go +++ b/internal/pkg/report/reporter.go @@ -48,6 +48,7 @@ type Reporter struct { debugOutputEnabled bool stopCommandEndToken string + issuedLocalCancel bool } func NewReporter(ctx context.Context, cancel context.CancelFunc, c client.Client, task *runnerv1.Task, reportInterval time.Duration) *Reporter { @@ -192,11 +193,19 @@ func (r *Reporter) RunDaemon() { return } if r.ctx.Err() != nil { + // This shouldn't happen because DaemonContext is used for `r.ctx` which should outlive any running job. + log.Warnf("Terminating RunDaemon on an active job due to error: %v", r.ctx.Err()) return } - _ = r.ReportLog(false) - _ = r.ReportState() + err := r.ReportLog(false) + if err != nil { + log.Warnf("ReportLog error: %v", err) + } + err = r.ReportState() + if err != nil { + log.Warnf("ReportState error: %v", err) + } time.AfterFunc(r.reportInterval, r.RunDaemon) } @@ -391,8 +400,17 @@ func (r *Reporter) ReportState() error { r.outputs.Store(k, struct{}{}) } - switch resp.Msg.GetState().GetResult() { + localResultState := state.GetResult() + remoteResultState := resp.Msg.GetState().GetResult() + switch remoteResultState { case runnerv1.Result_RESULT_CANCELLED, runnerv1.Result_RESULT_FAILURE: + // issuedLocalCancel is just used to deduplicate this log message if our local state doesn't catch up with our + // remote state as quickly as the report-interval, which would cause this message to repeat in the logs. + if !r.issuedLocalCancel && remoteResultState != localResultState { + log.Infof("UpdateTask returned task result %v for a task that was in local state %v - beginning local task termination", + remoteResultState, localResultState) + r.issuedLocalCancel = true + } r.cancel() } From b772be7131102d2c19a745811a569cd4ff1cbced Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 3 Oct 2025 08:22:06 +0000 Subject: [PATCH 03/18] fix(security): a multiline secret may be found in a single log entry (#1051) With secrets.MULTILINE set to ``` ABC DEF GHI ``` the following is logged in debug mode: ``` 2025-09-18T10:54:04.4656189Z expression '${{ secrets.MULTILINE }}' rewritten to 'format('{0}', secrets.MULTILINE)' 2025-09-18T10:54:04.4656426Z evaluating expression 'format('{0}', secrets.MULTILINE)' 2025-09-18T10:54:04.4656797Z expression 'format('{0}', secrets.MULTILINE)' evaluated to '%!t(string=ABC\nDEF\nGHI)' ``` Although it is displayed with \ followed by n, it is a single line entry displayed with the secret verbatim and must also be redacted. - bug fixes - [PR](https://code.forgejo.org/forgejo/runner/pulls/1051): fix(security): a multiline secret may be found in a single log entry Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1051 Reviewed-by: Michael Kriese Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- internal/pkg/report/mask.go | 2 +- internal/pkg/report/mask_test.go | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/internal/pkg/report/mask.go b/internal/pkg/report/mask.go index 2ae19b8f..3dbb6f42 100644 --- a/internal/pkg/report/mask.go +++ b/internal/pkg/report/mask.go @@ -38,7 +38,7 @@ func (o *masker) add(secret string) { }) // a multiline secret transformed into a single line by replacing // newlines with \ followed by n must also be redacted - secret = strings.Join(lines, "\\n") + o.lines = append(o.lines, strings.Join(lines, "\\n")) } o.lines = append(o.lines, secret) diff --git a/internal/pkg/report/mask_test.go b/internal/pkg/report/mask_test.go index 8f26e698..b785c4dc 100644 --- a/internal/pkg/report/mask_test.go +++ b/internal/pkg/report/mask_test.go @@ -7,6 +7,8 @@ import ( "fmt" "testing" + runnerv1 "code.forgejo.org/forgejo/actions-proto/runner/v1" + "github.com/stretchr/testify/assert" ) @@ -267,4 +269,17 @@ SIX` assert.Equal(t, testCase.out, rowsToString(rows)) }) } + + t.Run("MultilineSecretInSingleRow", func(t *testing.T) { + secret := "ABC\nDEF\nGHI" + m := newMasker() + m.add(secret) + rows := []*runnerv1.LogRow{ + {Content: fmt.Sprintf("BEFORE%sAFTER", secret)}, + } + noMore := false + needMore := m.replace(rows, noMore) + assert.False(t, needMore) + assert.Equal(t, "BEFORE***AFTER\n", rowsToString(rows)) + }) } From 1fa156c7e40c908a742742961e0f1c165ab42699 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 3 Oct 2025 09:03:32 +0000 Subject: [PATCH 04/18] Update dependency forgejo/lxc-helpers to v1.1.1 (#1050) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [forgejo/lxc-helpers](https://code.forgejo.org/forgejo/lxc-helpers) | patch | `1.1.0` -> `1.1.1` | --- ### Release Notes
forgejo/lxc-helpers (forgejo/lxc-helpers) ### [`v1.1.1`](https://code.forgejo.org/forgejo/lxc-helpers/compare/v1.1.0...v1.1.1) [Compare Source](https://code.forgejo.org/forgejo/lxc-helpers/compare/v1.1.0...v1.1.1)
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1050 Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- examples/lxc-systemd/forgejo-runner-service.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/lxc-systemd/forgejo-runner-service.sh b/examples/lxc-systemd/forgejo-runner-service.sh index c0334bea..0c1e2204 100755 --- a/examples/lxc-systemd/forgejo-runner-service.sh +++ b/examples/lxc-systemd/forgejo-runner-service.sh @@ -20,7 +20,7 @@ trap "rm -fr $TMPDIR" EXIT : ${INPUTS_TOKEN:=} : ${INPUTS_FORGEJO:=https://code.forgejo.org} : ${INPUTS_LIFETIME:=7d} -DEFAULT_LXC_HELPERS_VERSION=1.1.0 # renovate: datasource=forgejo-tags depName=forgejo/lxc-helpers +DEFAULT_LXC_HELPERS_VERSION=1.1.1 # renovate: datasource=forgejo-tags depName=forgejo/lxc-helpers : ${INPUTS_LXC_HELPERS_VERSION:=$DEFAULT_LXC_HELPERS_VERSION} DEFAULT_RUNNER_VERSION=11.1.1 # renovate: datasource=forgejo-releases depName=forgejo/runner : ${INPUTS_RUNNER_VERSION:=$DEFAULT_RUNNER_VERSION} From a980acd9369da246bfc8f72a6aa8992c3ab71feb Mon Sep 17 00:00:00 2001 From: cascading-pr Date: Fri, 3 Oct 2025 09:09:29 +0000 Subject: [PATCH 05/18] fix: make the creation of LXC templates atomic so they cannot be interrupted while building (#1049) cascading-pr from https://code.forgejo.org/forgejo/lxc-helpers/pulls/49 - other - [PR](https://code.forgejo.org/forgejo/runner/pulls/1049): cascading-pr from https://code.forgejo.org/forgejo/lxc-helpers refs/pull/49/head to forgejo/lxc-helpers-49 Co-authored-by: cascading-pr Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1049 Reviewed-by: earl-warren Co-authored-by: cascading-pr Co-committed-by: cascading-pr --- act/runner/lxc-helpers-lib.sh | 78 ++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 15 deletions(-) diff --git a/act/runner/lxc-helpers-lib.sh b/act/runner/lxc-helpers-lib.sh index 78d70113..cd855a89 100755 --- a/act/runner/lxc-helpers-lib.sh +++ b/act/runner/lxc-helpers-lib.sh @@ -11,6 +11,8 @@ LXC_IPV6_PREFIX_DEFAULT="fd15" LXC_DOCKER_PREFIX_DEFAULT="172.17" LXC_IPV6_DOCKER_PREFIX_DEFAULT="fd00:d0ca" LXC_APT_TOO_OLD='1 week ago' +: ${LXC_TRANSACTION_TIMEOUT:=600} +LXC_TRANSACTION_LOCK_FILE=/tmp/lxc-helper.lock : ${LXC_SUDO:=} : ${LXC_CONTAINER_RELEASE:=bookworm} @@ -28,16 +30,22 @@ function lxc_template_release() { echo lxc-helpers-$LXC_CONTAINER_RELEASE } +function lxc_directory() { + local name="$1" + + echo /var/lib/lxc/$name +} + function lxc_root() { local name="$1" - echo /var/lib/lxc/$name/rootfs + echo $(lxc_directory $name)/rootfs } function lxc_config() { local name="$1" - echo /var/lib/lxc/$name/config + echo $(lxc_directory $name)/config } function lxc_container_run() { @@ -47,6 +55,44 @@ function lxc_container_run() { $LXC_SUDO lxc-attach --clear-env --name $name -- "$@" } +function lxc_transaction_lock() { + exec 7>$LXC_TRANSACTION_LOCK_FILE + flock --timeout $LXC_TRANSACTION_TIMEOUT 7 +} + +function lxc_transaction_unlock() { + exec 7>&- +} + +function lxc_transaction_draft_name() { + echo "lxc-helper-draft" +} + +function lxc_transaction_begin() { + local name=$1 # not actually used but it helps when reading in the caller + local draft=$(lxc_transaction_draft_name) + + lxc_transaction_lock + lxc_container_destroy $draft + + echo $draft +} + +function lxc_transaction_commit() { + local name=$1 + local draft=$(lxc_transaction_draft_name) + + # do not use lxc-copy because it is not atomic if lxc-copy is + # interrupted it may leave the $name container half populated + $LXC_SUDO sed -i -e "s/$draft/$name/g" \ + $(lxc_config $draft) \ + $(lxc_root $draft)/etc/hosts \ + $(lxc_root $draft)/etc/hostname + $LXC_SUDO rm -f $(lxc_root $draft)/var/lib/dhcp/dhclient.* + $LXC_SUDO mv $(lxc_directory $draft) $(lxc_directory $name) + lxc_transaction_unlock +} + function lxc_container_run_script_as() { local name="$1" local user="$2" @@ -242,7 +288,7 @@ function lxc_container_configure() { function lxc_container_install_lxc_helpers() { local name="$1" - $LXC_SUDO cp -a $LXC_SELF_DIR/lxc-helpers*.sh $root/$LXC_BIN + $LXC_SUDO cp -a $LXC_SELF_DIR/lxc-helpers*.sh $(lxc_root $name)/$LXC_BIN # # Wait for the network to come up # @@ -304,10 +350,9 @@ function lxc_container_stop() { function lxc_container_destroy() { local name="$1" - local root="$2" if lxc_exists "$name"; then - lxc_container_stop $name $root + lxc_container_stop $name $LXC_SUDO lxc-destroy --force --name="$name" fi } @@ -346,14 +391,15 @@ function lxc_build_template_release() { return fi - local root=$(lxc_root $name) - $LXC_SUDO lxc-create --name $name --template debian -- --release=$LXC_CONTAINER_RELEASE - echo 'lxc.apparmor.profile = unconfined' | $LXC_SUDO tee -a $(lxc_config $name) - lxc_container_install_lxc_helpers $name - lxc_container_start $name - lxc_container_run $name apt-get update -qq - lxc_apt_install $name sudo git python3 - lxc_container_stop $name + local draft=$(lxc_transaction_begin $name) + $LXC_SUDO lxc-create --name $draft --template debian -- --release=$LXC_CONTAINER_RELEASE + echo 'lxc.apparmor.profile = unconfined' | $LXC_SUDO tee -a $(lxc_config $draft) + lxc_container_install_lxc_helpers $draft + lxc_container_start $draft + lxc_container_run $draft apt-get update -qq + lxc_apt_install $draft sudo git python3 + lxc_container_stop $draft + lxc_transaction_commit $name } function lxc_build_template() { @@ -368,10 +414,12 @@ function lxc_build_template() { lxc_build_template_release fi - if ! $LXC_SUDO lxc-copy --name=$name --newname=$newname; then - echo lxc-copy --name=$name --newname=$newname failed + local draft=$(lxc_transaction_begin $newname) + if ! $LXC_SUDO lxc-copy --name=$name --newname=$draft; then + echo lxc-copy --name=$name --newname=$draft failed return 1 fi + lxc_transaction_commit $newname lxc_container_configure $newname } From 413a52605df9d06dc4d6f375164f80eb2a6649bd Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 3 Oct 2025 16:14:08 +0000 Subject: [PATCH 06/18] fix: remove LXC backend leftovers when the job completes (#1054) - remove the root of the LXC container after destroying it, with sudo as it may contain files owned by root while the runner id is not root - os.RemoveAll only for native host runs, it is no longer needed for the LXC backend - remove the CleanUp function that is an indirection with no use Resolves forgejo/runner#442 When running the test from a non-root user and without this fix, it fails as follow: ``` go test -v -count=1 -run='TestRunnerLXC' ./internal/app/run === RUN TestRunnerLXC ... time="2025-10-03T15:05:12+02:00" level=debug msg=stopHostEnvironment time="2025-10-03T15:05:13+02:00" level=debug msg="HostEnvironment.Remove /tmp/TestRunnerLXC1841090130/001/d29c1256e2912892/hostexecutor" time="2025-10-03T15:05:13+02:00" level=error msg="Error while stop job container FORGEJO-ACTIONS-TASK-0_WORKFLOW-3ede81fbc69d42e6db70bef5820490fc3e7dc4d9dcbfb64981f2d00f08a30d6e_JOB-job: unlinkat /tmp/TestRunnerLXC1841090130/001/d29c1256e2912892/hostexecutor/some/directory/owned/by/root: permission denied" === NAME TestRunnerLXC runner_test.go:469: Error Trace: /home/earl-warren/software/runner/internal/app/run/runner_test.go:469 /home/earl-warren/software/runner/internal/app/run/runner_test.go:496 Error: Received unexpected error: Error occurred running finally: unlinkat /tmp/TestRunnerLXC1841090130/001/d29c1256e2912892/hostexecutor/some/directory/owned/by/root: permission denied (original error: ) Test: TestRunnerLXC Messages: OK === NAME TestRunnerLXC/OK testing.go:1679: test executed panic(nil) or runtime.Goexit: subtest may have called FailNow on a parent test === NAME TestRunnerLXC testing.go:1267: TempDir RemoveAll cleanup: unlinkat /tmp/TestRunnerLXC1841090130/001/d29c1256e2912892/hostexecutor/some/directory/owned/by/root: permission denied --- FAIL: TestRunnerLXC (6.84s) --- FAIL: TestRunnerLXC/OK (6.84s) FAIL FAIL code.forgejo.org/forgejo/runner/v11/internal/app/run 6.847s FAIL ``` - bug fixes - [PR](https://code.forgejo.org/forgejo/runner/pulls/1054): fix: remove LXC backend leftovers when the job completes Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1054 Reviewed-by: Mathieu Fenniak Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- act/container/host_environment.go | 10 +++++----- act/runner/run_context.go | 9 ++++----- internal/app/run/runner_test.go | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/act/container/host_environment.go b/act/container/host_environment.go index c8742008..34d24b9b 100644 --- a/act/container/host_environment.go +++ b/act/container/host_environment.go @@ -34,7 +34,6 @@ type HostEnvironment struct { Workdir string ActPath string Root string - CleanUp func() StdOut io.Writer LXC bool } @@ -404,11 +403,12 @@ func (e *HostEnvironment) UpdateFromEnv(srcPath string, env *map[string]string) func (e *HostEnvironment) Remove() common.Executor { return func(ctx context.Context) error { - if e.CleanUp != nil { - e.CleanUp() + if e.GetLXC() { + // there may be files owned by root: removal + // is the responsibility of the LXC backend + return nil } - common.Logger(ctx).Debugf("HostEnvironment.Remove %s", e.Path) - return os.RemoveAll(e.Path) + return os.RemoveAll(e.Root) } } diff --git a/act/runner/run_context.go b/act/runner/run_context.go index bb79ad3a..59fcf6f8 100644 --- a/act/runner/run_context.go +++ b/act/runner/run_context.go @@ -248,6 +248,8 @@ var stopTemplate = template.Must(template.New("stop").Parse(`#!/bin/bash source $(dirname $0)/lxc-helpers-lib.sh lxc_container_destroy "{{.Name}}" +lxc_maybe_sudo +$LXC_SUDO rm -fr "{{ .Root }}" `)) func (rc *RunContext) stopHostEnvironment(ctx context.Context) error { @@ -314,11 +316,8 @@ func (rc *RunContext) startHostEnvironment() common.Executor { ToolCache: rc.getToolCache(ctx), Workdir: rc.Config.Workdir, ActPath: actPath, - CleanUp: func() { - os.RemoveAll(miscpath) - }, - StdOut: logWriter, - LXC: rc.IsLXCHostEnv(ctx), + StdOut: logWriter, + LXC: rc.IsLXCHostEnv(ctx), } rc.cleanUpJobContainer = func(ctx context.Context) error { if err := rc.stopHostEnvironment(ctx); err != nil { diff --git a/internal/app/run/runner_test.go b/internal/app/run/runner_test.go index eda5de9e..bbd6c235 100644 --- a/internal/app/run/runner_test.go +++ b/internal/app/run/runner_test.go @@ -491,7 +491,7 @@ jobs: job: runs-on: lxc steps: - - run: echo OK + - run: mkdir -p some/directory/owned/by/root ` runWorkflow(ctx, cancel, workflow, "push", "refs/heads/main", "OK") }) From 996ac343ee155a4b47d89a6bf1854feb1c04fdb2 Mon Sep 17 00:00:00 2001 From: limiting-factor Date: Fri, 3 Oct 2025 16:15:43 +0000 Subject: [PATCH 07/18] chore(cleanup): lxc-helpers does not need a global lock (#1047) - https://code.forgejo.org/forgejo/lxc-helpers/src/tag/v1.1.1 has locking and atomicity - this lock was only good for when two release templates are populated simultaneously and not when it was interrupted Refs forgejo/lxc-helpers#8 - other - [PR](https://code.forgejo.org/forgejo/runner/pulls/1047): WIP: chore(cleanup): lxc-helpers does not need a global lock Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1047 Reviewed-by: earl-warren Co-authored-by: limiting-factor Co-committed-by: limiting-factor --- act/runner/run_context.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/act/runner/run_context.go b/act/runner/run_context.go index 59fcf6f8..ff551322 100644 --- a/act/runner/run_context.go +++ b/act/runner/run_context.go @@ -193,8 +193,6 @@ var lxcHelpers string var startTemplate = template.Must(template.New("start").Parse(`#!/bin/bash -e -exec 5<>/tmp/forgejo-runner-lxc.lock ; flock --timeout 21600 5 - LXC_CONTAINER_CONFIG="{{.Config}}" LXC_CONTAINER_RELEASE="{{.Release}}" From 2bccbec3aec926d28894e73dc42690d7b64b4367 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 4 Oct 2025 11:15:31 +0000 Subject: [PATCH 08/18] Update forgejo-runner to v11.1.2 (#1055) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [code.forgejo.org/forgejo/runner](https://forgejo.org) ([source](https://code.forgejo.org/forgejo/runner)) | patch | `11.1.1` -> `11.1.2` | | [forgejo/runner](https://code.forgejo.org/forgejo/runner) | patch | `11.1.1` -> `11.1.2` | --- ### Release Notes
forgejo/runner (code.forgejo.org/forgejo/runner) ### [`v11.1.2`](https://code.forgejo.org/forgejo/runner/releases/tag/v11.1.2) [Compare Source](https://code.forgejo.org/forgejo/runner/compare/v11.1.1...v11.1.2) - [User guide](https://forgejo.org/docs/next/user/actions/overview/) - [Administrator guide](https://forgejo.org/docs/next/admin/actions/) - [Container images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions) Release Notes *** - features - [PR](https://code.forgejo.org/forgejo/runner/pulls/1026): feat: support evaluating workflow-level concurrency blocks in jobparser - bug fixes - [PR](https://code.forgejo.org/forgejo/runner/pulls/1051): fix(security): a multiline secret may be found in a single log entry - [PR](https://code.forgejo.org/forgejo/runner/pulls/1048): fix: improve logging to diagnose mystery job terminations - [PR](https://code.forgejo.org/forgejo/runner/pulls/1043): fix: modifying a cache secret does not invalidate cached entries - [PR](https://code.forgejo.org/forgejo/runner/pulls/1040): fix: allow GC & cache operations to operate concurrently - [PR](https://code.forgejo.org/forgejo/runner/pulls/1037): fix: do not attempt to run the LXC stop script with self-hosted - [PR](https://code.forgejo.org/forgejo/runner/pulls/1031): fix: event.pull\_request.action == closed can use the cache of the base repository - [PR](https://code.forgejo.org/forgejo/runner/pulls/1027): fix: Correctly override the value of `Forgejo-Cache-Host` when ACTIONS\_CACHE\_URL is overridden. - [PR](https://code.forgejo.org/forgejo/runner/pulls/1019): fix: a composite action must not change the result of the calling step before it completes - other - [PR](https://code.forgejo.org/forgejo/runner/pulls/1033): chore: test: exercise contexts in matrix when validating workflows - [PR](https://code.forgejo.org/forgejo/runner/pulls/1029): Update module connectrpc.com/connect to v1.19.0 - [PR](https://code.forgejo.org/forgejo/runner/pulls/1025): chore: fix typo in the documentation - [PR](https://code.forgejo.org/forgejo/runner/pulls/1023): Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.5.0 - [PR](https://code.forgejo.org/forgejo/runner/pulls/1022): Update code.forgejo.org/forgejo/forgejo Docker tag to v11.0.6 - [PR](https://code.forgejo.org/forgejo/runner/pulls/1020): chore: upgrade LXC example to Debian GNU/Linux trixie - [PR](https://code.forgejo.org/forgejo/runner/pulls/1021): Update action to v3.0.4 - [PR](https://code.forgejo.org/forgejo/runner/pulls/1018): Update forgejo-runner to v11.1.1
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1055 Reviewed-by: earl-warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- examples/docker-compose/compose-forgejo-and-runner.yml | 4 ++-- examples/lxc-systemd/forgejo-runner-service.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/docker-compose/compose-forgejo-and-runner.yml b/examples/docker-compose/compose-forgejo-and-runner.yml index 7aeae3ec..201650ae 100644 --- a/examples/docker-compose/compose-forgejo-and-runner.yml +++ b/examples/docker-compose/compose-forgejo-and-runner.yml @@ -51,7 +51,7 @@ services: - 8080:3000 runner-register: - image: code.forgejo.org/forgejo/runner:11.1.1 + image: code.forgejo.org/forgejo/runner:11.1.2 links: - docker-in-docker - forgejo @@ -77,7 +77,7 @@ services: ' runner-daemon: - image: code.forgejo.org/forgejo/runner:11.1.1 + image: code.forgejo.org/forgejo/runner:11.1.2 links: - docker-in-docker - forgejo diff --git a/examples/lxc-systemd/forgejo-runner-service.sh b/examples/lxc-systemd/forgejo-runner-service.sh index 0c1e2204..e949f3de 100755 --- a/examples/lxc-systemd/forgejo-runner-service.sh +++ b/examples/lxc-systemd/forgejo-runner-service.sh @@ -22,7 +22,7 @@ trap "rm -fr $TMPDIR" EXIT : ${INPUTS_LIFETIME:=7d} DEFAULT_LXC_HELPERS_VERSION=1.1.1 # renovate: datasource=forgejo-tags depName=forgejo/lxc-helpers : ${INPUTS_LXC_HELPERS_VERSION:=$DEFAULT_LXC_HELPERS_VERSION} -DEFAULT_RUNNER_VERSION=11.1.1 # renovate: datasource=forgejo-releases depName=forgejo/runner +DEFAULT_RUNNER_VERSION=11.1.2 # renovate: datasource=forgejo-releases depName=forgejo/runner : ${INPUTS_RUNNER_VERSION:=$DEFAULT_RUNNER_VERSION} : ${KILL_AFTER:=21600} # 6h == 21600 From 5f3ff3d2e2020b0ca16dfae69baa5adbfff62160 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sat, 4 Oct 2025 11:16:16 +0000 Subject: [PATCH 09/18] chore: teach renovate about example-lxc-systemd.yml (#1053) Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1053 Reviewed-by: Michael Kriese Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- .forgejo/workflows/example-lxc-systemd.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/example-lxc-systemd.yml b/.forgejo/workflows/example-lxc-systemd.yml index e333092b..c8907609 100644 --- a/.forgejo/workflows/example-lxc-systemd.yml +++ b/.forgejo/workflows/example-lxc-systemd.yml @@ -14,6 +14,7 @@ env: SERIAL: "30" LIFETIME: "60" SYSTEMD_OPTIONS: "--no-pager --full" + USE_VERSION: 11.0.6 # renovate: datasource=docker depName=code.forgejo.org/forgejo/forgejo jobs: example-lxc-systemd: @@ -57,7 +58,7 @@ jobs: with: user: root password: admin1234 - binary: https://code.forgejo.org/forgejo/forgejo/releases/download/v7.0.12/forgejo-7.0.12-linux-amd64 + binary: https://code.forgejo.org/forgejo/forgejo/releases/download/v${{ env.USE_VERSION }}/forgejo-${{ env.USE_VERSION }}-linux-amd64 # must be the same as LXC_IPV4_PREFIX in examples/lxc-systemd/forgejo-runner-service.sh lxc-ip-prefix: 10.105.7 From 4010e6116501bae290629330aa54b46d98304a4b Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 5 Oct 2025 07:39:29 +0000 Subject: [PATCH 10/18] Update module google.golang.org/protobuf to v1.36.10 (#1058) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) | `v1.36.9` -> `v1.36.10` | [![age](https://developer.mend.io/api/mc/badges/age/go/google.golang.org%2fprotobuf/v1.36.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/google.golang.org%2fprotobuf/v1.36.9/v1.36.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
protocolbuffers/protobuf-go (google.golang.org/protobuf) ### [`v1.36.10`](https://github.com/protocolbuffers/protobuf-go/releases/tag/v1.36.10) [Compare Source](https://github.com/protocolbuffers/protobuf-go/compare/v1.36.9...v1.36.10) **Full Changelog**: Bug fixes: [CL/704415](https://go-review.googlesource.com/c/protobuf/+/704415): reflect/protodesc: edition-2024-specific properties should not be lost when converting FileDescriptorProto to protoreflect.FileDescriptor Maintenance: [CL/708555](https://go-review.googlesource.com/c/protobuf/+/708555): internal/race\_test: add missing impl.LazyEnabled() t.Skip [CL/703295](https://go-review.googlesource.com/c/protobuf/+/703295): proto: add more invalid group encoding test cases [CL/703276](https://go-review.googlesource.com/c/protobuf/+/703276): internal/impl: verify lazy unmarshal on Deterministic encoding [CL/703275](https://go-review.googlesource.com/c/protobuf/+/703275): internal/impl: stop using deprecated .Field in lazy\_test.go [CL/702795](https://go-review.googlesource.com/c/protobuf/+/702795): all: update to latest github.com/google/go-cmp
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1058 Reviewed-by: earl-warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d1763deb..0ebcf50e 100644 --- a/go.mod +++ b/go.mod @@ -39,7 +39,7 @@ require ( go.yaml.in/yaml/v3 v3.0.4 golang.org/x/term v0.35.0 golang.org/x/time v0.13.0 - google.golang.org/protobuf v1.36.9 + google.golang.org/protobuf v1.36.10 gotest.tools/v3 v3.5.2 ) diff --git a/go.sum b/go.sum index d5b9a302..5231c673 100644 --- a/go.sum +++ b/go.sum @@ -260,8 +260,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From ba1beb08f4c774df55f3b431bf6c516384956949 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Sun, 5 Oct 2025 07:40:06 +0000 Subject: [PATCH 11/18] fix: log error for why cache couldn't be started (#1057) Example, `cache.port` set to `22`: ``` time="2025-10-04T09:27:42-06:00" level=error msg="Could not start the cache server, cache will be disabled: listen tcp :22: bind: permission denied" func="[setupCache]" file="[runner.go:126]" ``` - bug fixes - [PR](https://code.forgejo.org/forgejo/runner/pulls/1057): fix: log error for why cache couldn't be started Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1057 Reviewed-by: limiting-factor Co-authored-by: Mathieu Fenniak Co-committed-by: Mathieu Fenniak --- internal/app/run/runner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/app/run/runner.go b/internal/app/run/runner.go index 1bf32c78..82deea2b 100644 --- a/internal/app/run/runner.go +++ b/internal/app/run/runner.go @@ -123,7 +123,7 @@ func setupCache(cfg *config.Config, envs map[string]string) *cacheproxy.Handler log.StandardLogger().WithField("module", "cache_request"), ) if err != nil { - log.Error("Could not start the cache server, cache will be disabled") + log.Errorf("Could not start the cache server, cache will be disabled: %v", err) return nil } From e68940619c1194f86f527125f2be1af9aefc5b20 Mon Sep 17 00:00:00 2001 From: Andrew Cassidy Date: Mon, 6 Oct 2025 03:51:19 +0000 Subject: [PATCH 12/18] chore(docs): clarify the behavior of `[container].docker_host` (#1059) The description of `container.docker_host` was misleading (and IMO the setting itself does too many things, see forgejo/forgejo-actions-feature-requests#58). I clarified the comments in the example config to clearly explain that it will mount the specified socket inside the job container, and give some example values - other - [PR](https://code.forgejo.org/forgejo/runner/pulls/1059): Clarify the behavior of `container.docker_host` Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1059 Reviewed-by: earl-warren Co-authored-by: Andrew Cassidy Co-committed-by: Andrew Cassidy --- internal/pkg/config/config.example.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/pkg/config/config.example.yaml b/internal/pkg/config/config.example.yaml index c32526e1..5addac41 100644 --- a/internal/pkg/config/config.example.yaml +++ b/internal/pkg/config/config.example.yaml @@ -176,10 +176,12 @@ container: # valid_volumes: # - '**' valid_volumes: [] - # overrides the docker client host with the specified one. - # If "-" or "", an available docker host will automatically be found. + # Overrides the docker host set by the DOCKER_HOST environment variable, and mounts on the job container. + # If "-" or "", no docker host will be mounted in the job container # If "automount", an available docker host will automatically be found and mounted in the job container (e.g. /var/run/docker.sock). - # Otherwise the specified docker host will be used and an error will be returned if it doesn't work. + # If it's a url, the specified docker host will be mounted in the job container + # Example urls: unix:///run/docker.socket or ssh://user@host + # The specified socket is mounted within the job container at /var/run/docker.sock docker_host: "-" # Pull docker image(s) even if already present force_pull: false From a22c5a2e659ef1cc296681940f4db24e078ac446 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 6 Oct 2025 04:22:26 +0000 Subject: [PATCH 13/18] Update module github.com/go-git/go-git/v5 to v5.16.3 (#1062) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) | `v5.16.2` -> `v5.16.3` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fgo-git%2fgo-git%2fv5/v5.16.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fgo-git%2fgo-git%2fv5/v5.16.2/v5.16.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
go-git/go-git (github.com/go-git/go-git/v5) ### [`v5.16.3`](https://github.com/go-git/go-git/releases/tag/v5.16.3) [Compare Source](https://github.com/go-git/go-git/compare/v5.16.2...v5.16.3) #### What's Changed - internal: Expand regex to fix build \[5.x] by [@​baloo](https://github.com/baloo) in [#​1644](https://github.com/go-git/go-git/pull/1644) - build: raise timeouts for windows CI tests and disable CIFuzz \[5.x] by [@​baloo](https://github.com/baloo) in [#​1646](https://github.com/go-git/go-git/pull/1646) - plumbing: support commits extra headers, support jujutsu signed commit \[5.x] by [@​baloo](https://github.com/baloo) in [#​1633](https://github.com/go-git/go-git/pull/1633) **Full Changelog**:
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1062 Reviewed-by: earl-warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0ebcf50e..ec37b2c5 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/docker/docker v28.4.0+incompatible github.com/docker/go-connections v0.6.0 github.com/go-git/go-billy/v5 v5.6.2 - github.com/go-git/go-git/v5 v5.16.2 + github.com/go-git/go-git/v5 v5.16.3 github.com/gobwas/glob v0.2.3 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index 5231c673..59f8c95c 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM= -github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= +github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8= +github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= From 0a0b25d88609b47c4ebd2068b4edc19df6f71d98 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Tue, 7 Oct 2025 08:09:14 +0000 Subject: [PATCH 14/18] fix: don't panic on cacheproxy startup failure (#1067) In #1064 I theorized that a failure to start up the cache proxy server might cause it to still be registered as `ACTIONS_CACHE_URL`. It turns out to not be the case as it will just panic, since `cacheProxy` is `nil`. But regardless, it seems better to not panic and crash if the intent here is "cache will be disabled" as documented in the log message. - bug fixes - [PR](https://code.forgejo.org/forgejo/runner/pulls/1067): fix: don't panic on cacheproxy startup failure Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1067 Reviewed-by: earl-warren Co-authored-by: Mathieu Fenniak Co-committed-by: Mathieu Fenniak --- internal/app/run/runner.go | 4 +- internal/app/run/runner_test.go | 93 +++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/internal/app/run/runner.go b/internal/app/run/runner.go index 82deea2b..31a1b320 100644 --- a/internal/app/run/runner.go +++ b/internal/app/run/runner.go @@ -150,10 +150,10 @@ func setupCache(cfg *config.Config, envs map[string]string) *cacheproxy.Handler ) if err != nil { log.Errorf("cannot init cache proxy, cache will be disabled: %v", err) + } else { + envs["ACTIONS_CACHE_URL"] = cacheProxy.ExternalURL() } - envs["ACTIONS_CACHE_URL"] = cacheProxy.ExternalURL() - return cacheProxy } diff --git a/internal/app/run/runner_test.go b/internal/app/run/runner_test.go index bbd6c235..5de1b58c 100644 --- a/internal/app/run/runner_test.go +++ b/internal/app/run/runner_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net" "os" "testing" "time" @@ -421,6 +422,98 @@ jobs: }) } +func TestRunnerCacheStartupFailure(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + testCases := []struct { + desc string + listen string + }{ + { + desc: "disable cache server", + listen: "127.0.0.1:40715", + }, + { + desc: "disable cache proxy server", + listen: "127.0.0.1:40716", + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + 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) + + // We'll be listening on some network port in this test that will conflict with the cache configuration... + l, err := net.Listen("tcp4", tc.listen) + require.NoError(t, err) + defer l.Close() + + runner := NewRunner( + &config.Config{ + Cache: config.Cache{ + Port: 40715, + ProxyPort: 40716, + Dir: t.TempDir(), + }, + Host: config.Host{ + WorkdirParent: t.TempDir(), + }, + }, + &config.Registration{ + Labels: []string{"ubuntu-latest:docker://code.forgejo.org/oci/node:20-bookworm"}, + }, + forgejoClient) + require.NotNil(t, runner) + + // Ensure that cacheProxy failed to start + assert.Nil(t, runner.cacheProxy) + + runWorkflow := func(ctx context.Context, cancel context.CancelFunc, yamlContent 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("push"), + "ref": structpb.NewStringValue("refs/heads/main"), + }, + }, + } + + reporter := report.NewReporter(ctx, cancel, forgejoClient, task, time.Second) + err := runner.run(ctx, task, reporter) + reporter.Close(nil) + require.NoError(t, err) + } + + ctx, cancel := context.WithCancel(t.Context()) + defer cancel() + + checkCacheYaml := ` +name: Verify No ACTIONS_CACHE_URL +on: + push: +jobs: + job-cache-check-1: + runs-on: ubuntu-latest + steps: + - run: echo $ACTIONS_CACHE_URL + - run: '[[ "$ACTIONS_CACHE_URL" = "" ]] || exit 1' +` + runWorkflow(ctx, cancel, checkCacheYaml) + }) + } +} + func TestRunnerLXC(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") From a3d46d7597e31d491ea70e2a76152177613507ec Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Tue, 7 Oct 2025 08:12:16 +0000 Subject: [PATCH 15/18] chore: add additional logging to cacheproxy (#1065) In support of diagnosing #1064, I hope. - other - [PR](https://code.forgejo.org/forgejo/runner/pulls/1065): chore: add additional logging to cacheproxy Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1065 Reviewed-by: earl-warren Co-authored-by: Mathieu Fenniak Co-committed-by: Mathieu Fenniak --- act/cacheproxy/handler.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/act/cacheproxy/handler.go b/act/cacheproxy/handler.go index e494b9c5..499c5167 100644 --- a/act/cacheproxy/handler.go +++ b/act/cacheproxy/handler.go @@ -64,7 +64,7 @@ func StartHandler(targetHost, outboundIP string, port uint16, cacheProxyHostOver discard.Out = io.Discard logger = discard } - logger = logger.WithField("module", "artifactcache") + logger = logger.WithField("module", "cacheproxy") h.logger = logger h.cacheSecret = cacheSecret @@ -139,6 +139,7 @@ func (h *Handler) newReverseProxy(targetHost string) (*httputil.ReverseProxy, er r.SetURL(targetURL) r.Out.URL.Path = uri + h.logger.Debugf("proxy req %s %q to %q", r.In.Method, r.In.URL, r.Out.URL) r.Out.Header.Set("Forgejo-Cache-Repo", runData.RepositoryFullName) r.Out.Header.Set("Forgejo-Cache-RunNumber", runData.RunNumber) @@ -150,6 +151,10 @@ func (h *Handler) newReverseProxy(targetHost string) (*httputil.ReverseProxy, er r.Out.Header.Set("Forgejo-Cache-WriteIsolationKey", runData.WriteIsolationKey) } }, + ModifyResponse: func(r *http.Response) error { + h.logger.Debugf("proxy resp %s w/ %d bytes", r.Status, r.ContentLength) + return nil + }, } return proxy, nil } From 041524d663fa205ec5e72a9dc22b9b01ffa2c481 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 7 Oct 2025 08:13:23 +0000 Subject: [PATCH 16/18] Update module github.com/docker/cli to v28.5.0+incompatible (#1066) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [github.com/docker/cli](https://github.com/docker/cli) | `v28.4.0+incompatible` -> `v28.5.0+incompatible` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fdocker%2fcli/v28.5.0+incompatible?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fdocker%2fcli/v28.4.0+incompatible/v28.5.0+incompatible?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
docker/cli (github.com/docker/cli) ### [`v28.5.0+incompatible`](https://github.com/docker/cli/compare/v28.4.0...v28.5.0) [Compare Source](https://github.com/docker/cli/compare/v28.4.0...v28.5.0)
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1066 Reviewed-by: earl-warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ec37b2c5..4e85c9b6 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/containerd/errdefs v1.0.0 github.com/creack/pty v1.1.24 github.com/distribution/reference v0.6.0 - github.com/docker/cli v28.4.0+incompatible + github.com/docker/cli v28.5.0+incompatible github.com/docker/docker v28.4.0+incompatible github.com/docker/go-connections v0.6.0 github.com/go-git/go-billy/v5 v5.6.2 diff --git a/go.sum b/go.sum index 59f8c95c..0cc56d8b 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY= -github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v28.5.0+incompatible h1:crVqLrtKsrhC9c00ythRx435H8LiQnUKRtJLRR+Auxk= +github.com/docker/cli v28.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk= github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= From 3f52c56d1e24c31297290eacfd035fb703e6b810 Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Tue, 7 Oct 2025 14:41:55 +0000 Subject: [PATCH 17/18] feat: improve readability of error messages from ParseRawOn (#1063) With https://codeberg.org/forgejo/forgejo/pulls/9530, the error messages from `ParseRawOn` are user-facing and need a pass to improve their meaning. - features - [PR](https://code.forgejo.org/forgejo/runner/pulls/1063): feat: improve readability of error messages from ParseRawOn Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1063 Reviewed-by: earl-warren Co-authored-by: Mathieu Fenniak Co-committed-by: Mathieu Fenniak --- act/jobparser/model.go | 60 ++++++++++++++++----------------- act/jobparser/model_test.go | 67 ++++++++++++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 34 deletions(-) diff --git a/act/jobparser/model.go b/act/jobparser/model.go index e94123e3..74de0450 100644 --- a/act/jobparser/model.go +++ b/act/jobparser/model.go @@ -2,6 +2,7 @@ package jobparser import ( "fmt" + "strings" "code.forgejo.org/forgejo/runner/v11/act/model" "go.yaml.in/yaml/v3" @@ -226,7 +227,7 @@ func ParseRawOn(rawOn *yaml.Node) ([]*Event, error) { var val string err := rawOn.Decode(&val) if err != nil { - return nil, err + return nil, fmt.Errorf("unable to interpret scalar value into a string: %w", err) } return []*Event{ {Name: val}, @@ -238,12 +239,12 @@ func ParseRawOn(rawOn *yaml.Node) ([]*Event, error) { return nil, err } res := make([]*Event, 0, len(val)) - for _, v := range val { + for i, v := range val { switch t := v.(type) { case string: res = append(res, &Event{Name: t}) default: - return nil, fmt.Errorf("invalid type %T", t) + return nil, fmt.Errorf("value at index %d was unexpected type %[2]T; must be a string but was %#[2]v", i, v) } } return res, nil @@ -263,16 +264,6 @@ func ParseRawOn(rawOn *yaml.Node) ([]*Event, error) { continue } switch t := v.(type) { - case string: - res = append(res, &Event{ - Name: k, - acts: map[string][]string{}, - }) - case []string: - res = append(res, &Event{ - Name: k, - acts: map[string][]string{}, - }) case map[string]any: acts := make(map[string][]string, len(t)) for act, branches := range t { @@ -286,15 +277,15 @@ func ParseRawOn(rawOn *yaml.Node) ([]*Event, error) { for i, v := range b { var ok bool if acts[act][i], ok = v.(string); !ok { - return nil, fmt.Errorf("unknown on type: %#v", branches) + return nil, fmt.Errorf("key %q.%q index %d had unexpected type %[4]T; a string was expected but was %#[4]v", k, act, i, v) } } case map[string]any: - if isInvalidOnType(k, act) { - return nil, fmt.Errorf("unknown on type: %#v", v) + if err := isInvalidOnType(k, act); err != nil { + return nil, fmt.Errorf("invalid value on key %q: %w", k, err) } default: - return nil, fmt.Errorf("unknown on type: %#v", branches) + return nil, fmt.Errorf("key %q.%q had unexpected type %T; was %#v", k, act, branches, branches) } } if k == "workflow_dispatch" || k == "workflow_call" { @@ -306,19 +297,22 @@ func ParseRawOn(rawOn *yaml.Node) ([]*Event, error) { }) case []any: if k != "schedule" { - return nil, fmt.Errorf("unknown on type: %#v", v) + return nil, fmt.Errorf("key %q had an type %T; only the 'schedule' key is expected with this type", k, v) } schedules := make([]map[string]string, len(t)) for i, tt := range t { vv, ok := tt.(map[string]any) if !ok { - return nil, fmt.Errorf("unknown on type: %#v", v) + return nil, fmt.Errorf("key %q[%d] had unexpected type %[3]T; a map with a key \"cron\" was expected, but value was %#[3]v", k, i, tt) } schedules[i] = make(map[string]string, len(vv)) - for k, vvv := range vv { + for kk, vvv := range vv { + if strings.ToLower(kk) != "cron" { + return nil, fmt.Errorf("key %q[%d] had unexpected key %q; \"cron\" was expected", k, i, kk) + } var ok bool - if schedules[i][k], ok = vvv.(string); !ok { - return nil, fmt.Errorf("unknown on type: %#v", v) + if schedules[i][kk], ok = vvv.(string); !ok { + return nil, fmt.Errorf("key %q[%d].%q had unexpected type %[4]T; a string was expected by was %#[4]v", k, i, kk, vvv) } } } @@ -327,23 +321,29 @@ func ParseRawOn(rawOn *yaml.Node) ([]*Event, error) { schedules: schedules, }) default: - return nil, fmt.Errorf("unknown on type: %#v", v) + return nil, fmt.Errorf("key %q had unexpected type %[2]T; expected a map or array but was %#[2]v", k, v) } } return res, nil default: - return nil, fmt.Errorf("unknown on type: %v", rawOn.Kind) + return nil, fmt.Errorf("unexpected yaml node in `on`: %v", rawOn.Kind) } } -func isInvalidOnType(onType, subKey string) bool { - if onType == "workflow_dispatch" && subKey == "inputs" { - return false +func isInvalidOnType(onType, subKey string) error { + if onType == "workflow_dispatch" { + if subKey == "inputs" { + return nil + } + return fmt.Errorf("workflow_dispatch only supports key \"inputs\", but key %q was found", subKey) } - if onType == "workflow_call" && (subKey == "inputs" || subKey == "outputs") { - return false + if onType == "workflow_call" { + if subKey == "inputs" || subKey == "outputs" { + return nil + } + return fmt.Errorf("workflow_call only supports keys \"inputs\" and \"outputs\", but key %q was found", subKey) } - return true + return fmt.Errorf("unexpected key %q.%q", onType, subKey) } // parseMappingNode parse a mapping node and preserve order. diff --git a/act/jobparser/model_test.go b/act/jobparser/model_test.go index 45d77428..454de71c 100644 --- a/act/jobparser/model_test.go +++ b/act/jobparser/model_test.go @@ -16,6 +16,7 @@ func TestParseRawOn(t *testing.T) { kases := []struct { input string result []*Event + err string }{ { input: "on: issue_comment", @@ -33,7 +34,10 @@ func TestParseRawOn(t *testing.T) { }, }, }, - + { + input: "on: [123]", + err: "value at index 0 was unexpected type int; must be a string but was 123", + }, { input: "on:\n - push\n - pull_request", result: []*Event{ @@ -45,6 +49,19 @@ func TestParseRawOn(t *testing.T) { }, }, }, + { + input: "on: { push: null }", + result: []*Event{ + { + Name: "push", + acts: map[string][]string{}, + }, + }, + }, + { + input: "on: { push: 'abc' }", + err: "key \"push\" had unexpected type string; expected a map or array but was \"abc\"", + }, { input: "on:\n push:\n branches:\n - master", result: []*Event{ @@ -72,6 +89,10 @@ func TestParseRawOn(t *testing.T) { }, }, }, + { + input: "on:\n branch_protection_rule:\n types: [123, deleted]", + err: "key \"branch_protection_rule\".\"types\" index 0 had unexpected type int; a string was expected but was 123", + }, { input: "on:\n project:\n types: [created, deleted]\n milestone:\n types: [opened, deleted]", result: []*Event{ @@ -189,6 +210,22 @@ func TestParseRawOn(t *testing.T) { }, }, }, + { + input: "on:\n schedule2:\n - cron: '20 6 * * *'", + err: "key \"schedule2\" had an type []interface {}; only the 'schedule' key is expected with this type", + }, + { + input: "on:\n schedule:\n - 123", + err: "key \"schedule\"[0] had unexpected type int; a map with a key \"cron\" was expected, but value was 123", + }, + { + input: "on:\n schedule:\n - corn: '20 6 * * *'", + err: "key \"schedule\"[0] had unexpected key \"corn\"; \"cron\" was expected", + }, + { + input: "on:\n schedule:\n - cron: 123", + err: "key \"schedule\"[0].\"cron\" had unexpected type int; a string was expected by was 123", + }, { input: ` on: @@ -222,15 +259,37 @@ on: }, }, }, + { + input: ` +on: + workflow_call: + mistake: + access-token: + description: 'A token passed from the caller workflow' + required: false +`, + err: "invalid value on key \"workflow_call\": workflow_call only supports keys \"inputs\" and \"outputs\", but key \"mistake\" was found", + }, + { + input: ` +on: + workflow_call: { map: 123 } +`, + err: "key \"workflow_call\".\"map\" had unexpected type int; was 123", + }, } for _, kase := range kases { t.Run(kase.input, func(t *testing.T) { origin, err := model.ReadWorkflow(strings.NewReader(kase.input), false) - assert.NoError(t, err) + require.NoError(t, err) events, err := ParseRawOn(&origin.RawOn) - assert.NoError(t, err) - assert.EqualValues(t, kase.result, events, fmt.Sprintf("%#v", events)) + if kase.err != "" { + assert.ErrorContains(t, err, kase.err) + } else { + assert.NoError(t, err) + assert.EqualValues(t, kase.result, events, fmt.Sprintf("%#v", events)) + } }) } } From b17b1f00b318535684897c5c9e49a2e10252b572 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 8 Oct 2025 09:42:15 +0000 Subject: [PATCH 18/18] Update dependency go to v1.24.8 (#1069) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [go](https://go.dev/) ([source](https://github.com/golang/go)) | toolchain | patch | `1.24.7` -> `1.24.8` | --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/1069 Reviewed-by: earl-warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 4e85c9b6..c10be44a 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module code.forgejo.org/forgejo/runner/v11 go 1.24.0 -toolchain go1.24.7 +toolchain go1.24.8 require ( code.forgejo.org/forgejo/actions-proto v0.5.2