mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-08-08 12:25:02 +02:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3879f14d89 | ||
|
|
0ee4643d4a | ||
|
|
e774003c18 | ||
|
|
eeb479ea89 | ||
|
|
eba33e178d |
+4
-4
@@ -17,14 +17,14 @@ RUN make clean && make build
|
|||||||
### DIND VARIANT
|
### DIND VARIANT
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
FROM docker:29.6.0-dind AS dind
|
FROM docker:29.6.1-dind AS dind
|
||||||
|
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
|
|
||||||
LABEL org.opencontainers.image.source="https://gitea.com/gitea/runner"
|
LABEL org.opencontainers.image.source="https://gitea.com/gitea/runner"
|
||||||
LABEL org.opencontainers.image.version="${VERSION}"
|
LABEL org.opencontainers.image.version="${VERSION}"
|
||||||
|
|
||||||
RUN apk add --no-cache s6 bash git tzdata
|
RUN apk add --no-cache s6 bash git tzdata nftables
|
||||||
|
|
||||||
COPY --from=builder /opt/src/runner/gitea-runner /usr/local/bin/gitea-runner
|
COPY --from=builder /opt/src/runner/gitea-runner /usr/local/bin/gitea-runner
|
||||||
COPY scripts/run.sh /usr/local/bin/run.sh
|
COPY scripts/run.sh /usr/local/bin/run.sh
|
||||||
@@ -37,7 +37,7 @@ ENTRYPOINT ["s6-svscan","/etc/s6"]
|
|||||||
### DIND-ROOTLESS VARIANT
|
### DIND-ROOTLESS VARIANT
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
FROM docker:29.6.0-dind-rootless AS dind-rootless
|
FROM docker:29.6.1-dind-rootless AS dind-rootless
|
||||||
|
|
||||||
ARG VERSION=dev
|
ARG VERSION=dev
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ LABEL org.opencontainers.image.source="https://gitea.com/gitea/runner"
|
|||||||
LABEL org.opencontainers.image.version="${VERSION}"
|
LABEL org.opencontainers.image.version="${VERSION}"
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
RUN apk add --no-cache s6 bash git tzdata
|
RUN apk add --no-cache s6 bash git tzdata nftables
|
||||||
|
|
||||||
COPY --from=builder /opt/src/runner/gitea-runner /usr/local/bin/gitea-runner
|
COPY --from=builder /opt/src/runner/gitea-runner /usr/local/bin/gitea-runner
|
||||||
COPY scripts/run.sh /usr/local/bin/run.sh
|
COPY scripts/run.sh /usr/local/bin/run.sh
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -85,6 +86,19 @@ func newCompositeRunContext(ctx context.Context, parent *RunContext, step action
|
|||||||
return compositerc
|
return compositerc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// appendUniqueMasks appends the masks from src to dst, skipping any mask that
|
||||||
|
// is already present in dst. This prevents the parent RunContext's Masks slice
|
||||||
|
// from growing exponentially when composite actions are nested or repeated,
|
||||||
|
// since each composite RunContext is seeded with its parent's masks.
|
||||||
|
func appendUniqueMasks(dst, src []string) []string {
|
||||||
|
for _, m := range src {
|
||||||
|
if !slices.Contains(dst, m) {
|
||||||
|
dst = append(dst, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
func execAsComposite(step actionStep) common.Executor {
|
func execAsComposite(step actionStep) common.Executor {
|
||||||
rc := step.getRunContext()
|
rc := step.getRunContext()
|
||||||
action := step.getActionModel()
|
action := step.getActionModel()
|
||||||
@@ -110,7 +124,11 @@ func execAsComposite(step actionStep) common.Executor {
|
|||||||
}, eval.Interpolate(ctx, output.Value))
|
}, eval.Interpolate(ctx, output.Value))
|
||||||
}
|
}
|
||||||
|
|
||||||
rc.Masks = append(rc.Masks, compositeRC.Masks...)
|
// compositeRC.Masks is seeded with rc.Masks (see newCompositeRunContext)
|
||||||
|
// and may have additional masks appended while the composite action runs.
|
||||||
|
// Only append masks that are not already present, otherwise nested or
|
||||||
|
// repeated composite actions grow rc.Masks exponentially.
|
||||||
|
rc.Masks = appendUniqueMasks(rc.Masks, compositeRC.Masks)
|
||||||
rc.ExtraPath = compositeRC.ExtraPath
|
rc.ExtraPath = compositeRC.ExtraPath
|
||||||
// compositeRC.Env is dirty, contains INPUT_ and merged step env, only rely on compositeRC.GlobalEnv
|
// compositeRC.Env is dirty, contains INPUT_ and merged step env, only rely on compositeRC.GlobalEnv
|
||||||
mergeIntoMap := mergeIntoMapCaseSensitive
|
mergeIntoMap := mergeIntoMapCaseSensitive
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAppendUniqueMasks(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
dst []string
|
||||||
|
src []string
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "appends new masks",
|
||||||
|
dst: []string{"a"},
|
||||||
|
src: []string{"b", "c"},
|
||||||
|
want: []string{"a", "b", "c"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "skips masks already present",
|
||||||
|
dst: []string{"a", "b"},
|
||||||
|
src: []string{"a", "b"},
|
||||||
|
want: []string{"a", "b"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "deduplicates within src",
|
||||||
|
dst: []string{"a"},
|
||||||
|
src: []string{"b", "b", "a"},
|
||||||
|
want: []string{"a", "b"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty src leaves dst unchanged",
|
||||||
|
dst: []string{"a"},
|
||||||
|
src: nil,
|
||||||
|
want: []string{"a"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.want, appendUniqueMasks(tt.dst, tt.src))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAppendUniqueMasksNoExponentialGrowth reproduces the exponential growth of
|
||||||
|
// the parent's Masks slice observed with nested/repeated composite actions. A
|
||||||
|
// composite RunContext is seeded with its parent's masks and the whole seeded
|
||||||
|
// slice was previously appended back into the parent, doubling its length on
|
||||||
|
// every composite action.
|
||||||
|
func TestAppendUniqueMasksNoExponentialGrowth(t *testing.T) {
|
||||||
|
parentMasks := []string{"secret"}
|
||||||
|
|
||||||
|
for range 20 {
|
||||||
|
// compositeRC.Masks starts as a copy of the parent's masks (it is
|
||||||
|
// seeded with parent.Masks in newCompositeRunContext).
|
||||||
|
compositeMasks := make([]string, len(parentMasks))
|
||||||
|
copy(compositeMasks, parentMasks)
|
||||||
|
|
||||||
|
parentMasks = appendUniqueMasks(parentMasks, compositeMasks)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, []string{"secret"}, parentMasks)
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ require (
|
|||||||
github.com/containerd/errdefs v1.0.0
|
github.com/containerd/errdefs v1.0.0
|
||||||
github.com/creack/pty v1.1.24
|
github.com/creack/pty v1.1.24
|
||||||
github.com/distribution/reference v0.6.0
|
github.com/distribution/reference v0.6.0
|
||||||
github.com/docker/cli v29.6.0+incompatible
|
github.com/docker/cli v29.6.1+incompatible
|
||||||
github.com/docker/go-connections v0.7.0
|
github.com/docker/go-connections v0.7.0
|
||||||
github.com/go-git/go-billy/v5 v5.9.0
|
github.com/go-git/go-billy/v5 v5.9.0
|
||||||
github.com/go-git/go-git/v5 v5.19.1
|
github.com/go-git/go-git/v5 v5.19.1
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ github.com/docker/cli v29.5.3+incompatible h1:nbEFfz774vBwQ5KRYv7c/AghjReqnGISvr
|
|||||||
github.com/docker/cli v29.5.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v29.5.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/cli v29.6.0+incompatible h1:nw9himxMMZ7eIeherJNlKQq+acnlzGgHd+4uf10QRSc=
|
github.com/docker/cli v29.6.0+incompatible h1:nw9himxMMZ7eIeherJNlKQq+acnlzGgHd+4uf10QRSc=
|
||||||
github.com/docker/cli v29.6.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v29.6.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
|
github.com/docker/cli v29.6.1+incompatible h1:oO7F4nn3Ovr/5TlfTUWFbMwBSS/B7Xs6Epv26gBrUP8=
|
||||||
|
github.com/docker/cli v29.6.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/docker-credential-helpers v0.9.6 h1:cT2PbRPSlnMmNTfT2TDMXRyQ1KMWHG7xoTLBcn1ZNv0=
|
github.com/docker/docker-credential-helpers v0.9.6 h1:cT2PbRPSlnMmNTfT2TDMXRyQ1KMWHG7xoTLBcn1ZNv0=
|
||||||
github.com/docker/docker-credential-helpers v0.9.6/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
github.com/docker/docker-credential-helpers v0.9.6/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||||
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
|
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ runner:
|
|||||||
# The maximum interval for fetching the job from the Gitea instance.
|
# The maximum interval for fetching the job from the Gitea instance.
|
||||||
# The runner uses exponential backoff when idle, increasing the interval up to this maximum.
|
# The runner uses exponential backoff when idle, increasing the interval up to this maximum.
|
||||||
# Set to 0 or same as fetch_interval to disable backoff.
|
# Set to 0 or same as fetch_interval to disable backoff.
|
||||||
fetch_interval_max: 5s
|
fetch_interval_max: 1m
|
||||||
# While idle, remove stale bind-workdir task directories and orphaned host-mode
|
# While idle, remove stale bind-workdir task directories and orphaned host-mode
|
||||||
# scratch directories (left behind when a host cleanup delete stalls) older than
|
# scratch directories (left behind when a host cleanup delete stalls) older than
|
||||||
# this duration. Setting either workdir_cleanup_age or idle_cleanup_interval to 0
|
# this duration. Setting either workdir_cleanup_age or idle_cleanup_interval to 0
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ func LoadDefault(file string) (*Config, error) {
|
|||||||
cfg.Runner.FetchInterval = 2 * time.Second
|
cfg.Runner.FetchInterval = 2 * time.Second
|
||||||
}
|
}
|
||||||
if cfg.Runner.FetchIntervalMax <= 0 {
|
if cfg.Runner.FetchIntervalMax <= 0 {
|
||||||
cfg.Runner.FetchIntervalMax = 5 * time.Second
|
cfg.Runner.FetchIntervalMax = time.Minute
|
||||||
}
|
}
|
||||||
if cfg.Runner.WorkdirCleanupAge == 0 && !definedRunnerKeys["workdir_cleanup_age"] {
|
if cfg.Runner.WorkdirCleanupAge == 0 && !definedRunnerKeys["workdir_cleanup_age"] {
|
||||||
cfg.Runner.WorkdirCleanupAge = 24 * time.Hour
|
cfg.Runner.WorkdirCleanupAge = 24 * time.Hour
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ func TestLoadDefault_DefaultsWorkdirCleanupAge(t *testing.T) {
|
|||||||
assert.Equal(t, 10*time.Minute, cfg.Runner.IdleCleanupInterval)
|
assert.Equal(t, 10*time.Minute, cfg.Runner.IdleCleanupInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadDefault_DefaultsIdleFetchBackoffMax(t *testing.T) {
|
||||||
|
cfg, err := LoadDefault("")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, time.Minute, cfg.Runner.FetchIntervalMax)
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoadDefault_UsesConfiguredWorkdirCleanupAge(t *testing.T) {
|
func TestLoadDefault_UsesConfiguredWorkdirCleanupAge(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
path := filepath.Join(dir, "config.yaml")
|
path := filepath.Join(dir, "config.yaml")
|
||||||
|
|||||||
Reference in New Issue
Block a user