Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 81 additions & 23 deletions models/github_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ type GithubActionsStep struct {
Line int `json:"line" yaml:"-"`
Action string `json:"action,omitempty" yaml:"-"`

// Set when the step comes from a flattened `parallel:` block (steps that
// run concurrently). No order index: parallel steps have no defined order.
Parallel bool `json:"parallel,omitempty" yaml:"-"`

Lines map[string]int `json:"lines" yaml:"-"`
}

Expand Down Expand Up @@ -216,7 +220,7 @@ func (o GithubActionsMetadata) IsValid() bool {

func (o *GithubActionsJobs) UnmarshalYAML(node *yaml.Node) error {
if node.Kind != yaml.MappingNode {
return fmt.Errorf("invalid yaml node type for jobs")
return nil
}

*o = make(GithubActionsJobs, 0, len(node.Content)/2)
Expand All @@ -233,7 +237,8 @@ func (o *GithubActionsJobs) UnmarshalYAML(node *yaml.Node) error {
}
err := value.Decode(&job)
if err != nil {
return err
// Skip the offending job, keep the rest of the workflow.
continue
}

for j := 0; j < len(value.Content); j += 2 {
Expand Down Expand Up @@ -261,7 +266,7 @@ func (o *GithubActionsJobSecrets) UnmarshalYAML(node *yaml.Node) error {
}

if node.Kind != yaml.MappingNode {
return fmt.Errorf("invalid yaml node type for secrets")
return nil
}

for i := 0; i < len(node.Content); i += 2 {
Expand All @@ -280,13 +285,15 @@ func (o *StringList) UnmarshalYAML(node *yaml.Node) error {
}

if node.Kind != yaml.SequenceNode {
return fmt.Errorf("invalid yaml node type %v for string list", node.Kind)
// Lenient: unexpected shapes leave an empty list rather than failing
// the entire workflow parse.
return nil
}

var l []string = make([]string, len(node.Content))
l := make([]string, len(node.Content))
err := node.Decode(&l)
if err != nil {
return err
return nil
}

*o = l
Expand Down Expand Up @@ -319,20 +326,23 @@ func (o *GithubActionsEvents) UnmarshalYAML(node *yaml.Node) error {

err := value.Decode(&crons)
if err != nil {
return err
// Skip a malformed schedule block, keep other events.
continue
}

for _, c := range crons {
if c.Cron == "" {
return fmt.Errorf("invalid cron object")
// Skip empty/invalid cron entries individually.
continue
}

event.Cron = append(event.Cron, c.Cron)
}
} else {
err := value.Decode(&event)
if err != nil {
return err
// Skip the offending event, keep the rest.
continue
}
}

Expand All @@ -345,7 +355,7 @@ func (o *GithubActionsEvents) UnmarshalYAML(node *yaml.Node) error {

func (o *GithubActionsOutputs) UnmarshalYAML(node *yaml.Node) error {
if node.Kind != yaml.MappingNode {
return fmt.Errorf("invalid yaml node type for outputs")
return nil
}

for i := 0; i < len(node.Content); i += 2 {
Expand All @@ -360,7 +370,8 @@ func (o *GithubActionsOutputs) UnmarshalYAML(node *yaml.Node) error {
output = GithubActionsOutput{Name: name}
err := value.Decode(&output)
if err != nil {
return err
// Skip the offending output, keep the rest.
continue
}
*o = append(*o, output)
}
Expand All @@ -372,7 +383,7 @@ func (o *GithubActionsOutputs) UnmarshalYAML(node *yaml.Node) error {

func (o *GithubActionsInputs) UnmarshalYAML(node *yaml.Node) error {
if node.Kind != yaml.MappingNode {
return fmt.Errorf("invalid yaml node type for inputs")
return nil
}

for i := 0; i < len(node.Content); i += 2 {
Expand All @@ -382,7 +393,8 @@ func (o *GithubActionsInputs) UnmarshalYAML(node *yaml.Node) error {
err := value.Decode(&input)

if err != nil {
return err
// Skip the offending input, keep the rest.
continue
}

*o = append(*o, input)
Expand All @@ -400,7 +412,7 @@ func (o *GithubActionsEnvs) UnmarshalYAML(node *yaml.Node) error {
}

if node.Kind != yaml.MappingNode {
return fmt.Errorf("invalid yaml node type for env")
return nil
}

for i := 0; i < len(node.Content); i += 2 {
Expand All @@ -412,6 +424,48 @@ func (o *GithubActionsEnvs) UnmarshalYAML(node *yaml.Node) error {
return nil
}

func (o *GithubActionsSteps) UnmarshalYAML(node *yaml.Node) error {
// Be lenient: a single malformed step should only drop that step, not the
// whole job (and therefore not the whole workflow file).
if node.Kind != yaml.SequenceNode {
return nil
}

for _, item := range node.Content {
// flatten parallel steps
if p := mappingValue(item, "parallel"); p != nil {
var nested GithubActionsSteps
_ = p.Decode(&nested) // recurses; handles nested parallel
for k := range nested {
nested[k].Parallel = true
}
*o = append(*o, nested...)
continue
}

var step GithubActionsStep
if err := item.Decode(&step); err != nil {
continue
}
*o = append(*o, step)
}

return nil
}

// mappingValue returns the value node for key in a mapping node, or nil.
func mappingValue(node *yaml.Node, key string) *yaml.Node {
if node.Kind != yaml.MappingNode {
return nil
}
for i := 0; i+1 < len(node.Content); i += 2 {
if node.Content[i].Value == key {
return node.Content[i+1]
}
}
return nil
}

func (o *GithubActionsStep) UnmarshalYAML(node *yaml.Node) error {
type Alias GithubActionsStep
t := Alias{
Expand Down Expand Up @@ -467,7 +521,9 @@ func (o *GithubActionsPermissions) UnmarshalYAML(node *yaml.Node) error {
case "read-all":
permission = PermissionRead
default:
return fmt.Errorf("invalid permission %s", node.Value)
// Unknown scalar (e.g. a typo): leave permissions empty rather
// than failing the whole workflow parse.
return nil
}

*o = make(GithubActionsPermissions, 0, len(AllScopes))
Expand All @@ -478,7 +534,7 @@ func (o *GithubActionsPermissions) UnmarshalYAML(node *yaml.Node) error {
}

if node.Kind != yaml.MappingNode {
return fmt.Errorf("invalid yaml node type for permissions")
return nil
}

*o = make(GithubActionsPermissions, 0, len(node.Content)/2)
Expand All @@ -497,7 +553,7 @@ func (o *GithubActionsJobRunsOn) UnmarshalYAML(node *yaml.Node) error {
var runsOn StringList
err := node.Decode(&runsOn)
if err != nil {
return err
return nil
}
*o = GithubActionsJobRunsOn(runsOn)
}
Expand All @@ -510,18 +566,19 @@ func (o *GithubActionsJobRunsOn) UnmarshalYAML(node *yaml.Node) error {
var runsOn RunsOn
err := node.Decode(&runsOn)
if err != nil {
return err
return nil
}
for _, group := range runsOn.Group {
if group == "" {
return fmt.Errorf("unexpected empty group")
// Skip empty entries individually instead of failing.
continue
}
*o = append(*o, fmt.Sprintf("group:%s", group))
}

for _, label := range runsOn.Labels {
if label == "" {
return fmt.Errorf("unexpected empty label")
continue
}
*o = append(*o, fmt.Sprintf("label:%s", label))
}
Expand All @@ -540,7 +597,8 @@ func (o *GithubActionsJobContainer) UnmarshalYAML(node *yaml.Node) error {
var c container
err := node.Decode(&c)
if err != nil {
return err
// Lenient: leave the container empty rather than failing the parse.
return nil
}
*o = GithubActionsJobContainer(c)
return nil
Expand All @@ -553,13 +611,13 @@ func (o *GithubActionsJobEnvironments) UnmarshalYAML(node *yaml.Node) error {
}

if node.Kind != yaml.MappingNode {
return fmt.Errorf("invalid yaml node type for environment")
return nil
}

var env GithubActionsJobEnvironment
err := node.Decode(&env)
if err != nil {
return err
return nil
}

*o = GithubActionsJobEnvironments{env}
Expand Down
Loading
Loading