@@ -114,6 +114,10 @@ type GithubActionsStep struct {
114114 Line int `json:"line" yaml:"-"`
115115 Action string `json:"action,omitempty" yaml:"-"`
116116
117+ // Set when the step comes from a flattened `parallel:` block (steps that
118+ // run concurrently). No order index: parallel steps have no defined order.
119+ Parallel bool `json:"parallel,omitempty" yaml:"-"`
120+
117121 Lines map [string ]int `json:"lines" yaml:"-"`
118122}
119123
@@ -216,7 +220,7 @@ func (o GithubActionsMetadata) IsValid() bool {
216220
217221func (o * GithubActionsJobs ) UnmarshalYAML (node * yaml.Node ) error {
218222 if node .Kind != yaml .MappingNode {
219- return fmt . Errorf ( "invalid yaml node type for jobs" )
223+ return nil
220224 }
221225
222226 * o = make (GithubActionsJobs , 0 , len (node .Content )/ 2 )
@@ -233,7 +237,8 @@ func (o *GithubActionsJobs) UnmarshalYAML(node *yaml.Node) error {
233237 }
234238 err := value .Decode (& job )
235239 if err != nil {
236- return err
240+ // Skip the offending job, keep the rest of the workflow.
241+ continue
237242 }
238243
239244 for j := 0 ; j < len (value .Content ); j += 2 {
@@ -261,7 +266,7 @@ func (o *GithubActionsJobSecrets) UnmarshalYAML(node *yaml.Node) error {
261266 }
262267
263268 if node .Kind != yaml .MappingNode {
264- return fmt . Errorf ( "invalid yaml node type for secrets" )
269+ return nil
265270 }
266271
267272 for i := 0 ; i < len (node .Content ); i += 2 {
@@ -280,13 +285,15 @@ func (o *StringList) UnmarshalYAML(node *yaml.Node) error {
280285 }
281286
282287 if node .Kind != yaml .SequenceNode {
283- return fmt .Errorf ("invalid yaml node type %v for string list" , node .Kind )
288+ // Lenient: unexpected shapes leave an empty list rather than failing
289+ // the entire workflow parse.
290+ return nil
284291 }
285292
286- var l [] string = make ([]string , len (node .Content ))
293+ l : = make ([]string , len (node .Content ))
287294 err := node .Decode (& l )
288295 if err != nil {
289- return err
296+ return nil
290297 }
291298
292299 * o = l
@@ -319,20 +326,23 @@ func (o *GithubActionsEvents) UnmarshalYAML(node *yaml.Node) error {
319326
320327 err := value .Decode (& crons )
321328 if err != nil {
322- return err
329+ // Skip a malformed schedule block, keep other events.
330+ continue
323331 }
324332
325333 for _ , c := range crons {
326334 if c .Cron == "" {
327- return fmt .Errorf ("invalid cron object" )
335+ // Skip empty/invalid cron entries individually.
336+ continue
328337 }
329338
330339 event .Cron = append (event .Cron , c .Cron )
331340 }
332341 } else {
333342 err := value .Decode (& event )
334343 if err != nil {
335- return err
344+ // Skip the offending event, keep the rest.
345+ continue
336346 }
337347 }
338348
@@ -345,7 +355,7 @@ func (o *GithubActionsEvents) UnmarshalYAML(node *yaml.Node) error {
345355
346356func (o * GithubActionsOutputs ) UnmarshalYAML (node * yaml.Node ) error {
347357 if node .Kind != yaml .MappingNode {
348- return fmt . Errorf ( "invalid yaml node type for outputs" )
358+ return nil
349359 }
350360
351361 for i := 0 ; i < len (node .Content ); i += 2 {
@@ -360,7 +370,8 @@ func (o *GithubActionsOutputs) UnmarshalYAML(node *yaml.Node) error {
360370 output = GithubActionsOutput {Name : name }
361371 err := value .Decode (& output )
362372 if err != nil {
363- return err
373+ // Skip the offending output, keep the rest.
374+ continue
364375 }
365376 * o = append (* o , output )
366377 }
@@ -372,7 +383,7 @@ func (o *GithubActionsOutputs) UnmarshalYAML(node *yaml.Node) error {
372383
373384func (o * GithubActionsInputs ) UnmarshalYAML (node * yaml.Node ) error {
374385 if node .Kind != yaml .MappingNode {
375- return fmt . Errorf ( "invalid yaml node type for inputs" )
386+ return nil
376387 }
377388
378389 for i := 0 ; i < len (node .Content ); i += 2 {
@@ -382,7 +393,8 @@ func (o *GithubActionsInputs) UnmarshalYAML(node *yaml.Node) error {
382393 err := value .Decode (& input )
383394
384395 if err != nil {
385- return err
396+ // Skip the offending input, keep the rest.
397+ continue
386398 }
387399
388400 * o = append (* o , input )
@@ -400,7 +412,7 @@ func (o *GithubActionsEnvs) UnmarshalYAML(node *yaml.Node) error {
400412 }
401413
402414 if node .Kind != yaml .MappingNode {
403- return fmt . Errorf ( "invalid yaml node type for env" )
415+ return nil
404416 }
405417
406418 for i := 0 ; i < len (node .Content ); i += 2 {
@@ -412,6 +424,48 @@ func (o *GithubActionsEnvs) UnmarshalYAML(node *yaml.Node) error {
412424 return nil
413425}
414426
427+ func (o * GithubActionsSteps ) UnmarshalYAML (node * yaml.Node ) error {
428+ // Be lenient: a single malformed step should only drop that step, not the
429+ // whole job (and therefore not the whole workflow file).
430+ if node .Kind != yaml .SequenceNode {
431+ return nil
432+ }
433+
434+ for _ , item := range node .Content {
435+ // flatten parallel steps
436+ if p := mappingValue (item , "parallel" ); p != nil {
437+ var nested GithubActionsSteps
438+ _ = p .Decode (& nested ) // recurses; handles nested parallel
439+ for k := range nested {
440+ nested [k ].Parallel = true
441+ }
442+ * o = append (* o , nested ... )
443+ continue
444+ }
445+
446+ var step GithubActionsStep
447+ if err := item .Decode (& step ); err != nil {
448+ continue
449+ }
450+ * o = append (* o , step )
451+ }
452+
453+ return nil
454+ }
455+
456+ // mappingValue returns the value node for key in a mapping node, or nil.
457+ func mappingValue (node * yaml.Node , key string ) * yaml.Node {
458+ if node .Kind != yaml .MappingNode {
459+ return nil
460+ }
461+ for i := 0 ; i + 1 < len (node .Content ); i += 2 {
462+ if node .Content [i ].Value == key {
463+ return node .Content [i + 1 ]
464+ }
465+ }
466+ return nil
467+ }
468+
415469func (o * GithubActionsStep ) UnmarshalYAML (node * yaml.Node ) error {
416470 type Alias GithubActionsStep
417471 t := Alias {
@@ -467,7 +521,9 @@ func (o *GithubActionsPermissions) UnmarshalYAML(node *yaml.Node) error {
467521 case "read-all" :
468522 permission = PermissionRead
469523 default :
470- return fmt .Errorf ("invalid permission %s" , node .Value )
524+ // Unknown scalar (e.g. a typo): leave permissions empty rather
525+ // than failing the whole workflow parse.
526+ return nil
471527 }
472528
473529 * o = make (GithubActionsPermissions , 0 , len (AllScopes ))
@@ -478,7 +534,7 @@ func (o *GithubActionsPermissions) UnmarshalYAML(node *yaml.Node) error {
478534 }
479535
480536 if node .Kind != yaml .MappingNode {
481- return fmt . Errorf ( "invalid yaml node type for permissions" )
537+ return nil
482538 }
483539
484540 * o = make (GithubActionsPermissions , 0 , len (node .Content )/ 2 )
@@ -497,7 +553,7 @@ func (o *GithubActionsJobRunsOn) UnmarshalYAML(node *yaml.Node) error {
497553 var runsOn StringList
498554 err := node .Decode (& runsOn )
499555 if err != nil {
500- return err
556+ return nil
501557 }
502558 * o = GithubActionsJobRunsOn (runsOn )
503559 }
@@ -510,18 +566,19 @@ func (o *GithubActionsJobRunsOn) UnmarshalYAML(node *yaml.Node) error {
510566 var runsOn RunsOn
511567 err := node .Decode (& runsOn )
512568 if err != nil {
513- return err
569+ return nil
514570 }
515571 for _ , group := range runsOn .Group {
516572 if group == "" {
517- return fmt .Errorf ("unexpected empty group" )
573+ // Skip empty entries individually instead of failing.
574+ continue
518575 }
519576 * o = append (* o , fmt .Sprintf ("group:%s" , group ))
520577 }
521578
522579 for _ , label := range runsOn .Labels {
523580 if label == "" {
524- return fmt . Errorf ( "unexpected empty label" )
581+ continue
525582 }
526583 * o = append (* o , fmt .Sprintf ("label:%s" , label ))
527584 }
@@ -540,7 +597,8 @@ func (o *GithubActionsJobContainer) UnmarshalYAML(node *yaml.Node) error {
540597 var c container
541598 err := node .Decode (& c )
542599 if err != nil {
543- return err
600+ // Lenient: leave the container empty rather than failing the parse.
601+ return nil
544602 }
545603 * o = GithubActionsJobContainer (c )
546604 return nil
@@ -553,13 +611,13 @@ func (o *GithubActionsJobEnvironments) UnmarshalYAML(node *yaml.Node) error {
553611 }
554612
555613 if node .Kind != yaml .MappingNode {
556- return fmt . Errorf ( "invalid yaml node type for environment" )
614+ return nil
557615 }
558616
559617 var env GithubActionsJobEnvironment
560618 err := node .Decode (& env )
561619 if err != nil {
562- return err
620+ return nil
563621 }
564622
565623 * o = GithubActionsJobEnvironments {env }
0 commit comments