Skip to content

Commit

Permalink
Export & document parsing logic
Browse files Browse the repository at this point in the history
Update `parse.go` to intentionally export parsing logic in order to
allow re-use of the logic for parsing GitHub Actions workflow and
manifest files. Even if incomplete, it may be re-usable, plus I'm
willing to expand the structs to allow more use cases (it may come
in handy for this project in the future). As exported funcs (and
structs) there have been given documentation for completeness.

The rest of the codebase is kept unexported. While the parsing logic
is unlikely to see a breaking change (with the struct renames), the
rest of the codebase is, at this point in time, still unstable and
likely to have API changes.

Signed-off-by: Eric Cornelissen <ericornelissen@gmail.com>
  • Loading branch information
ericcornelissen committed Aug 25, 2023
1 parent a93cc88 commit 488140d
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 73 deletions.
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func tryManifest(manifestPath string) (problems []string, err error) {
return nil, nil
}

manifest, err := parseManifest(data)
manifest, err := ParseManifest(data)
if err != nil {
return nil, err
}
Expand All @@ -181,7 +181,7 @@ func tryWorkflow(workflowPath string) (problems []string, err error) {
return nil, err
}

workflow, err := parseWorkflow(data)
workflow, err := ParseWorkflow(data)
if err != nil {
return nil, err
}
Expand Down
26 changes: 17 additions & 9 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,44 +21,52 @@ import (
"gopkg.in/yaml.v3"
)

// Workflow is a (simplified) representation of a GitHub Actions workflow.
type Workflow struct {
Jobs map[string]Job `yaml:"jobs"`
Jobs map[string]WorkflowJob `yaml:"jobs"`
}

type Job struct {
Name string `yaml:"name"`
Steps []Step `yaml:"steps"`
// WorkflowJob is a (simplified) representation of a workflow job.
type WorkflowJob struct {
Name string `yaml:"name"`
Steps []JobStep `yaml:"steps"`
}

type Step struct {
// JobStep is a (simplified) representation of a workflow job step object.
type JobStep struct {
Name string `yaml:"name"`
Run string `yaml:"run"`
Uses string `yaml:"uses"`
With StepWith `yaml:"with"`
}

// StepWith is a (simplified) representation of a job step's `with:` object.
type StepWith struct {
Script string `yaml:"script"`
}

func parseWorkflow(data []byte) (workflow Workflow, err error) {
// ParseWorkflow parses a GitHub Actions workflow file into a Workflow struct.
func ParseWorkflow(data []byte) (workflow Workflow, err error) {
if err = yaml.Unmarshal(data, &workflow); err != nil {
return workflow, fmt.Errorf("could not parse workflow: %v", err)
}

return workflow, nil
}

// Manifest is a (simplified) representation of a GitHub Actions Action manifest.
type Manifest struct {
Runs ManifestRuns `yaml:"runs"`
}

// ManifestRuns is a (simplified) representation of an Action manifest's `runs:` object.
type ManifestRuns struct {
Using string `yaml:"using"`
Steps []Step `yaml:"steps"`
Using string `yaml:"using"`
Steps []JobStep `yaml:"steps"`
}

func parseManifest(data []byte) (manifest Manifest, err error) {
// ParseManifest parses a GitHub Actions Action manifest file into a Manifest struct.
func ParseManifest(data []byte) (manifest Manifest, err error) {
if err = yaml.Unmarshal(data, &manifest); err != nil {
return manifest, fmt.Errorf("could not parse manifest: %v", err)
}
Expand Down
24 changes: 12 additions & 12 deletions parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ jobs:
run: echo '${{ inputs.value }}'
`,
expected: Workflow{
Jobs: map[string]Job{
Jobs: map[string]WorkflowJob{
"example": {
Name: "Example",
Steps: []Step{
Steps: []JobStep{
{
Name: "Checkout repository",
Uses: "actions/checkout@v3",
Expand Down Expand Up @@ -72,10 +72,10 @@ jobs:
script: console.log('${{ inputs.value }}')
`,
expected: Workflow{
Jobs: map[string]Job{
Jobs: map[string]WorkflowJob{
"example": {
Name: "Example",
Steps: []Step{
Steps: []JobStep{
{
Name: "Checkout repository",
Uses: "actions/checkout@v3",
Expand Down Expand Up @@ -104,10 +104,10 @@ jobs:
- run: echo ${{ inputs.value }}
`,
expected: Workflow{
Jobs: map[string]Job{
Jobs: map[string]WorkflowJob{
"example": {
Name: "",
Steps: []Step{
Steps: []JobStep{
{
Uses: "actions/setup-node@v3",
},
Expand All @@ -123,7 +123,7 @@ jobs:

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
workflow, err := parseWorkflow([]byte(tt.yaml))
workflow, err := ParseWorkflow([]byte(tt.yaml))
if err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
Expand Down Expand Up @@ -199,7 +199,7 @@ jobs:

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
_, err := parseWorkflow([]byte(tt.yaml))
_, err := ParseWorkflow([]byte(tt.yaml))
if err == nil {
t.Fatal("Expected an error, got none")
}
Expand Down Expand Up @@ -242,7 +242,7 @@ runs:
expected: Manifest{
Runs: ManifestRuns{
Using: "composite",
Steps: []Step{
Steps: []JobStep{
{
Name: "Checkout repository",
Uses: "actions/checkout@v3",
Expand Down Expand Up @@ -273,7 +273,7 @@ runs:
expected: Manifest{
Runs: ManifestRuns{
Using: "composite",
Steps: []Step{
Steps: []JobStep{
{
Name: "Checkout repository",
Uses: "actions/checkout@v3",
Expand All @@ -293,7 +293,7 @@ runs:

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
manifest, err := parseManifest([]byte(tt.yaml))
manifest, err := ParseManifest([]byte(tt.yaml))
if err != nil {
t.Fatalf("Unexpected error: %#v", err)
}
Expand Down Expand Up @@ -349,7 +349,7 @@ runs:

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
_, err := parseManifest([]byte(tt.yaml))
_, err := ParseManifest([]byte(tt.yaml))
if err == nil {
t.Fatal("Expected an error, got none")
}
Expand Down
10 changes: 5 additions & 5 deletions workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func processWorkflow(workflow *Workflow) (problems []string) {
return problems
}

func processJob(id string, job *Job) (problems []string) {
func processJob(id string, job *WorkflowJob) (problems []string) {
name := job.Name
if name == "" {
name = id
Expand All @@ -54,7 +54,7 @@ func processJob(id string, job *Job) (problems []string) {
return problems
}

func processSteps(steps []Step) (problems []string) {
func processSteps(steps []JobStep) (problems []string) {
for i, step := range steps {
step := step
problems = append(problems, processStep(i, &step)...)
Expand All @@ -63,7 +63,7 @@ func processSteps(steps []Step) (problems []string) {
return problems
}

func processStep(id int, step *Step) (problems []string) {
func processStep(id int, step *JobStep) (problems []string) {
name := fmt.Sprintf("'%s'", step.Name)
if step.Name == "" {
name = fmt.Sprintf("#%d", id)
Expand Down Expand Up @@ -94,10 +94,10 @@ func processScript(script string) (problems []string) {
return problems
}

func isRunStep(step *Step) bool {
func isRunStep(step *JobStep) bool {
return len(step.Run) > 0
}

func isActionsGitHubScriptStep(step *Step) bool {
func isActionsGitHubScriptStep(step *JobStep) bool {
return strings.HasPrefix(step.Uses, "actions/github-script@")
}
Loading

0 comments on commit 488140d

Please sign in to comment.