11package jobparser
22
33import (
4+ "bytes"
45 "fmt"
56
67 "code.forgejo.org/forgejo/runner/v9/act/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
@@ -190,6 +193,85 @@ func (evt *Event) Schedules() []map[string]string {
190193 return evt .schedules
191194}
192195
196+ func ReadWorkflowRawConcurrency (content []byte ) (* model.RawConcurrency , error ) {
197+ w := new (model.Workflow )
198+ err := yaml .NewDecoder (bytes .NewReader (content )).Decode (w )
199+ return w .RawConcurrency , err
200+ }
201+
202+ 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 ) {
203+ actJob := & model.Job {}
204+ if job != nil {
205+ actJob .Strategy = & model.Strategy {
206+ FailFastString : job .Strategy .FailFastString ,
207+ MaxParallelString : job .Strategy .MaxParallelString ,
208+ RawMatrix : job .Strategy .RawMatrix ,
209+ }
210+ actJob .Strategy .FailFast = actJob .Strategy .GetFailFast ()
211+ actJob .Strategy .MaxParallel = actJob .Strategy .GetMaxParallel ()
212+ }
213+
214+ matrix := make (map [string ]any )
215+ matrixes , err := actJob .GetMatrixes ()
216+ if err != nil {
217+ return "" , false , err
218+ }
219+ if len (matrixes ) > 0 {
220+ matrix = matrixes [0 ]
221+ }
222+
223+ evaluator := NewExpressionEvaluator (NewInterpeter (jobID , actJob , matrix , toGitContext (gitCtx ), results , vars , inputs ))
224+ var node yaml.Node
225+ if err := node .Encode (rc ); err != nil {
226+ return "" , false , fmt .Errorf ("failed to encode concurrency: %w" , err )
227+ }
228+ if err := evaluator .EvaluateYamlNode (& node ); err != nil {
229+ return "" , false , fmt .Errorf ("failed to evaluate concurrency: %w" , err )
230+ }
231+ var evaluated model.RawConcurrency
232+ if err := node .Decode (& evaluated ); err != nil {
233+ return "" , false , fmt .Errorf ("failed to unmarshal evaluated concurrency: %w" , err )
234+ }
235+ if evaluated .RawExpression != "" {
236+ return evaluated .RawExpression , false , nil
237+ }
238+ return evaluated .Group , evaluated .CancelInProgress == "true" , nil
239+ }
240+
241+ func toGitContext (input map [string ]any ) * model.GithubContext {
242+ gitContext := & model.GithubContext {
243+ EventPath : asString (input ["event_path" ]),
244+ Workflow : asString (input ["workflow" ]),
245+ RunID : asString (input ["run_id" ]),
246+ RunNumber : asString (input ["run_number" ]),
247+ Actor : asString (input ["actor" ]),
248+ Repository : asString (input ["repository" ]),
249+ EventName : asString (input ["event_name" ]),
250+ Sha : asString (input ["sha" ]),
251+ Ref : asString (input ["ref" ]),
252+ RefName : asString (input ["ref_name" ]),
253+ RefType : asString (input ["ref_type" ]),
254+ HeadRef : asString (input ["head_ref" ]),
255+ BaseRef : asString (input ["base_ref" ]),
256+ Token : asString (input ["token" ]),
257+ Workspace : asString (input ["workspace" ]),
258+ Action : asString (input ["action" ]),
259+ ActionPath : asString (input ["action_path" ]),
260+ ActionRef : asString (input ["action_ref" ]),
261+ ActionRepository : asString (input ["action_repository" ]),
262+ Job : asString (input ["job" ]),
263+ RepositoryOwner : asString (input ["repository_owner" ]),
264+ RetentionDays : asString (input ["retention_days" ]),
265+ }
266+
267+ event , ok := input ["event" ].(map [string ]any )
268+ if ok {
269+ gitContext .Event = event
270+ }
271+
272+ return gitContext
273+ }
274+
193275func ParseRawOn (rawOn * yaml.Node ) ([]* Event , error ) {
194276 switch rawOn .Kind {
195277 case yaml .ScalarNode :
@@ -348,3 +430,12 @@ func parseMappingNode[T any](node *yaml.Node) ([]string, []T, error) {
348430
349431 return scalars , datas , nil
350432}
433+
434+ func asString (v interface {}) string {
435+ if v == nil {
436+ return ""
437+ } else if s , ok := v .(string ); ok {
438+ return s
439+ }
440+ return ""
441+ }
0 commit comments