mirror of
https://gitea.com/gitea/act
synced 2026-05-01 01:27:48 +02:00
Remove jobparser because of moving to Gitea main project (#155)
Reviewed-on: https://gitea.com/gitea/act/pulls/155 Reviewed-by: silverwind <silverwind@noreply.gitea.com> Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
This commit is contained in:
@@ -16,6 +16,3 @@ jobs:
|
|||||||
run: go vet -v ./...
|
run: go vet -v ./...
|
||||||
- name: build
|
- name: build
|
||||||
run: go build -v ./...
|
run: go build -v ./...
|
||||||
- name: test
|
|
||||||
run: | # Test only the new packages in this fork. Add more packages as needed.
|
|
||||||
go test -v ./pkg/jobparser
|
|
||||||
|
|||||||
@@ -1,185 +0,0 @@
|
|||||||
package jobparser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/nektos/act/pkg/exprparser"
|
|
||||||
"go.yaml.in/yaml/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ExpressionEvaluator is copied from runner.expressionEvaluator,
|
|
||||||
// to avoid unnecessary dependencies
|
|
||||||
type ExpressionEvaluator struct {
|
|
||||||
interpreter exprparser.Interpreter
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewExpressionEvaluator(interpreter exprparser.Interpreter) *ExpressionEvaluator {
|
|
||||||
return &ExpressionEvaluator{interpreter: interpreter}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ee ExpressionEvaluator) evaluate(in string, defaultStatusCheck exprparser.DefaultStatusCheck) (interface{}, error) {
|
|
||||||
evaluated, err := ee.interpreter.Evaluate(in, defaultStatusCheck)
|
|
||||||
|
|
||||||
return evaluated, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ee ExpressionEvaluator) evaluateScalarYamlNode(node *yaml.Node) error {
|
|
||||||
var in string
|
|
||||||
if err := node.Decode(&in); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !strings.Contains(in, "${{") || !strings.Contains(in, "}}") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
expr, _ := rewriteSubExpression(in, false)
|
|
||||||
res, err := ee.evaluate(expr, exprparser.DefaultStatusCheckNone)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return node.Encode(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ee ExpressionEvaluator) evaluateMappingYamlNode(node *yaml.Node) error {
|
|
||||||
// GitHub has this undocumented feature to merge maps, called insert directive
|
|
||||||
insertDirective := regexp.MustCompile(`\${{\s*insert\s*}}`)
|
|
||||||
for i := 0; i < len(node.Content)/2; {
|
|
||||||
k := node.Content[i*2]
|
|
||||||
v := node.Content[i*2+1]
|
|
||||||
if err := ee.EvaluateYamlNode(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var sk string
|
|
||||||
// Merge the nested map of the insert directive
|
|
||||||
if k.Decode(&sk) == nil && insertDirective.MatchString(sk) {
|
|
||||||
node.Content = append(append(node.Content[:i*2], v.Content...), node.Content[(i+1)*2:]...)
|
|
||||||
i += len(v.Content) / 2
|
|
||||||
} else {
|
|
||||||
if err := ee.EvaluateYamlNode(k); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ee ExpressionEvaluator) evaluateSequenceYamlNode(node *yaml.Node) error {
|
|
||||||
for i := 0; i < len(node.Content); {
|
|
||||||
v := node.Content[i]
|
|
||||||
// Preserve nested sequences
|
|
||||||
wasseq := v.Kind == yaml.SequenceNode
|
|
||||||
if err := ee.EvaluateYamlNode(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// GitHub has this undocumented feature to merge sequences / arrays
|
|
||||||
// We have a nested sequence via evaluation, merge the arrays
|
|
||||||
if v.Kind == yaml.SequenceNode && !wasseq {
|
|
||||||
node.Content = append(append(node.Content[:i], v.Content...), node.Content[i+1:]...)
|
|
||||||
i += len(v.Content)
|
|
||||||
} else {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ee ExpressionEvaluator) EvaluateYamlNode(node *yaml.Node) error {
|
|
||||||
switch node.Kind {
|
|
||||||
case yaml.ScalarNode:
|
|
||||||
return ee.evaluateScalarYamlNode(node)
|
|
||||||
case yaml.MappingNode:
|
|
||||||
return ee.evaluateMappingYamlNode(node)
|
|
||||||
case yaml.SequenceNode:
|
|
||||||
return ee.evaluateSequenceYamlNode(node)
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ee ExpressionEvaluator) Interpolate(in string) string {
|
|
||||||
if !strings.Contains(in, "${{") || !strings.Contains(in, "}}") {
|
|
||||||
return in
|
|
||||||
}
|
|
||||||
|
|
||||||
expr, _ := rewriteSubExpression(in, true)
|
|
||||||
evaluated, err := ee.evaluate(expr, exprparser.DefaultStatusCheckNone)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
value, ok := evaluated.(string)
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Sprintf("Expression %s did not evaluate to a string", expr))
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func escapeFormatString(in string) string {
|
|
||||||
return strings.ReplaceAll(strings.ReplaceAll(in, "{", "{{"), "}", "}}")
|
|
||||||
}
|
|
||||||
|
|
||||||
func rewriteSubExpression(in string, forceFormat bool) (string, error) {
|
|
||||||
if !strings.Contains(in, "${{") || !strings.Contains(in, "}}") {
|
|
||||||
return in, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
strPattern := regexp.MustCompile("(?:''|[^'])*'")
|
|
||||||
pos := 0
|
|
||||||
exprStart := -1
|
|
||||||
strStart := -1
|
|
||||||
var results []string
|
|
||||||
formatOut := ""
|
|
||||||
for pos < len(in) {
|
|
||||||
if strStart > -1 {
|
|
||||||
matches := strPattern.FindStringIndex(in[pos:])
|
|
||||||
if matches == nil {
|
|
||||||
panic("unclosed string.")
|
|
||||||
}
|
|
||||||
|
|
||||||
strStart = -1
|
|
||||||
pos += matches[1]
|
|
||||||
} else if exprStart > -1 {
|
|
||||||
exprEnd := strings.Index(in[pos:], "}}")
|
|
||||||
strStart = strings.Index(in[pos:], "'")
|
|
||||||
|
|
||||||
if exprEnd > -1 && strStart > -1 {
|
|
||||||
if exprEnd < strStart {
|
|
||||||
strStart = -1
|
|
||||||
} else {
|
|
||||||
exprEnd = -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if exprEnd > -1 {
|
|
||||||
formatOut += fmt.Sprintf("{%d}", len(results))
|
|
||||||
results = append(results, strings.TrimSpace(in[exprStart:pos+exprEnd]))
|
|
||||||
pos += exprEnd + 2
|
|
||||||
exprStart = -1
|
|
||||||
} else if strStart > -1 {
|
|
||||||
pos += strStart + 1
|
|
||||||
} else {
|
|
||||||
panic("unclosed expression.")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
exprStart = strings.Index(in[pos:], "${{")
|
|
||||||
if exprStart != -1 {
|
|
||||||
formatOut += escapeFormatString(in[pos : pos+exprStart])
|
|
||||||
exprStart = pos + exprStart + 3
|
|
||||||
pos = exprStart
|
|
||||||
} else {
|
|
||||||
formatOut += escapeFormatString(in[pos:])
|
|
||||||
pos = len(in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(results) == 1 && formatOut == "{0}" && !forceFormat {
|
|
||||||
return in, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
out := fmt.Sprintf("format('%s', %s)", strings.ReplaceAll(formatOut, "'", "''"), strings.Join(results, ", "))
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
package jobparser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/nektos/act/pkg/exprparser"
|
|
||||||
"github.com/nektos/act/pkg/model"
|
|
||||||
"go.yaml.in/yaml/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewInterpeter returns an interpeter 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(
|
|
||||||
jobID string,
|
|
||||||
job *model.Job,
|
|
||||||
matrix map[string]interface{},
|
|
||||||
gitCtx *model.GithubContext,
|
|
||||||
results map[string]*JobResult,
|
|
||||||
vars map[string]string,
|
|
||||||
inputs map[string]interface{},
|
|
||||||
) exprparser.Interpreter {
|
|
||||||
strategy := make(map[string]interface{})
|
|
||||||
if job.Strategy != nil {
|
|
||||||
strategy["fail-fast"] = job.Strategy.FailFast
|
|
||||||
strategy["max-parallel"] = job.Strategy.MaxParallel
|
|
||||||
}
|
|
||||||
|
|
||||||
run := &model.Run{
|
|
||||||
Workflow: &model.Workflow{
|
|
||||||
Jobs: map[string]*model.Job{},
|
|
||||||
},
|
|
||||||
JobID: jobID,
|
|
||||||
}
|
|
||||||
for id, result := range results {
|
|
||||||
need := yaml.Node{}
|
|
||||||
_ = need.Encode(result.Needs)
|
|
||||||
run.Workflow.Jobs[id] = &model.Job{
|
|
||||||
RawNeeds: need,
|
|
||||||
Result: result.Result,
|
|
||||||
Outputs: result.Outputs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jobs := run.Workflow.Jobs
|
|
||||||
jobNeeds := run.Job().Needs()
|
|
||||||
|
|
||||||
using := map[string]exprparser.Needs{}
|
|
||||||
for _, need := range jobNeeds {
|
|
||||||
if v, ok := jobs[need]; ok {
|
|
||||||
using[need] = exprparser.Needs{
|
|
||||||
Outputs: v.Outputs,
|
|
||||||
Result: v.Result,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ee := &exprparser.EvaluationEnvironment{
|
|
||||||
Github: gitCtx,
|
|
||||||
Env: nil, // no need
|
|
||||||
Job: nil, // no need
|
|
||||||
Steps: nil, // no need
|
|
||||||
Runner: nil, // no need
|
|
||||||
Secrets: nil, // no need
|
|
||||||
Strategy: strategy,
|
|
||||||
Matrix: matrix,
|
|
||||||
Needs: using,
|
|
||||||
Inputs: inputs,
|
|
||||||
Vars: vars,
|
|
||||||
}
|
|
||||||
|
|
||||||
config := exprparser.Config{
|
|
||||||
Run: run,
|
|
||||||
WorkingDir: "", // WorkingDir is used for the function hashFiles, but it's not needed in the server
|
|
||||||
Context: "job",
|
|
||||||
}
|
|
||||||
|
|
||||||
return exprparser.NewInterpeter(ee, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
// JobResult is the minimum requirement of job results for Interpeter
|
|
||||||
type JobResult struct {
|
|
||||||
Needs []string
|
|
||||||
Result string
|
|
||||||
Outputs map[string]string
|
|
||||||
}
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
package jobparser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"go.yaml.in/yaml/v4"
|
|
||||||
|
|
||||||
"github.com/nektos/act/pkg/exprparser"
|
|
||||||
"github.com/nektos/act/pkg/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Parse(content []byte, options ...ParseOption) ([]*SingleWorkflow, error) {
|
|
||||||
origin, err := model.ReadWorkflow(bytes.NewReader(content))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("model.ReadWorkflow: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
workflow := &SingleWorkflow{}
|
|
||||||
if err := yaml.Unmarshal(content, workflow); err != nil {
|
|
||||||
return nil, fmt.Errorf("yaml.Unmarshal: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pc := &parseContext{}
|
|
||||||
for _, o := range options {
|
|
||||||
o(pc)
|
|
||||||
}
|
|
||||||
results := map[string]*JobResult{}
|
|
||||||
for id, job := range origin.Jobs {
|
|
||||||
results[id] = &JobResult{
|
|
||||||
Needs: job.Needs(),
|
|
||||||
Result: pc.jobResults[id],
|
|
||||||
Outputs: nil, // not supported yet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret []*SingleWorkflow
|
|
||||||
ids, jobs, err := workflow.jobs()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid jobs: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
evaluator := NewExpressionEvaluator(exprparser.NewInterpeter(&exprparser.EvaluationEnvironment{Github: pc.gitContext, Vars: pc.vars, Inputs: pc.inputs}, exprparser.Config{}))
|
|
||||||
workflow.RunName = evaluator.Interpolate(workflow.RunName)
|
|
||||||
|
|
||||||
for i, id := range ids {
|
|
||||||
job := jobs[i]
|
|
||||||
matricxes, err := getMatrixes(origin.GetJob(id))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("getMatrixes: %w", err)
|
|
||||||
}
|
|
||||||
for _, matrix := range matricxes {
|
|
||||||
job := job.Clone()
|
|
||||||
if job.Name == "" {
|
|
||||||
job.Name = id
|
|
||||||
}
|
|
||||||
job.Strategy.RawMatrix = encodeMatrix(matrix)
|
|
||||||
evaluator := NewExpressionEvaluator(NewInterpeter(id, origin.GetJob(id), matrix, pc.gitContext, results, pc.vars, pc.inputs))
|
|
||||||
job.Name = nameWithMatrix(job.Name, matrix, evaluator)
|
|
||||||
runsOn := origin.GetJob(id).RunsOn()
|
|
||||||
for i, v := range runsOn {
|
|
||||||
runsOn[i] = evaluator.Interpolate(v)
|
|
||||||
}
|
|
||||||
job.RawRunsOn = encodeRunsOn(runsOn)
|
|
||||||
swf := &SingleWorkflow{
|
|
||||||
Name: workflow.Name,
|
|
||||||
RawOn: workflow.RawOn,
|
|
||||||
Env: workflow.Env,
|
|
||||||
Defaults: workflow.Defaults,
|
|
||||||
RawPermissions: workflow.RawPermissions,
|
|
||||||
RunName: workflow.RunName,
|
|
||||||
}
|
|
||||||
if err := swf.SetJob(id, job); err != nil {
|
|
||||||
return nil, fmt.Errorf("SetJob: %w", err)
|
|
||||||
}
|
|
||||||
ret = append(ret, swf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithJobResults(results map[string]string) ParseOption {
|
|
||||||
return func(c *parseContext) {
|
|
||||||
c.jobResults = results
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithGitContext(context *model.GithubContext) ParseOption {
|
|
||||||
return func(c *parseContext) {
|
|
||||||
c.gitContext = context
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithVars(vars map[string]string) ParseOption {
|
|
||||||
return func(c *parseContext) {
|
|
||||||
c.vars = vars
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithInputs(inputs map[string]any) ParseOption {
|
|
||||||
return func(c *parseContext) {
|
|
||||||
c.inputs = inputs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type parseContext struct {
|
|
||||||
jobResults map[string]string
|
|
||||||
gitContext *model.GithubContext
|
|
||||||
vars map[string]string
|
|
||||||
inputs map[string]any
|
|
||||||
}
|
|
||||||
|
|
||||||
type ParseOption func(c *parseContext)
|
|
||||||
|
|
||||||
func getMatrixes(job *model.Job) ([]map[string]interface{}, error) {
|
|
||||||
ret, err := job.GetMatrixes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("GetMatrixes: %w", err)
|
|
||||||
}
|
|
||||||
sort.Slice(ret, func(i, j int) bool {
|
|
||||||
return matrixName(ret[i]) < matrixName(ret[j])
|
|
||||||
})
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeMatrix(matrix map[string]interface{}) yaml.Node {
|
|
||||||
if len(matrix) == 0 {
|
|
||||||
return yaml.Node{}
|
|
||||||
}
|
|
||||||
value := map[string][]interface{}{}
|
|
||||||
for k, v := range matrix {
|
|
||||||
value[k] = []interface{}{v}
|
|
||||||
}
|
|
||||||
node := yaml.Node{}
|
|
||||||
_ = node.Encode(value)
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeRunsOn(runsOn []string) yaml.Node {
|
|
||||||
node := yaml.Node{}
|
|
||||||
if len(runsOn) == 1 {
|
|
||||||
_ = node.Encode(runsOn[0])
|
|
||||||
} else {
|
|
||||||
_ = node.Encode(runsOn)
|
|
||||||
}
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func nameWithMatrix(name string, m map[string]interface{}, evaluator *ExpressionEvaluator) string {
|
|
||||||
if len(m) == 0 {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(name, "${{") || !strings.Contains(name, "}}") {
|
|
||||||
return name + " " + matrixName(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
return evaluator.Interpolate(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func matrixName(m map[string]interface{}) string {
|
|
||||||
ks := make([]string, 0, len(m))
|
|
||||||
for k := range m {
|
|
||||||
ks = append(ks, k)
|
|
||||||
}
|
|
||||||
sort.Strings(ks)
|
|
||||||
vs := make([]string, 0, len(m))
|
|
||||||
for _, v := range ks {
|
|
||||||
vs = append(vs, fmt.Sprint(m[v]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("(%s)", strings.Join(vs, ", "))
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package jobparser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"go.yaml.in/yaml/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
options []ParseOption
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "multiple_jobs",
|
|
||||||
options: nil,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple_matrix",
|
|
||||||
options: nil,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "has_needs",
|
|
||||||
options: nil,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "has_with",
|
|
||||||
options: nil,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "has_secrets",
|
|
||||||
options: nil,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty_step",
|
|
||||||
options: nil,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "job_name_with_matrix",
|
|
||||||
options: nil,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "prefixed_newline",
|
|
||||||
options: nil,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
content := ReadTestdata(t, tt.name+".in.yaml")
|
|
||||||
want := ReadTestdata(t, tt.name+".out.yaml")
|
|
||||||
got, err := Parse(content, tt.options...)
|
|
||||||
if tt.wantErr {
|
|
||||||
require.Error(t, err)
|
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
builder := &strings.Builder{}
|
|
||||||
for _, v := range got {
|
|
||||||
if builder.Len() > 0 {
|
|
||||||
builder.WriteString("---\n")
|
|
||||||
}
|
|
||||||
encoder := yaml.NewEncoder(builder)
|
|
||||||
encoder.SetIndent(2)
|
|
||||||
require.NoError(t, encoder.Encode(v))
|
|
||||||
id, job := v.Job()
|
|
||||||
assert.NotEmpty(t, id)
|
|
||||||
assert.NotNil(t, job)
|
|
||||||
}
|
|
||||||
assert.Equal(t, string(want), builder.String())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,528 +0,0 @@
|
|||||||
package jobparser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/nektos/act/pkg/model"
|
|
||||||
"go.yaml.in/yaml/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SingleWorkflow is a workflow with single job and single matrix
|
|
||||||
type SingleWorkflow struct {
|
|
||||||
Name string `yaml:"name,omitempty"`
|
|
||||||
RawOn yaml.Node `yaml:"on,omitempty"`
|
|
||||||
Env map[string]string `yaml:"env,omitempty"`
|
|
||||||
RawJobs yaml.Node `yaml:"jobs,omitempty"`
|
|
||||||
Defaults Defaults `yaml:"defaults,omitempty"`
|
|
||||||
RawPermissions yaml.Node `yaml:"permissions,omitempty"`
|
|
||||||
RunName string `yaml:"run-name,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *SingleWorkflow) Job() (string, *Job) {
|
|
||||||
ids, jobs, _ := w.jobs()
|
|
||||||
if len(ids) >= 1 {
|
|
||||||
return ids[0], jobs[0]
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *SingleWorkflow) jobs() ([]string, []*Job, error) {
|
|
||||||
ids, jobs, err := parseMappingNode[*Job](&w.RawJobs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, job := range jobs {
|
|
||||||
steps := make([]*Step, 0, len(job.Steps))
|
|
||||||
for _, s := range job.Steps {
|
|
||||||
if s != nil {
|
|
||||||
steps = append(steps, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
job.Steps = steps
|
|
||||||
}
|
|
||||||
|
|
||||||
return ids, jobs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *SingleWorkflow) SetJob(id string, job *Job) error {
|
|
||||||
m := map[string]*Job{
|
|
||||||
id: job,
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
encoder := yaml.NewEncoder(&buf)
|
|
||||||
encoder.SetIndent(2)
|
|
||||||
encoder.Encode(m)
|
|
||||||
encoder.Close()
|
|
||||||
|
|
||||||
node := yaml.Node{}
|
|
||||||
if err := yaml.Unmarshal(buf.Bytes(), &node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(node.Content) != 1 || node.Content[0].Kind != yaml.MappingNode {
|
|
||||||
return fmt.Errorf("can not set job: %s", buf.String())
|
|
||||||
}
|
|
||||||
w.RawJobs = *node.Content[0]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *SingleWorkflow) Marshal() ([]byte, error) {
|
|
||||||
return yaml.Marshal(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Job struct {
|
|
||||||
Name string `yaml:"name,omitempty"`
|
|
||||||
RawNeeds yaml.Node `yaml:"needs,omitempty"`
|
|
||||||
RawRunsOn yaml.Node `yaml:"runs-on,omitempty"`
|
|
||||||
Env yaml.Node `yaml:"env,omitempty"`
|
|
||||||
If yaml.Node `yaml:"if,omitempty"`
|
|
||||||
Steps []*Step `yaml:"steps,omitempty"`
|
|
||||||
TimeoutMinutes string `yaml:"timeout-minutes,omitempty"`
|
|
||||||
Services map[string]*ContainerSpec `yaml:"services,omitempty"`
|
|
||||||
Strategy Strategy `yaml:"strategy,omitempty"`
|
|
||||||
RawContainer yaml.Node `yaml:"container,omitempty"`
|
|
||||||
Defaults Defaults `yaml:"defaults,omitempty"`
|
|
||||||
Outputs map[string]string `yaml:"outputs,omitempty"`
|
|
||||||
Uses string `yaml:"uses,omitempty"`
|
|
||||||
With map[string]interface{} `yaml:"with,omitempty"`
|
|
||||||
RawSecrets yaml.Node `yaml:"secrets,omitempty"`
|
|
||||||
RawConcurrency *model.RawConcurrency `yaml:"concurrency,omitempty"`
|
|
||||||
RawPermissions yaml.Node `yaml:"permissions,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *Job) Clone() *Job {
|
|
||||||
if j == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &Job{
|
|
||||||
Name: j.Name,
|
|
||||||
RawNeeds: j.RawNeeds,
|
|
||||||
RawRunsOn: j.RawRunsOn,
|
|
||||||
Env: j.Env,
|
|
||||||
If: j.If,
|
|
||||||
Steps: j.Steps,
|
|
||||||
TimeoutMinutes: j.TimeoutMinutes,
|
|
||||||
Services: j.Services,
|
|
||||||
Strategy: j.Strategy,
|
|
||||||
RawContainer: j.RawContainer,
|
|
||||||
Defaults: j.Defaults,
|
|
||||||
Outputs: j.Outputs,
|
|
||||||
Uses: j.Uses,
|
|
||||||
With: j.With,
|
|
||||||
RawSecrets: j.RawSecrets,
|
|
||||||
RawConcurrency: j.RawConcurrency,
|
|
||||||
RawPermissions: j.RawPermissions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *Job) Needs() []string {
|
|
||||||
return (&model.Job{RawNeeds: j.RawNeeds}).Needs()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *Job) EraseNeeds() *Job {
|
|
||||||
j.RawNeeds = yaml.Node{}
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *Job) RunsOn() []string {
|
|
||||||
return (&model.Job{RawRunsOn: j.RawRunsOn}).RunsOn()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Step struct {
|
|
||||||
ID string `yaml:"id,omitempty"`
|
|
||||||
If yaml.Node `yaml:"if,omitempty"`
|
|
||||||
Name string `yaml:"name,omitempty"`
|
|
||||||
Uses string `yaml:"uses,omitempty"`
|
|
||||||
Run string `yaml:"run,omitempty"`
|
|
||||||
WorkingDirectory string `yaml:"working-directory,omitempty"`
|
|
||||||
Shell string `yaml:"shell,omitempty"`
|
|
||||||
Env yaml.Node `yaml:"env,omitempty"`
|
|
||||||
With map[string]string `yaml:"with,omitempty"`
|
|
||||||
ContinueOnError bool `yaml:"continue-on-error,omitempty"`
|
|
||||||
TimeoutMinutes string `yaml:"timeout-minutes,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// String gets the name of step
|
|
||||||
func (s *Step) String() string {
|
|
||||||
if s == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return (&model.Step{
|
|
||||||
ID: s.ID,
|
|
||||||
Name: s.Name,
|
|
||||||
Uses: s.Uses,
|
|
||||||
Run: s.Run,
|
|
||||||
}).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type ContainerSpec struct {
|
|
||||||
Image string `yaml:"image,omitempty"`
|
|
||||||
Env map[string]string `yaml:"env,omitempty"`
|
|
||||||
Ports []string `yaml:"ports,omitempty"`
|
|
||||||
Volumes []string `yaml:"volumes,omitempty"`
|
|
||||||
Options string `yaml:"options,omitempty"`
|
|
||||||
Credentials map[string]string `yaml:"credentials,omitempty"`
|
|
||||||
Cmd []string `yaml:"cmd,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Strategy struct {
|
|
||||||
FailFastString string `yaml:"fail-fast,omitempty"`
|
|
||||||
MaxParallelString string `yaml:"max-parallel,omitempty"`
|
|
||||||
RawMatrix yaml.Node `yaml:"matrix,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Defaults struct {
|
|
||||||
Run RunDefaults `yaml:"run,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RunDefaults struct {
|
|
||||||
Shell string `yaml:"shell,omitempty"`
|
|
||||||
WorkingDirectory string `yaml:"working-directory,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type WorkflowDispatchInput struct {
|
|
||||||
Name string `yaml:"name"`
|
|
||||||
Description string `yaml:"description"`
|
|
||||||
Required bool `yaml:"required"`
|
|
||||||
Default string `yaml:"default"`
|
|
||||||
Type string `yaml:"type"`
|
|
||||||
Options []string `yaml:"options"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Event struct {
|
|
||||||
Name string
|
|
||||||
acts map[string][]string
|
|
||||||
schedules []map[string]string
|
|
||||||
inputs []WorkflowDispatchInput
|
|
||||||
}
|
|
||||||
|
|
||||||
func (evt *Event) IsSchedule() bool {
|
|
||||||
return evt.schedules != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (evt *Event) Acts() map[string][]string {
|
|
||||||
return evt.acts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (evt *Event) Schedules() []map[string]string {
|
|
||||||
return evt.schedules
|
|
||||||
}
|
|
||||||
|
|
||||||
func (evt *Event) Inputs() []WorkflowDispatchInput {
|
|
||||||
return evt.inputs
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseWorkflowDispatchInputs(inputs map[string]interface{}) ([]WorkflowDispatchInput, error) {
|
|
||||||
var results []WorkflowDispatchInput
|
|
||||||
for name, input := range inputs {
|
|
||||||
inputMap, ok := input.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("invalid input: %v", input)
|
|
||||||
}
|
|
||||||
input := WorkflowDispatchInput{
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
if desc, ok := inputMap["description"].(string); ok {
|
|
||||||
input.Description = desc
|
|
||||||
}
|
|
||||||
if required, ok := inputMap["required"].(bool); ok {
|
|
||||||
input.Required = required
|
|
||||||
}
|
|
||||||
if defaultVal, ok := inputMap["default"].(string); ok {
|
|
||||||
input.Default = defaultVal
|
|
||||||
}
|
|
||||||
if inputType, ok := inputMap["type"].(string); ok {
|
|
||||||
input.Type = inputType
|
|
||||||
}
|
|
||||||
if options, ok := inputMap["options"].([]string); ok {
|
|
||||||
input.Options = options
|
|
||||||
} else if options, ok := inputMap["options"].([]interface{}); ok {
|
|
||||||
for _, option := range options {
|
|
||||||
if opt, ok := option.(string); ok {
|
|
||||||
input.Options = append(input.Options, opt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
results = append(results, input)
|
|
||||||
}
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadWorkflowRawConcurrency(content []byte) (*model.RawConcurrency, error) {
|
|
||||||
w := new(model.Workflow)
|
|
||||||
err := yaml.NewDecoder(bytes.NewReader(content)).Decode(w)
|
|
||||||
return w.RawConcurrency, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func EvaluateConcurrency(rc *model.RawConcurrency, jobID string, job *Job, gitCtx map[string]any, results map[string]*JobResult, vars map[string]string, inputs map[string]any) (string, bool, error) {
|
|
||||||
actJob := &model.Job{}
|
|
||||||
if job != nil {
|
|
||||||
actJob.Strategy = &model.Strategy{
|
|
||||||
FailFastString: job.Strategy.FailFastString,
|
|
||||||
MaxParallelString: job.Strategy.MaxParallelString,
|
|
||||||
RawMatrix: job.Strategy.RawMatrix,
|
|
||||||
}
|
|
||||||
actJob.Strategy.FailFast = actJob.Strategy.GetFailFast()
|
|
||||||
actJob.Strategy.MaxParallel = actJob.Strategy.GetMaxParallel()
|
|
||||||
}
|
|
||||||
|
|
||||||
matrix := make(map[string]any)
|
|
||||||
matrixes, err := actJob.GetMatrixes()
|
|
||||||
if err != nil {
|
|
||||||
return "", false, err
|
|
||||||
}
|
|
||||||
if len(matrixes) > 0 {
|
|
||||||
matrix = matrixes[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
evaluator := NewExpressionEvaluator(NewInterpeter(jobID, actJob, matrix, toGitContext(gitCtx), results, vars, inputs))
|
|
||||||
var node yaml.Node
|
|
||||||
if err := node.Encode(rc); err != nil {
|
|
||||||
return "", false, fmt.Errorf("failed to encode concurrency: %w", err)
|
|
||||||
}
|
|
||||||
if err := evaluator.EvaluateYamlNode(&node); err != nil {
|
|
||||||
return "", false, fmt.Errorf("failed to evaluate concurrency: %w", err)
|
|
||||||
}
|
|
||||||
var evaluated model.RawConcurrency
|
|
||||||
if err := node.Decode(&evaluated); err != nil {
|
|
||||||
return "", false, fmt.Errorf("failed to unmarshal evaluated concurrency: %w", err)
|
|
||||||
}
|
|
||||||
if evaluated.RawExpression != "" {
|
|
||||||
return evaluated.RawExpression, false, nil
|
|
||||||
}
|
|
||||||
return evaluated.Group, evaluated.CancelInProgress == "true", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toGitContext(input map[string]any) *model.GithubContext {
|
|
||||||
gitContext := &model.GithubContext{
|
|
||||||
EventPath: asString(input["event_path"]),
|
|
||||||
Workflow: asString(input["workflow"]),
|
|
||||||
RunID: asString(input["run_id"]),
|
|
||||||
RunNumber: asString(input["run_number"]),
|
|
||||||
Actor: asString(input["actor"]),
|
|
||||||
Repository: asString(input["repository"]),
|
|
||||||
EventName: asString(input["event_name"]),
|
|
||||||
Sha: asString(input["sha"]),
|
|
||||||
Ref: asString(input["ref"]),
|
|
||||||
RefName: asString(input["ref_name"]),
|
|
||||||
RefType: asString(input["ref_type"]),
|
|
||||||
HeadRef: asString(input["head_ref"]),
|
|
||||||
BaseRef: asString(input["base_ref"]),
|
|
||||||
Token: asString(input["token"]),
|
|
||||||
Workspace: asString(input["workspace"]),
|
|
||||||
Action: asString(input["action"]),
|
|
||||||
ActionPath: asString(input["action_path"]),
|
|
||||||
ActionRef: asString(input["action_ref"]),
|
|
||||||
ActionRepository: asString(input["action_repository"]),
|
|
||||||
Job: asString(input["job"]),
|
|
||||||
RepositoryOwner: asString(input["repository_owner"]),
|
|
||||||
RetentionDays: asString(input["retention_days"]),
|
|
||||||
}
|
|
||||||
|
|
||||||
event, ok := input["event"].(map[string]any)
|
|
||||||
if ok {
|
|
||||||
gitContext.Event = event
|
|
||||||
}
|
|
||||||
|
|
||||||
return gitContext
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseRawOn(rawOn *yaml.Node) ([]*Event, error) {
|
|
||||||
switch rawOn.Kind {
|
|
||||||
case yaml.ScalarNode:
|
|
||||||
var val string
|
|
||||||
err := rawOn.Decode(&val)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return []*Event{
|
|
||||||
{Name: val},
|
|
||||||
}, nil
|
|
||||||
case yaml.SequenceNode:
|
|
||||||
var val []interface{}
|
|
||||||
err := rawOn.Decode(&val)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
res := make([]*Event, 0, len(val))
|
|
||||||
for _, v := range val {
|
|
||||||
switch t := v.(type) {
|
|
||||||
case string:
|
|
||||||
res = append(res, &Event{Name: t})
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid type %T", t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
case yaml.MappingNode:
|
|
||||||
events, triggers, err := parseMappingNode[yaml.Node](rawOn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
res := make([]*Event, 0, len(events))
|
|
||||||
for i, k := range events {
|
|
||||||
v := triggers[i]
|
|
||||||
switch v.Kind {
|
|
||||||
case yaml.ScalarNode:
|
|
||||||
res = append(res, &Event{
|
|
||||||
Name: k,
|
|
||||||
})
|
|
||||||
case yaml.SequenceNode:
|
|
||||||
var t []interface{}
|
|
||||||
err := v.Decode(&t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
schedules := make([]map[string]string, len(t))
|
|
||||||
if k == "schedule" {
|
|
||||||
for i, tt := range t {
|
|
||||||
vv, ok := tt.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unknown on type(schedule): %#v", v)
|
|
||||||
}
|
|
||||||
schedules[i] = make(map[string]string, len(vv))
|
|
||||||
for k, vvv := range vv {
|
|
||||||
var ok bool
|
|
||||||
if schedules[i][k], ok = vvv.(string); !ok {
|
|
||||||
return nil, fmt.Errorf("unknown on type(schedule): %#v", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(schedules) == 0 {
|
|
||||||
schedules = nil
|
|
||||||
}
|
|
||||||
res = append(res, &Event{
|
|
||||||
Name: k,
|
|
||||||
schedules: schedules,
|
|
||||||
})
|
|
||||||
case yaml.MappingNode:
|
|
||||||
acts := make(map[string][]string, len(v.Content)/2)
|
|
||||||
var inputs []WorkflowDispatchInput
|
|
||||||
expectedKey := true
|
|
||||||
var act string
|
|
||||||
for _, content := range v.Content {
|
|
||||||
if expectedKey {
|
|
||||||
if content.Kind != yaml.ScalarNode {
|
|
||||||
return nil, fmt.Errorf("key type not string: %#v", content)
|
|
||||||
}
|
|
||||||
act = ""
|
|
||||||
err := content.Decode(&act)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch content.Kind {
|
|
||||||
case yaml.SequenceNode:
|
|
||||||
var t []string
|
|
||||||
err := content.Decode(&t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
acts[act] = t
|
|
||||||
case yaml.ScalarNode:
|
|
||||||
var t string
|
|
||||||
err := content.Decode(&t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
acts[act] = []string{t}
|
|
||||||
case yaml.MappingNode:
|
|
||||||
if k != "workflow_dispatch" || act != "inputs" {
|
|
||||||
return nil, fmt.Errorf("map should only for workflow_dispatch but %s: %#v", act, content)
|
|
||||||
}
|
|
||||||
|
|
||||||
var key string
|
|
||||||
for i, vv := range content.Content {
|
|
||||||
if i%2 == 0 {
|
|
||||||
if vv.Kind != yaml.ScalarNode {
|
|
||||||
return nil, fmt.Errorf("key type not string: %#v", vv)
|
|
||||||
}
|
|
||||||
key = ""
|
|
||||||
if err := vv.Decode(&key); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if vv.Kind != yaml.MappingNode {
|
|
||||||
return nil, fmt.Errorf("key type not map(%s): %#v", key, vv)
|
|
||||||
}
|
|
||||||
|
|
||||||
input := WorkflowDispatchInput{}
|
|
||||||
if err := vv.Decode(&input); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
input.Name = key
|
|
||||||
inputs = append(inputs, input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unknown on type: %#v", content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expectedKey = !expectedKey
|
|
||||||
}
|
|
||||||
if len(inputs) == 0 {
|
|
||||||
inputs = nil
|
|
||||||
}
|
|
||||||
if len(acts) == 0 {
|
|
||||||
acts = nil
|
|
||||||
}
|
|
||||||
res = append(res, &Event{
|
|
||||||
Name: k,
|
|
||||||
acts: acts,
|
|
||||||
inputs: inputs,
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unknown on type: %v", v.Kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unknown on type: %v", rawOn.Kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseMappingNode parse a mapping node and preserve order.
|
|
||||||
func parseMappingNode[T any](node *yaml.Node) ([]string, []T, error) {
|
|
||||||
if node.Kind != yaml.MappingNode {
|
|
||||||
return nil, nil, fmt.Errorf("input node is not a mapping node")
|
|
||||||
}
|
|
||||||
|
|
||||||
var scalars []string
|
|
||||||
var datas []T
|
|
||||||
expectKey := true
|
|
||||||
for _, item := range node.Content {
|
|
||||||
if expectKey {
|
|
||||||
if item.Kind != yaml.ScalarNode {
|
|
||||||
return nil, nil, fmt.Errorf("not a valid scalar node: %v", item.Value)
|
|
||||||
}
|
|
||||||
scalars = append(scalars, item.Value)
|
|
||||||
expectKey = false
|
|
||||||
} else {
|
|
||||||
var val T
|
|
||||||
if err := item.Decode(&val); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
datas = append(datas, val)
|
|
||||||
expectKey = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(scalars) != len(datas) {
|
|
||||||
return nil, nil, fmt.Errorf("invalid definition of on: %v", node.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return scalars, datas, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func asString(v interface{}) string {
|
|
||||||
if v == nil {
|
|
||||||
return ""
|
|
||||||
} else if s, ok := v.(string); ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
@@ -1,372 +0,0 @@
|
|||||||
package jobparser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/nektos/act/pkg/model"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"go.yaml.in/yaml/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
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 push:\n branches: main",
|
|
||||||
result: []*Event{
|
|
||||||
{
|
|
||||||
Name: "push",
|
|
||||||
acts: map[string][]string{
|
|
||||||
"branches": {
|
|
||||||
"main",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
|
|
||||||
events, err := ParseRawOn(&origin.RawOn)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.EqualValues(t, kase.result, events, fmt.Sprintf("%#v", events))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseMappingNode(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
input string
|
|
||||||
scalars []string
|
|
||||||
datas []interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
input: "on:\n push:\n branches:\n - master",
|
|
||||||
scalars: []string{"push"},
|
|
||||||
datas: []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"branches": []interface{}{"master"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "on:\n branch_protection_rule:\n types: [created, deleted]",
|
|
||||||
scalars: []string{"branch_protection_rule"},
|
|
||||||
datas: []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"types": []interface{}{"created", "deleted"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "on:\n project:\n types: [created, deleted]\n milestone:\n types: [opened, deleted]",
|
|
||||||
scalars: []string{"project", "milestone"},
|
|
||||||
datas: []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"types": []interface{}{"created", "deleted"},
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"types": []interface{}{"opened", "deleted"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "on:\n pull_request:\n types:\n - opened\n branches:\n - 'releases/**'",
|
|
||||||
scalars: []string{"pull_request"},
|
|
||||||
datas: []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"types": []interface{}{"opened"},
|
|
||||||
"branches": []interface{}{"releases/**"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "on:\n push:\n branches:\n - main\n pull_request:\n types:\n - opened\n branches:\n - '**'",
|
|
||||||
scalars: []string{"push", "pull_request"},
|
|
||||||
datas: []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"branches": []interface{}{"main"},
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"types": []interface{}{"opened"},
|
|
||||||
"branches": []interface{}{"**"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "on:\n schedule:\n - cron: '20 6 * * *'",
|
|
||||||
scalars: []string{"schedule"},
|
|
||||||
datas: []interface{}{
|
|
||||||
[]interface{}{map[string]interface{}{
|
|
||||||
"cron": "20 6 * * *",
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.input, func(t *testing.T) {
|
|
||||||
workflow, err := model.ReadWorkflow(strings.NewReader(test.input))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
scalars, datas, err := parseMappingNode[interface{}](&workflow.RawOn)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.EqualValues(t, test.scalars, scalars, fmt.Sprintf("%#v", scalars))
|
|
||||||
assert.EqualValues(t, test.datas, datas, fmt.Sprintf("%#v", datas))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
8
pkg/jobparser/testdata/empty_step.in.yaml
vendored
8
pkg/jobparser/testdata/empty_step.in.yaml
vendored
@@ -1,8 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: echo job-1
|
|
||||||
-
|
|
||||||
7
pkg/jobparser/testdata/empty_step.out.yaml
vendored
7
pkg/jobparser/testdata/empty_step.out.yaml
vendored
@@ -1,7 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: echo job-1
|
|
||||||
16
pkg/jobparser/testdata/erase_needs.in.yaml
vendored
16
pkg/jobparser/testdata/erase_needs.in.yaml
vendored
@@ -1,16 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a
|
|
||||||
job2:
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a
|
|
||||||
needs: job1
|
|
||||||
job3:
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a
|
|
||||||
needs: [job1, job2]
|
|
||||||
23
pkg/jobparser/testdata/erase_needs.out.yaml
vendored
23
pkg/jobparser/testdata/erase_needs.out.yaml
vendored
@@ -1,23 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job2:
|
|
||||||
name: job2
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job3:
|
|
||||||
name: job3
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a
|
|
||||||
16
pkg/jobparser/testdata/has_needs.in.yaml
vendored
16
pkg/jobparser/testdata/has_needs.in.yaml
vendored
@@ -1,16 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a
|
|
||||||
job2:
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a
|
|
||||||
needs: job1
|
|
||||||
job3:
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a
|
|
||||||
needs: [job1, job2]
|
|
||||||
25
pkg/jobparser/testdata/has_needs.out.yaml
vendored
25
pkg/jobparser/testdata/has_needs.out.yaml
vendored
@@ -1,25 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job2:
|
|
||||||
name: job2
|
|
||||||
needs: job1
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job3:
|
|
||||||
name: job3
|
|
||||||
needs: [job1, job2]
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a
|
|
||||||
14
pkg/jobparser/testdata/has_secrets.in.yaml
vendored
14
pkg/jobparser/testdata/has_secrets.in.yaml
vendored
@@ -1,14 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1
|
|
||||||
runs-on: linux
|
|
||||||
uses: .gitea/workflows/build.yml
|
|
||||||
secrets:
|
|
||||||
secret: hideme
|
|
||||||
|
|
||||||
job2:
|
|
||||||
name: job2
|
|
||||||
runs-on: linux
|
|
||||||
uses: .gitea/workflows/build.yml
|
|
||||||
secrets: inherit
|
|
||||||
16
pkg/jobparser/testdata/has_secrets.out.yaml
vendored
16
pkg/jobparser/testdata/has_secrets.out.yaml
vendored
@@ -1,16 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1
|
|
||||||
runs-on: linux
|
|
||||||
uses: .gitea/workflows/build.yml
|
|
||||||
secrets:
|
|
||||||
secret: hideme
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job2:
|
|
||||||
name: job2
|
|
||||||
runs-on: linux
|
|
||||||
uses: .gitea/workflows/build.yml
|
|
||||||
secrets: inherit
|
|
||||||
15
pkg/jobparser/testdata/has_with.in.yaml
vendored
15
pkg/jobparser/testdata/has_with.in.yaml
vendored
@@ -1,15 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1
|
|
||||||
runs-on: linux
|
|
||||||
uses: .gitea/workflows/build.yml
|
|
||||||
with:
|
|
||||||
package: service
|
|
||||||
|
|
||||||
job2:
|
|
||||||
name: job2
|
|
||||||
runs-on: linux
|
|
||||||
uses: .gitea/workflows/build.yml
|
|
||||||
with:
|
|
||||||
package: module
|
|
||||||
17
pkg/jobparser/testdata/has_with.out.yaml
vendored
17
pkg/jobparser/testdata/has_with.out.yaml
vendored
@@ -1,17 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1
|
|
||||||
runs-on: linux
|
|
||||||
uses: .gitea/workflows/build.yml
|
|
||||||
with:
|
|
||||||
package: service
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job2:
|
|
||||||
name: job2
|
|
||||||
runs-on: linux
|
|
||||||
uses: .gitea/workflows/build.yml
|
|
||||||
with:
|
|
||||||
package: module
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-22.04, ubuntu-20.04]
|
|
||||||
version: [1.17, 1.18, 1.19]
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
name: test_version_${{ matrix.version }}_on_${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
101
pkg/jobparser/testdata/job_name_with_matrix.out.yaml
vendored
101
pkg/jobparser/testdata/job_name_with_matrix.out.yaml
vendored
@@ -1,101 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: test_version_1.17_on_ubuntu-20.04
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-20.04
|
|
||||||
version:
|
|
||||||
- 1.17
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: test_version_1.18_on_ubuntu-20.04
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-20.04
|
|
||||||
version:
|
|
||||||
- 1.18
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: test_version_1.19_on_ubuntu-20.04
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-20.04
|
|
||||||
version:
|
|
||||||
- 1.19
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: test_version_1.17_on_ubuntu-22.04
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-22.04
|
|
||||||
version:
|
|
||||||
- 1.17
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: test_version_1.18_on_ubuntu-22.04
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-22.04
|
|
||||||
version:
|
|
||||||
- 1.18
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: test_version_1.19_on_ubuntu-22.04
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-22.04
|
|
||||||
version:
|
|
||||||
- 1.19
|
|
||||||
22
pkg/jobparser/testdata/multiple_jobs.in.yaml
vendored
22
pkg/jobparser/testdata/multiple_jobs.in.yaml
vendored
@@ -1,22 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
zzz:
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: echo zzz
|
|
||||||
job1:
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a && go version
|
|
||||||
job2:
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a && go version
|
|
||||||
job3:
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a && go version
|
|
||||||
aaa:
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a && go version
|
|
||||||
39
pkg/jobparser/testdata/multiple_jobs.out.yaml
vendored
39
pkg/jobparser/testdata/multiple_jobs.out.yaml
vendored
@@ -1,39 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
zzz:
|
|
||||||
name: zzz
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: echo zzz
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a && go version
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job2:
|
|
||||||
name: job2
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a && go version
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job3:
|
|
||||||
name: job3
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a && go version
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
aaa:
|
|
||||||
name: aaa
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- run: uname -a && go version
|
|
||||||
13
pkg/jobparser/testdata/multiple_matrix.in.yaml
vendored
13
pkg/jobparser/testdata/multiple_matrix.in.yaml
vendored
@@ -1,13 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-22.04, ubuntu-20.04]
|
|
||||||
version: [1.17, 1.18, 1.19]
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
101
pkg/jobparser/testdata/multiple_matrix.out.yaml
vendored
101
pkg/jobparser/testdata/multiple_matrix.out.yaml
vendored
@@ -1,101 +0,0 @@
|
|||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1 (ubuntu-20.04, 1.17)
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-20.04
|
|
||||||
version:
|
|
||||||
- 1.17
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1 (ubuntu-20.04, 1.18)
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-20.04
|
|
||||||
version:
|
|
||||||
- 1.18
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1 (ubuntu-20.04, 1.19)
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-20.04
|
|
||||||
version:
|
|
||||||
- 1.19
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1 (ubuntu-22.04, 1.17)
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-22.04
|
|
||||||
version:
|
|
||||||
- 1.17
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1 (ubuntu-22.04, 1.18)
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-22.04
|
|
||||||
version:
|
|
||||||
- 1.18
|
|
||||||
---
|
|
||||||
name: test
|
|
||||||
jobs:
|
|
||||||
job1:
|
|
||||||
name: job1 (ubuntu-22.04, 1.19)
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.version }}
|
|
||||||
- run: uname -a && go version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-22.04
|
|
||||||
version:
|
|
||||||
- 1.19
|
|
||||||
14
pkg/jobparser/testdata/prefixed_newline.in.yaml
vendored
14
pkg/jobparser/testdata/prefixed_newline.in.yaml
vendored
@@ -1,14 +0,0 @@
|
|||||||
name: Step with leading new line
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: "\nExtract tag for variant"
|
|
||||||
id: extract_tag
|
|
||||||
run: |
|
|
||||||
|
|
||||||
echo Test
|
|
||||||
15
pkg/jobparser/testdata/prefixed_newline.out.yaml
vendored
15
pkg/jobparser/testdata/prefixed_newline.out.yaml
vendored
@@ -1,15 +0,0 @@
|
|||||||
name: Step with leading new line
|
|
||||||
"on":
|
|
||||||
push:
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- id: extract_tag
|
|
||||||
name: |2-
|
|
||||||
|
|
||||||
Extract tag for variant
|
|
||||||
run: |2
|
|
||||||
|
|
||||||
echo Test
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package jobparser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"embed"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed testdata
|
|
||||||
var testdata embed.FS
|
|
||||||
|
|
||||||
func ReadTestdata(t *testing.T, name string) []byte {
|
|
||||||
content, err := testdata.ReadFile(filepath.Join("testdata", name))
|
|
||||||
require.NoError(t, err)
|
|
||||||
return content
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user