fix(actions): fix nil errors and double list size

This commit is contained in:
TheFox0x7 2025-01-02 00:33:12 +01:00
parent a9a9a1633a
commit 81c1662340
No known key found for this signature in database
GPG Key ID: 6CA33903484AF7C2
5 changed files with 106 additions and 279 deletions

2
go.mod
View File

@ -102,6 +102,7 @@ require (
github.com/prometheus/client_golang v1.20.5
github.com/quasoft/websspi v1.1.2
github.com/redis/go-redis/v9 v9.7.0
github.com/rhysd/actionlint v1.7.3
github.com/robfig/cron/v3 v3.0.1
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/sassoftware/go-rpmutils v0.4.0
@ -271,7 +272,6 @@ require (
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rhysd/actionlint v1.7.3 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/xid v1.6.0 // indirect

View File

@ -9,10 +9,10 @@ import (
"gopkg.in/yaml.v3"
)
// NewInterpeter returns an interpeter used in the server,
// NewInterpreter returns an interpreter used in the server,
// need github, needs, strategy, matrix, inputs context only,
// see https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability
func NewInterpeter(
func NewInterpreter(
jobID string,
job *model.Job,
matrix map[string]any,
@ -78,7 +78,7 @@ func NewInterpeter(
return exprparser.NewInterpeter(ee, config)
}
// JobResult is the minimum requirement of job results for Interpeter
// JobResult is the minimum requirement of job results for Interpreter
type JobResult struct {
Needs []string
Result string

View File

@ -54,7 +54,7 @@ func Parse(content []byte, options ...ParseOption) ([]*SingleWorkflow, error) {
job.Name = id
}
job.Strategy.RawMatrix = encodeMatrix(matrix)
evaluator := NewExpressionEvaluator(NewInterpeter(id, origin.GetJob(id), matrix, pc.gitContext, results, pc.vars))
evaluator := NewExpressionEvaluator(NewInterpreter(id, origin.GetJob(id), matrix, pc.gitContext, results, pc.vars))
job.Name = nameWithMatrix(job.Name, matrix, evaluator)
runsOn := origin.GetJob(id).RunsOn()
for i, v := range runsOn {

View File

@ -204,16 +204,19 @@ func (evt *Event) Acts() map[string][]string {
// Helper to convert actionlint errors
func acErrToError(acErrs []*actionlint.Error) []error {
errs := make([]error, len(acErrs))
for _, err := range acErrs {
errs = append(errs, err)
for i, err := range acErrs {
errs[i] = err
}
return errs
}
func acStringToString(strs []*actionlint.String) []string {
if len(strs) == 0 {
return nil
}
strings := make([]string, len(strs))
for _, v := range strs {
strings = append(strings, v.Value)
for i, v := range strs {
strings[i] = v.Value
}
return strings
}
@ -246,29 +249,39 @@ func GetEventsFromContent(content []byte) ([]*Event, error) {
for _, acEvent := range wf.On {
event := &Event{
Name: acEvent.EventName(),
acts: map[string][]string{},
}
switch e := acEvent.(type) {
case *actionlint.ScheduledEvent:
schedules := make([]map[string]string, len(e.Cron))
for _, c := range e.Cron {
schedules = append(schedules, map[string]string{"cron": c.Value})
for i, c := range e.Cron {
schedules[i] = map[string]string{"cron": c.Value}
}
event.schedules = schedules
case *actionlint.WorkflowDispatchEvent:
inputs := make([]WorkflowDispatchInput, len(e.Inputs))
i := 0
for keyword, v := range e.Inputs {
inputs = append(inputs, WorkflowDispatchInput{
Name: keyword,
Required: v.Required.Value,
Description: v.Description.Value,
Default: v.Default.Value,
Options: acStringToString(v.Options),
Type: typeToString(v.Type),
})
wdi := WorkflowDispatchInput{
Name: keyword,
Options: acStringToString(v.Options),
Type: typeToString(v.Type),
}
if v.Required != nil {
wdi.Required = v.Required.Value
}
if v.Description != nil {
wdi.Description = v.Description.Value
}
if v.Default != nil {
wdi.Default = v.Default.Value
}
inputs[i] = wdi
i++
}
event.inputs = inputs
case *actionlint.WebhookEvent:
event.acts = map[string][]string{}
if e.Branches != nil {
event.acts[e.Branches.Name.Value] = acStringToString(e.Branches.Values)
}
@ -290,7 +303,6 @@ func GetEventsFromContent(content []byte) ([]*Event, error) {
if e.Types != nil {
event.acts["types"] = acStringToString(e.Types)
}
// if e.
}
events = append(events, event)
}

View File

@ -9,270 +9,85 @@ import (
"github.com/nektos/act/pkg/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
// func TestParseRawOn(t *testing.T) {
// kases := []struct {
// input string
// result []*Event
// }{
// {
// input: "on: issue_comment",
// result: []*Event{
// {
// Name: "issue_comment",
// },
// },
// },
// {
// input: "on:\n push",
// result: []*Event{
// {
// Name: "push",
// },
// },
// },
func TestGetEvents(t *testing.T) {
content := `
name: My Workflow
// {
// input: "on:\n - push\n - pull_request",
// result: []*Event{
// {
// Name: "push",
// },
// {
// Name: "pull_request",
// },
// },
// },
// {
// input: "on:\n push:\n branches:\n - master",
// result: []*Event{
// {
// Name: "push",
// acts: map[string][]string{
// "branches": {
// "master",
// },
// },
// },
// },
// },
// {
// input: "on:\n branch_protection_rule:\n types: [created, deleted]",
// result: []*Event{
// {
// Name: "branch_protection_rule",
// acts: map[string][]string{
// "types": {
// "created",
// "deleted",
// },
// },
// },
// },
// },
// {
// input: "on:\n project:\n types: [created, deleted]\n milestone:\n types: [opened, deleted]",
// result: []*Event{
// {
// Name: "project",
// acts: map[string][]string{
// "types": {
// "created",
// "deleted",
// },
// },
// },
// {
// Name: "milestone",
// acts: map[string][]string{
// "types": {
// "opened",
// "deleted",
// },
// },
// },
// },
// },
// {
// input: "on:\n pull_request:\n types:\n - opened\n branches:\n - 'releases/**'",
// result: []*Event{
// {
// Name: "pull_request",
// acts: map[string][]string{
// "types": {
// "opened",
// },
// "branches": {
// "releases/**",
// },
// },
// },
// },
// },
// {
// input: "on:\n push:\n branches:\n - main\n pull_request:\n types:\n - opened\n branches:\n - '**'",
// result: []*Event{
// {
// Name: "push",
// acts: map[string][]string{
// "branches": {
// "main",
// },
// },
// },
// {
// Name: "pull_request",
// acts: map[string][]string{
// "types": {
// "opened",
// },
// "branches": {
// "**",
// },
// },
// },
// },
// },
// {
// input: "on:\n push:\n branches:\n - 'main'\n - 'releases/**'",
// result: []*Event{
// {
// Name: "push",
// acts: map[string][]string{
// "branches": {
// "main",
// "releases/**",
// },
// },
// },
// },
// },
// {
// input: "on:\n push:\n tags:\n - v1.**",
// result: []*Event{
// {
// Name: "push",
// acts: map[string][]string{
// "tags": {
// "v1.**",
// },
// },
// },
// },
// },
// {
// input: "on: [pull_request, workflow_dispatch]",
// result: []*Event{
// {
// Name: "pull_request",
// },
// {
// Name: "workflow_dispatch",
// },
// },
// },
// {
// input: "on:\n schedule:\n - cron: '20 6 * * *'",
// result: []*Event{
// {
// Name: "schedule",
// schedules: []map[string]string{
// {
// "cron": "20 6 * * *",
// },
// },
// },
// },
// },
// {
// input: `on:
// workflow_dispatch:
// inputs:
// logLevel:
// description: 'Log level'
// required: true
// default: 'warning'
// type: choice
// options:
// - info
// - warning
// - debug
// tags:
// description: 'Test scenario tags'
// required: false
// type: boolean
// environment:
// description: 'Environment to run tests against'
// type: environment
// required: true
// push:
// `,
// 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",
// },
// },
// },
// }
// for _, kase := range kases {
// t.Run(kase.input, func(t *testing.T) {
// origin, err := model.ReadWorkflow(strings.NewReader(kase.input))
// assert.NoError(t, err)
on:
push:
branches: [main]
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
inputs:
my_variable1:
description: 'first variable'
required: true
type: string
my_variable2:
description: 'second variable'
required: false
type: number
default: 4
// events, err := ParseRawOn(&origin.RawOn)
// assert.NoError(t, err)
// assert.EqualValues(t, kase.result, events, fmt.Sprintf("%#v", events))
// })
// }
// }
jobs:
example:
runs-on: ubuntu-latest
steps:
- run: exit 0
`
expected := make([]*Event, 3)
expected[0] = &Event{acts: map[string][]string{"branches": {"main"}}, Name: "push"}
expected[1] = &Event{Name: "schedule", schedules: []map[string]string{{"cron": "0 0 * * *"}}}
expected[2] = &Event{
Name: "workflow_dispatch",
inputs: []WorkflowDispatchInput{
{
Name: "my_variable1",
Description: "first variable",
Required: true,
Type: "string",
},
{
Name: "my_variable2",
Type: "number",
Description: "second variable",
Default: "4",
},
},
}
actual, err := GetEventsFromContent([]byte(content))
require.NoError(t, err)
assert.Len(t, actual, 3)
assert.Equal(t, expected, actual)
//Toggler
}
// func TestSingleWorkflow_SetJob(t *testing.T) {
// t.Run("erase needs", func(t *testing.T) {
// content := ReadTestdata(t, "erase_needs.in.yaml")
// want := ReadTestdata(t, "erase_needs.out.yaml")
// swf, err := Parse(content)
// require.NoError(t, err)
// builder := &strings.Builder{}
// for _, v := range swf {
// id, job := v.Job()
// require.NoError(t, v.SetJob(id, job.EraseNeeds()))
func TestSingleWorkflow_SetJob(t *testing.T) {
t.Run("erase needs", func(t *testing.T) {
content := ReadTestdata(t, "erase_needs.in.yaml")
want := ReadTestdata(t, "erase_needs.out.yaml")
swf, err := Parse(content)
require.NoError(t, err)
builder := &strings.Builder{}
for _, v := range swf {
id, job := v.Job()
require.NoError(t, v.SetJob(id, job.EraseNeeds()))
// if builder.Len() > 0 {
// builder.WriteString("---\n")
// }
// encoder := yaml.NewEncoder(builder)
// encoder.SetIndent(2)
// require.NoError(t, encoder.Encode(v))
// }
// assert.Equal(t, string(want), builder.String())
// })
// }
if builder.Len() > 0 {
builder.WriteString("---\n")
}
encoder := yaml.NewEncoder(builder)
encoder.SetIndent(2)
require.NoError(t, encoder.Encode(v))
}
assert.Equal(t, string(want), builder.String())
})
}
func TestParseMappingNode(t *testing.T) {
tests := []struct {