11package jobparser
22
33import (
4+ "bytes"
45 "fmt"
56
67 "github.com/nektos/act/pkg/model"
@@ -82,6 +83,7 @@ type Job struct {
8283 Uses string `yaml:"uses,omitempty"`
8384 With map [string ]interface {} `yaml:"with,omitempty"`
8485 RawSecrets yaml.Node `yaml:"secrets,omitempty"`
86+ RawConcurrency * model.RawConcurrency `yaml:"concurrency,omitempty"`
8587}
8688
8789func (j * Job ) Clone () * Job {
@@ -104,6 +106,7 @@ func (j *Job) Clone() *Job {
104106 Uses : j .Uses ,
105107 With : j .With ,
106108 RawSecrets : j .RawSecrets ,
109+ RawConcurrency : j .RawConcurrency ,
107110 }
108111}
109112
@@ -241,6 +244,73 @@ func parseWorkflowDispatchInputs(inputs map[string]interface{}) ([]WorkflowDispa
241244 return results , nil
242245}
243246
247+ func ReadWorkflowRawConcurrency (content []byte ) (* model.RawConcurrency , error ) {
248+ w := new (model.Workflow )
249+ err := yaml .NewDecoder (bytes .NewReader (content )).Decode (w )
250+ return w .RawConcurrency , err
251+ }
252+
253+ 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 ) {
254+ actJob := & model.Job {}
255+ if job != nil {
256+ actJob .Strategy = & model.Strategy {
257+ FailFastString : job .Strategy .FailFastString ,
258+ MaxParallelString : job .Strategy .MaxParallelString ,
259+ RawMatrix : job .Strategy .RawMatrix ,
260+ }
261+ actJob .Strategy .FailFast = actJob .Strategy .GetFailFast ()
262+ actJob .Strategy .MaxParallel = actJob .Strategy .GetMaxParallel ()
263+ }
264+
265+ matrix := make (map [string ]any )
266+ matrixes , err := actJob .GetMatrixes ()
267+ if err != nil {
268+ return "" , false , err
269+ }
270+ if len (matrixes ) > 0 {
271+ matrix = matrixes [0 ]
272+ }
273+
274+ evaluator := NewExpressionEvaluator (NewInterpeter (jobID , actJob , matrix , toGitContext (gitCtx ), results , vars , inputs ))
275+ group := evaluator .Interpolate (rc .Group )
276+ cancelInProgress := evaluator .Interpolate (rc .CancelInProgress )
277+ return group , cancelInProgress == "true" , nil
278+ }
279+
280+ func toGitContext (input map [string ]any ) * model.GithubContext {
281+ gitContext := & model.GithubContext {
282+ EventPath : asString (input ["event_path" ]),
283+ Workflow : asString (input ["workflow" ]),
284+ RunID : asString (input ["run_id" ]),
285+ RunNumber : asString (input ["run_number" ]),
286+ Actor : asString (input ["actor" ]),
287+ Repository : asString (input ["repository" ]),
288+ EventName : asString (input ["event_name" ]),
289+ Sha : asString (input ["sha" ]),
290+ Ref : asString (input ["ref" ]),
291+ RefName : asString (input ["ref_name" ]),
292+ RefType : asString (input ["ref_type" ]),
293+ HeadRef : asString (input ["head_ref" ]),
294+ BaseRef : asString (input ["base_ref" ]),
295+ Token : asString (input ["token" ]),
296+ Workspace : asString (input ["workspace" ]),
297+ Action : asString (input ["action" ]),
298+ ActionPath : asString (input ["action_path" ]),
299+ ActionRef : asString (input ["action_ref" ]),
300+ ActionRepository : asString (input ["action_repository" ]),
301+ Job : asString (input ["job" ]),
302+ RepositoryOwner : asString (input ["repository_owner" ]),
303+ RetentionDays : asString (input ["retention_days" ]),
304+ }
305+
306+ event , ok := input ["event" ].(map [string ]any )
307+ if ok {
308+ gitContext .Event = event
309+ }
310+
311+ return gitContext
312+ }
313+
244314func ParseRawOn (rawOn * yaml.Node ) ([]* Event , error ) {
245315 switch rawOn .Kind {
246316 case yaml .ScalarNode :
@@ -422,3 +492,12 @@ func parseMappingNode[T any](node *yaml.Node) ([]string, []T, error) {
422492
423493 return scalars , datas , nil
424494}
495+
496+ func asString (v interface {}) string {
497+ if v == nil {
498+ return ""
499+ } else if s , ok := v .(string ); ok {
500+ return s
501+ }
502+ return ""
503+ }
0 commit comments