diff --git a/pkg/jobparser/interpeter.go b/pkg/jobparser/interpeter.go index 8aaf319..3d121ad 100644 --- a/pkg/jobparser/interpeter.go +++ b/pkg/jobparser/interpeter.go @@ -16,6 +16,7 @@ func NewInterpeter( 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 { @@ -62,7 +63,7 @@ func NewInterpeter( Strategy: strategy, Matrix: matrix, Needs: using, - Inputs: nil, // not supported yet + Inputs: inputs, Vars: vars, } diff --git a/pkg/jobparser/jobparser.go b/pkg/jobparser/jobparser.go index 2a482c3..eb23dbd 100644 --- a/pkg/jobparser/jobparser.go +++ b/pkg/jobparser/jobparser.go @@ -52,7 +52,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(NewInterpeter(id, origin.GetJob(id), matrix, pc.gitContext, results, pc.vars, nil)) job.Name = nameWithMatrix(job.Name, matrix, evaluator) runsOn := origin.GetJob(id).RunsOn() for i, v := range runsOn { diff --git a/pkg/jobparser/model.go b/pkg/jobparser/model.go index 08c5810..6f4ea27 100644 --- a/pkg/jobparser/model.go +++ b/pkg/jobparser/model.go @@ -1,6 +1,7 @@ package jobparser import ( + "bytes" "fmt" "github.com/nektos/act/pkg/model" @@ -82,6 +83,7 @@ type Job struct { Uses string `yaml:"uses,omitempty"` With map[string]interface{} `yaml:"with,omitempty"` RawSecrets yaml.Node `yaml:"secrets,omitempty"` + RawConcurrency *model.RawConcurrency `yaml:"concurrency,omitempty"` } func (j *Job) Clone() *Job { @@ -104,6 +106,7 @@ func (j *Job) Clone() *Job { Uses: j.Uses, With: j.With, RawSecrets: j.RawSecrets, + RawConcurrency: j.RawConcurrency, } } @@ -241,6 +244,73 @@ func parseWorkflowDispatchInputs(inputs map[string]interface{}) ([]WorkflowDispa 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)) + group := evaluator.Interpolate(rc.Group) + cancelInProgress := evaluator.Interpolate(rc.CancelInProgress) + return group, 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: @@ -422,3 +492,12 @@ func parseMappingNode[T any](node *yaml.Node) ([]string, []T, error) { return scalars, datas, nil } + +func asString(v interface{}) string { + if v == nil { + return "" + } else if s, ok := v.(string); ok { + return s + } + return "" +} diff --git a/pkg/model/workflow.go b/pkg/model/workflow.go index be1652a..f2ba70a 100644 --- a/pkg/model/workflow.go +++ b/pkg/model/workflow.go @@ -23,6 +23,8 @@ type Workflow struct { Env map[string]string `yaml:"env"` Jobs map[string]*Job `yaml:"jobs"` Defaults Defaults `yaml:"defaults"` + + RawConcurrency *RawConcurrency `yaml:"concurrency"` // For Gitea } // On events for the workflow @@ -769,3 +771,10 @@ func decodeNode(node yaml.Node, out interface{}) bool { } return true } + +// For Gitea +// RawConcurrency represents a workflow concurrency or a job concurrency with uninterpolated options +type RawConcurrency struct { + Group string `yaml:"group,omitempty"` + CancelInProgress string `yaml:"cancel-in-progress,omitempty"` +}