feat(actions): update actionslib, support self:, misc fixes (#39358)

Updates actionslib to https://gitea.com/gitea/actionslib/releases/tag/v1.2.1, moves workflow
parsing into it and aligns behaviour with GitHub.

1. `uses:` supports `self:` (Gitea-only feature) and `$/` paths.
1. `strategy`, `matrix`, `max-parallel` and `fail-fast` accept
expressions, including over `needs`. A job whose `name`, `runs-on` or
`continue-on-error` reads `needs` is resolved once they finish.
1. A job `if:` may only read `github`, `needs`, `vars` and `inputs` and
is decided before the matrix, as on github.com.
1. Matrix `fail-fast` cancels the other combinations, and `always()`
jobs keep running when a run is cancelled.
1. Invalid workflow files, including a malformed `on:` and unknown or
cyclic `needs`, show up on push as failed runs with the error.
1. A job whose `if:` or `concurrency:` fails to evaluate is skipped or
failed with the error, instead of staying blocked.
1. Reusable workflows: a missing and an unreadable repository fail
alike, public callers cannot use private workflows, nested jobs cannot
exceed the caller's token permissions.
1. Runner labels match case-insensitively, and `runs-on` accepts an
array from an expression.

Runner PR: https://gitea.com/gitea/runner/pulls/1247
Docs PR: https://gitea.com/gitea/docs/pulls/553
Fixes: https://github.com/go-gitea/gitea/issues/38990
Fixes: https://github.com/go-gitea/gitea/issues/39382
Fixes: https://github.com/go-gitea/gitea/issues/32364
Fixes: https://github.com/go-gitea/gitea/issues/36077
Fixes: https://github.com/go-gitea/gitea/issues/23277
Fixes: https://github.com/go-gitea/gitea/issues/29020
Co-authored-by: Claude (Opus 5) <noreply@anthropic.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
This commit is contained in:
silverwind
2026-09-25 00:06:42 +02:00
committed by GitHub
parent 64f31d9b70
commit f757631a47
59 changed files with 1509 additions and 2223 deletions
+33 -72
View File
@@ -17,9 +17,18 @@ import (
func TestParseRawOn(t *testing.T) {
kases := []struct {
input string
result []*Event
input string
result []*Event
wantErr bool
}{
{input: "on:\n push:\n branches:\n a: b", wantErr: true},
{input: "on: [push, {pull_request: null}]", wantErr: true},
{input: "on:", wantErr: true},
{input: "on: 42", wantErr: true},
{input: "jobs: {}", wantErr: true},
{input: "on:\n schedule: []", wantErr: true},
{input: "on:\n schedule:\n - {}", wantErr: true},
{input: "on:\n schedule:\n - cron: nope", wantErr: true},
{
input: "on: issue_comment",
result: []*Event{
@@ -190,13 +199,14 @@ func TestParseRawOn(t *testing.T) {
},
},
{
input: "on:\n schedule:\n - cron: '20 6 * * *'",
input: "on:\n schedule:\n - cron: '20 6 * * *'\n timezone: UTC",
result: []*Event{
{
Name: "schedule",
schedules: []map[string]string{
{
"cron": "20 6 * * *",
"cron": "20 6 * * *",
"timezone": "UTC",
},
},
},
@@ -228,28 +238,6 @@ func TestParseRawOn(t *testing.T) {
result: []*Event{
{
Name: "workflow_dispatch",
inputs: []WorkflowDispatchInput{
{
Name: "logLevel",
Description: "Log level",
Required: true,
Default: "warning",
Type: "choice",
Options: []string{"info", "warning", "debug"},
},
{
Name: "tags",
Description: "Test scenario tags",
Required: false,
Type: "boolean",
},
{
Name: "environment",
Description: "Environment to run tests against",
Type: "environment",
Required: true,
},
},
},
{
Name: "push",
@@ -310,6 +298,10 @@ func TestParseRawOn(t *testing.T) {
assert.NoError(t, err)
events, err := ParseRawOn(&origin.RawOn)
if kase.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, kase.result, events, events)
})
@@ -467,51 +459,6 @@ func TestParseMappingNode(t *testing.T) {
}
}
func TestEvaluateJobIfExpressionMatrix(t *testing.T) {
ifExprs := []string{
`${{ contains(fromJSON('["linux","windows"]'), matrix.target) }}`,
`${{ contains('["linux","windows"]', matrix.target) }}`,
}
want := map[string]bool{
"build (linux)": true,
"build (windows)": true,
"build (macos)": false,
}
for _, ifExpr := range ifExprs {
t.Run(ifExpr, func(t *testing.T) {
content := fmt.Sprintf(`
name: test
on: push
jobs:
build:
runs-on: ubuntu-latest
if: %s
strategy:
fail-fast: false
matrix:
target: [linux, windows, macos]
steps:
- run: echo ${{ matrix.target }}
`, ifExpr)
swfs, err := Parse([]byte(content))
require.NoError(t, err)
require.Len(t, swfs, 3)
got := make(map[string]bool, len(swfs))
for _, swf := range swfs {
id, job := swf.Job()
shouldRun, err := EvaluateJobIfExpression(id, job, map[string]any{}, map[string]*JobResult{id: {}}, nil, nil, false)
require.NoError(t, err)
got[job.Name] = shouldRun
}
assert.Equal(t, want, got)
})
}
}
func TestEvaluateJobIfExpression(t *testing.T) {
kases := []struct {
name string
@@ -567,9 +514,23 @@ jobs:
"job1": {Result: kase.needResult},
"job2": {Needs: []string{"job1"}},
}
got, err := EvaluateJobIfExpression("job2", job2, map[string]any{}, results, nil, nil, false)
got, err := EvaluateJobIfExpression("job2", job2, map[string]any{}, results, nil, nil)
require.NoError(t, err)
assert.Equal(t, kase.expected, got)
})
}
t.Run("stored unavailable context", func(t *testing.T) {
for condition, wantErr := range map[string]string{
"matrix.os == 'a'": "Unrecognized named-value: 'matrix'",
"${{ strategy.fail-fast }}": "Unrecognized named-value: 'strategy'",
"secrets.TOKEN != ''": "Unrecognized named-value: 'secrets'",
"${{ matrix.os == }}": "Unexpected end of expression",
} {
_, job, err := ParseRawSingleWorkflow(fmt.Appendf(nil, "jobs: {job2: {if: %q, strategy: {matrix: {os: [a]}}}}", condition))
require.NoError(t, err)
_, err = EvaluateJobIfExpression("job2", job, map[string]any{}, map[string]*JobResult{"job2": {}}, nil, nil)
assert.ErrorContains(t, err, wantErr, condition)
}
})
}