test: add end-to-end Gitea compatibility suite (#1180)

Adds a real-Gitea compatibility suite against stable and nightly to catch runner and API drift before release.

The suite shares one Gitea and regular runner, using repository runners only for cache v1/v2 and ephemeral behavior. It covers registration, payload and log encoding, secrets, variables, services, artifacts, outputs, matrices, cache, dispatch, live logs, cancellation, and ephemeral teardown.

CI runs both images in parallel. Warm local timings:

| Image | Before | After |
| --- | ---: | ---: |
| `gitea/gitea:latest` | ~70s | 26.54s |
| `gitea/gitea:main-nightly` | ~54s | 22.53s |

The former serial matrix took about 2 minutes. Parallel suite execution is now bounded by the slower ~26.5-second variant.

Shared Renovate matcher: https://gitea.com/gitea/renovate-config/pulls/552

---------

Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/runner/pulls/1180
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni
2026-08-23 12:32:22 +00:00
committed by silverwind
parent b97c61aa14
commit 8260e2def2
25 changed files with 1370 additions and 120 deletions
+11 -57
View File
@@ -8,13 +8,10 @@ import (
"bytes"
"context"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"slices"
"strings"
"sync"
"testing"
"time"
@@ -191,9 +188,6 @@ func TestGraphEvent(t *testing.T) {
}
}
// these two build the same action Dockerfiles into one image tag, so they cannot overlap
var sharedImageWorkflows = []string{"local-action-dockerfile", "local-action-via-composite-dockerfile"}
// bounds concurrent plans: each job holds a network, and the daemon's address pool is finite
var planSlots = make(chan struct{}, 4)
@@ -244,11 +238,18 @@ func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config
plan, err := planner.PlanEvent(j.eventName)
assert.True(t, (err == nil) != (plan == nil), "PlanEvent should return either a plan or an error") //nolint:testifylint // pre-existing issue from nektos/act
if err == nil && plan != nil {
err = func() error {
usesDocker := false
for _, platform := range j.platforms {
if platform != "-self-hosted" {
usesDocker = true
break
}
}
if usesDocker && !common.Dryrun(ctx) {
planSlots <- struct{}{}
defer func() { <-planSlots }()
return runner.NewPlanExecutor(plan)(ctx)
}()
}
err = runner.NewPlanExecutor(plan)(ctx)
if j.errorMessage == "" {
assert.NoError(t, err, fullWorkflowPath) //nolint:testifylint // pre-existing issue from nektos/act
} else {
@@ -292,7 +293,6 @@ func TestRunEvent(t *testing.T) {
{workdir, "basic", "push", "", platforms, secrets},
{workdir, "fail", "push", "exit with `FAILURE`: 1", platforms, secrets},
{workdir, "checkout", "push", "", platforms, secrets},
{workdir, "job-container", "push", "", platforms, secrets},
{workdir, "job-container-invalid-credentials", "push", "failed to handle credentials: failed to interpolate container.credentials.password", platforms, secrets},
{workdir, "container-hostname", "push", "", platforms, secrets},
@@ -335,7 +335,6 @@ func TestRunEvent(t *testing.T) {
{workdir, "services-empty-image", "push", "", platforms, secrets},
}
var sharedImageMu sync.Mutex
for _, table := range tables {
t.Run(table.workflowPath, func(t *testing.T) {
if table.workflowPath == "container-volumes" {
@@ -343,13 +342,10 @@ func TestRunEvent(t *testing.T) {
requireLinuxDocker(t)
}
t.Parallel()
if slices.Contains(sharedImageWorkflows, table.workflowPath) {
sharedImageMu.Lock()
defer sharedImageMu.Unlock()
}
config := &Config{
Secrets: table.secrets,
Env: map[string]string{"GITHUB_REPOSITORY": t.Name()},
}
eventFile := filepath.Join(workdir, table.workflowPath, "event.json")
@@ -401,7 +397,6 @@ func TestRunEventHostEnvironment(t *testing.T) {
{workdir, "evalmatrix-merge-map", "push", "", platforms, secrets},
{workdir, "evalmatrix-merge-array", "push", "", platforms, secrets},
{workdir, "checkout", "push", "", platforms, secrets},
{workdir, "matrix", "push", "", platforms, secrets},
{workdir, "commands", "push", "", platforms, secrets},
{workdir, "defaults-run", "push", "", platforms, secrets},
@@ -498,47 +493,6 @@ func TestReusableWorkflowCaller(t *testing.T) {
table.runTest(context.Background(), t, &Config{Secrets: table.secrets})
}
type maskJobLoggerFactory struct {
Output bytes.Buffer
}
func (f *maskJobLoggerFactory) WithJobLogger() *log.Logger {
logger := log.New()
logger.SetOutput(io.MultiWriter(&f.Output, os.Stdout))
logger.SetLevel(log.DebugLevel)
return logger
}
func TestMaskValues(t *testing.T) {
t.Parallel()
assertNoSecret := func(text, secret string) { //nolint:unparam // pre-existing issue from nektos/act
found := strings.Contains(text, "composite secret")
if found {
fmt.Printf("\nFound Secret in the given text:\n%s\n", text) //nolint:forbidigo // pre-existing issue from nektos/act
}
assert.False(t, strings.Contains(text, "composite secret")) //nolint:testifylint // pre-existing issue from nektos/act
}
requireDocker(t)
log.SetLevel(log.DebugLevel)
tjfi := TestJobFileInfo{
workdir: workdir,
workflowPath: "mask-values",
eventName: "push",
errorMessage: "",
platforms: platforms,
}
logger := &maskJobLoggerFactory{}
tjfi.runTest(WithJobLoggerFactory(common.WithLogger(context.Background(), logger.WithJobLogger()), logger), t, &Config{})
output := logger.Output.String()
assertNoSecret(output, "secret value")
assertNoSecret(output, "YWJjCg==")
}
func TestRunEventSecrets(t *testing.T) {
requireDocker(t)
t.Parallel()
-8
View File
@@ -1,8 +0,0 @@
name: checkout
on: push
jobs:
checkout:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
-12
View File
@@ -1,12 +0,0 @@
name: composite
description: composite
runs:
using: composite
steps:
- run: echo "secret value"
shell: bash
- run: echo "::add-mask::$(echo "abc" | base64)"
shell: bash
- run: echo "abc" | base64
shell: bash
-12
View File
@@ -1,12 +0,0 @@
name: mask-values
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: echo "::add-mask::secret value"
- run: echo "secret value"
- uses: ./mask-values/composite
- run: echo "YWJjCg=="