2 Commits

Author SHA1 Message Date
silverwind
0f7efae806 chore(deps): update workflow action versions (#792)
## Summary
- `actions/checkout`: v4/v5 → v6
- `actions/setup-go`: v5 → v6
- `docker/build-push-action`: v5 → v6

All other actions (`goreleaser/goreleaser-action@v6`, `docker/setup-qemu-action@v3`, `docker/setup-buildx-action@v3`, `docker/login-action@v3`, `crazy-max/ghaction-import-gpg@v6`) were already at their latest major versions.

No breaking changes affect the current workflow configurations. The main changes in the updated actions are Node.js 24 runtime upgrades and minor feature additions.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/792
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-18 06:05:34 +00:00
silverwind
aab249000c chore(deps): update act to v0.261.8 (#791)
## Summary
- Update `gitea.com/gitea/act` from pseudo-version `v0.261.7-0.20251202193638-5417d3ac6742` to `v0.261.8`
- Update yaml import in `workflow.go` from `gopkg.in/yaml.v3` to `go.yaml.in/yaml/v4` to match act's yaml v4 migration
- Add tests to verify yaml/v4 upgrade works correctly

## Changes included in act v0.261.8
- Recover from panics in parallel executor workers ([gitea/act#153](https://gitea.com/gitea/act/pulls/153))
- Fix max-parallel support for matrix jobs ([gitea/act#150](https://gitea.com/gitea/act/pulls/150))
- Fix yaml with prefixed newline broken setjob + yaml v4 ([gitea/act#144](https://gitea.com/gitea/act/pulls/144))
- Fixed typo ([gitea/act#151](https://gitea.com/gitea/act/pulls/151))

## Tests added
- **`Test_generateWorkflow`**: 7 new cases (was 1), covering: no needs, needs as list, workflow env/triggers, container+services, matrix strategy, special YAML characters, and invalid YAML error handling
- **`Test_yamlV4NodeRoundTrip`**: Directly exercises `go.yaml.in/yaml/v4` — `yaml.Node` construction, marshal/unmarshal round-trip, and node kind constants

Fixes: https://gitea.com/gitea/act_runner/issues/371
Fixes: https://gitea.com/gitea/act_runner/issues/772
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/791
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-18 05:37:21 +00:00
7 changed files with 274 additions and 15 deletions

View File

@@ -17,10 +17,10 @@ jobs:
goreleaser: goreleaser:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-go@v5 - uses: actions/setup-go@v6
with: with:
go-version-file: "go.mod" go-version-file: "go.mod"
- name: goreleaser - name: goreleaser
@@ -52,7 +52,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 # all history for all branches and tags fetch-depth: 0 # all history for all branches and tags

View File

@@ -9,10 +9,10 @@ jobs:
goreleaser: goreleaser:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: with:
fetch-depth: 0 # all history for all branches and tags fetch-depth: 0 # all history for all branches and tags
- uses: actions/setup-go@v5 - uses: actions/setup-go@v6
with: with:
go-version-file: "go.mod" go-version-file: "go.mod"
- name: Import GPG key - name: Import GPG key
@@ -46,7 +46,7 @@ jobs:
DOCKER_LATEST: latest DOCKER_LATEST: latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
fetch-depth: 0 # all history for all branches and tags fetch-depth: 0 # all history for all branches and tags
@@ -69,7 +69,7 @@ jobs:
echo REPO_VERSION=${GITHUB_REF_NAME#v} >> $GITHUB_OUTPUT echo REPO_VERSION=${GITHUB_REF_NAME#v} >> $GITHUB_OUTPUT
- name: Build and push - name: Build and push
uses: docker/build-push-action@v5 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile
@@ -83,7 +83,7 @@ jobs:
${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }} ${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}
- name: Build and push dind - name: Build and push dind
uses: docker/build-push-action@v5 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile
@@ -97,7 +97,7 @@ jobs:
${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}-dind ${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}-dind
- name: Build and push dind-rootless - name: Build and push dind-rootless
uses: docker/build-push-action@v5 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile

View File

@@ -8,8 +8,8 @@ jobs:
name: check and test name: check and test
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-go@v5 - uses: actions/setup-go@v6
with: with:
go-version-file: 'go.mod' go-version-file: 'go.mod'
- name: vet checks - name: vet checks

4
go.mod
View File

@@ -21,6 +21,8 @@ require (
gotest.tools/v3 v3.5.1 gotest.tools/v3 v3.5.1
) )
require go.yaml.in/yaml/v4 v4.0.0-rc.2
require ( require (
cyphar.com/go-pathrs v0.2.1 // indirect cyphar.com/go-pathrs v0.2.1 // indirect
dario.cat/mergo v1.0.0 // indirect dario.cat/mergo v1.0.0 // indirect
@@ -100,7 +102,7 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
) )
replace github.com/nektos/act => gitea.com/gitea/act v0.261.7-0.20251202193638-5417d3ac6742 replace github.com/nektos/act => gitea.com/gitea/act v0.261.8
replace github.com/go-git/go-git/v5 => github.com/go-git/go-git/v5 v5.16.2 replace github.com/go-git/go-git/v5 => github.com/go-git/go-git/v5 v5.16.2

6
go.sum
View File

@@ -8,8 +8,8 @@ cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8=
cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
gitea.com/gitea/act v0.261.7-0.20251202193638-5417d3ac6742 h1:ulcquQluJbmNASkh6ina70LvcHEa9eWYfQ+DeAZ0VEE= gitea.com/gitea/act v0.261.8 h1:rUWB5GOZOubfe2VteKb7XP3HRIbcW3UUmfh7bVAgQcA=
gitea.com/gitea/act v0.261.7-0.20251202193638-5417d3ac6742/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok= gitea.com/gitea/act v0.261.8/go.mod h1:lTp4136rwbZiZS3ZVQeHCvd4qRAZ7LYeiRBqOSdMY/4=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
@@ -227,6 +227,8 @@ go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.yaml.in/yaml/v4 v4.0.0-rc.2 h1:/FrI8D64VSr4HtGIlUtlFMGsm7H7pWTbj6vOLVZcA6s=
go.yaml.in/yaml/v4 v4.0.0-rc.2/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=

View File

@@ -11,7 +11,7 @@ import (
runnerv1 "code.gitea.io/actions-proto-go/runner/v1" runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
"github.com/nektos/act/pkg/model" "github.com/nektos/act/pkg/model"
"gopkg.in/yaml.v3" "go.yaml.in/yaml/v4"
) )
func generateWorkflow(task *runnerv1.Task) (*model.Workflow, string, error) { func generateWorkflow(task *runnerv1.Task) (*model.Workflow, string, error) {

View File

@@ -9,6 +9,7 @@ import (
runnerv1 "code.gitea.io/actions-proto-go/runner/v1" runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
"github.com/nektos/act/pkg/model" "github.com/nektos/act/pkg/model"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.yaml.in/yaml/v4"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
) )
@@ -62,13 +63,267 @@ jobs:
want1: "job9", want1: "job9",
wantErr: false, wantErr: false,
}, },
{
name: "no needs",
args: args{
task: &runnerv1.Task{
WorkflowPayload: []byte(`
name: Simple workflow
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: echo "hello"
`),
Needs: map[string]*runnerv1.TaskNeed{},
},
},
assert: func(t *testing.T, wf *model.Workflow) {
job := wf.GetJob("test")
assert.DeepEqual(t, job.Needs(), []string{})
assert.Equal(t, len(job.Steps), 2)
},
want1: "test",
wantErr: false,
},
{
name: "needs list",
args: args{
task: &runnerv1.Task{
WorkflowPayload: []byte(`
name: Workflow with list needs
on: push
jobs:
deploy:
needs: [build, test, lint]
runs-on: ubuntu-latest
steps:
- run: echo "deploying"
`),
Needs: map[string]*runnerv1.TaskNeed{
"build": {
Outputs: map[string]string{},
Result: runnerv1.Result_RESULT_SUCCESS,
},
"test": {
Outputs: map[string]string{
"coverage": "80%",
},
Result: runnerv1.Result_RESULT_SUCCESS,
},
"lint": {
Outputs: map[string]string{},
Result: runnerv1.Result_RESULT_FAILURE,
},
},
},
},
assert: func(t *testing.T, wf *model.Workflow) {
job := wf.GetJob("deploy")
needs := job.Needs()
assert.DeepEqual(t, needs, []string{"build", "lint", "test"})
assert.Equal(t, wf.Jobs["test"].Outputs["coverage"], "80%")
assert.Equal(t, wf.Jobs["lint"].Result, "failure")
},
want1: "deploy",
wantErr: false,
},
{
name: "workflow env and defaults",
args: args{
task: &runnerv1.Task{
WorkflowPayload: []byte(`
name: Complex workflow
on:
push:
branches: [main, develop]
pull_request:
types: [opened, synchronize]
env:
NODE_ENV: production
CI: "true"
jobs:
build:
runs-on: ubuntu-latest
env:
BUILD_TYPE: release
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: "18"
- run: npm ci
- run: npm run build
`),
Needs: map[string]*runnerv1.TaskNeed{},
},
},
assert: func(t *testing.T, wf *model.Workflow) {
assert.Equal(t, wf.Name, "Complex workflow")
assert.Equal(t, wf.Env["NODE_ENV"], "production")
assert.Equal(t, wf.Env["CI"], "true")
job := wf.GetJob("build")
assert.Equal(t, len(job.Steps), 4)
},
want1: "build",
wantErr: false,
},
{
name: "job with container and services",
args: args{
task: &runnerv1.Task{
WorkflowPayload: []byte(`
name: Integration tests
on: push
jobs:
integration:
runs-on: ubuntu-latest
container:
image: node:18
services:
postgres:
image: postgres:15
steps:
- uses: actions/checkout@v3
- run: npm test
`),
Needs: map[string]*runnerv1.TaskNeed{},
},
},
assert: func(t *testing.T, wf *model.Workflow) {
job := wf.GetJob("integration")
container := job.Container()
assert.Equal(t, container.Image, "node:18")
assert.Equal(t, job.Services["postgres"].Image, "postgres:15")
},
want1: "integration",
wantErr: false,
},
{
name: "job with matrix strategy",
args: args{
task: &runnerv1.Task{
WorkflowPayload: []byte(`
name: Matrix build
on: push
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: ["1.21", "1.22"]
steps:
- uses: actions/checkout@v3
- run: go test ./...
`),
Needs: map[string]*runnerv1.TaskNeed{},
},
},
assert: func(t *testing.T, wf *model.Workflow) {
job := wf.GetJob("test")
matrixes, err := job.GetMatrixes()
require.NoError(t, err)
assert.Equal(t, len(matrixes), 2)
},
want1: "test",
wantErr: false,
},
{
name: "special yaml characters in values",
args: args{
task: &runnerv1.Task{
WorkflowPayload: []byte("name: \"Special: characters & test\"\non: push\n\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: 'echo \"hello: world\"'\n - run: 'echo \"quotes & ampersands\"'\n - run: |\n echo \"multiline\"\n echo \"script\"\n"),
Needs: map[string]*runnerv1.TaskNeed{},
},
},
assert: func(t *testing.T, wf *model.Workflow) {
assert.Equal(t, wf.Name, "Special: characters & test")
job := wf.GetJob("test")
assert.Equal(t, len(job.Steps), 3)
},
want1: "test",
wantErr: false,
},
{
name: "invalid yaml",
args: args{
task: &runnerv1.Task{
WorkflowPayload: []byte(`
name: Bad workflow
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo "ok"
bad-indent: true
`),
Needs: map[string]*runnerv1.TaskNeed{},
},
},
assert: nil,
want1: "",
wantErr: true,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, got1, err := generateWorkflow(tt.args.task) got, got1, err := generateWorkflow(tt.args.task)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err) require.NoError(t, err)
tt.assert(t, got) tt.assert(t, got)
assert.Equal(t, got1, tt.want1) assert.Equal(t, got1, tt.want1)
}) })
} }
} }
func Test_yamlV4NodeRoundTrip(t *testing.T) {
t.Run("marshal sequence node", func(t *testing.T) {
node := yaml.Node{
Kind: yaml.SequenceNode,
Content: []*yaml.Node{
{Kind: yaml.ScalarNode, Value: "a"},
{Kind: yaml.ScalarNode, Value: "b"},
{Kind: yaml.ScalarNode, Value: "c"},
},
}
out, err := yaml.Marshal(&node)
require.NoError(t, err)
assert.Equal(t, string(out), "- a\n- b\n- c\n")
})
t.Run("unmarshal and re-marshal workflow", func(t *testing.T) {
input := []byte("name: test\non: push\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - run: echo hello\n")
var wf map[string]interface{}
err := yaml.Unmarshal(input, &wf)
require.NoError(t, err)
assert.Equal(t, wf["name"], "test")
out, err := yaml.Marshal(wf)
require.NoError(t, err)
var wf2 map[string]interface{}
err = yaml.Unmarshal(out, &wf2)
require.NoError(t, err)
assert.Equal(t, wf2["name"], "test")
})
t.Run("node kind constants", func(t *testing.T) {
// Verify yaml/v4 node kind constants are usable (same API as v3)
require.NotEqual(t, yaml.ScalarNode, yaml.SequenceNode)
require.NotEqual(t, yaml.SequenceNode, yaml.MappingNode)
})
}