Skip to content

Commit

Permalink
refactor: move workflow selection to gale module
Browse files Browse the repository at this point in the history
  • Loading branch information
aweris committed Nov 13, 2023
1 parent e08cb9e commit 8dfb5af
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 135 deletions.
56 changes: 48 additions & 8 deletions daggerverse/gale/gale.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"context"
"fmt"
"path/filepath"
"strings"
)

Expand Down Expand Up @@ -34,7 +35,7 @@ func (g *Gale) List(
sb := strings.Builder{}

err := walkWorkflowDir(ctx, workflowsDir, workflows,
func(ctx context.Context, path string, file *File) error {
func(ctx context.Context, path string, file *File) (bool, error) {
// dagger do not support maps yet, so we're defining anonymous struct to unmarshal the yaml file to avoid
// hit this limitation.
var workflow struct {
Expand All @@ -43,7 +44,7 @@ func (g *Gale) List(
}

if err := unmarshalContentsToYAML(ctx, file, &workflow); err != nil {
return err
return false, err
}

sb.WriteString("Workflow: ")
Expand All @@ -61,7 +62,7 @@ func (g *Gale) List(

sb.WriteString("\n") // extra empty line

return nil
return true, nil
},
)

Expand Down Expand Up @@ -104,19 +105,58 @@ func (g *Gale) Run(
runnerDebug Optional[bool],
// GitHub token to use for authentication.
token Optional[*Secret],
) *WorkflowRun {
) (*WorkflowRun, error) {
wp := ""
wf, ok := workflowFile.Get()
if !ok {
workflow, ok := workflow.Get()
if !ok {
return nil, fmt.Errorf("workflow or workflow file must be provided")
}

workflows := getWorkflowsDir(source, repo, tag, branch, workflowsDir)

err := walkWorkflowDir(ctx, workflowsDir, workflows, func(ctx context.Context, path string, file *File) (bool, error) {
// when relative path or file name is matches with the workflow name, we assume that it is the workflow
// file.
if path == workflow || filepath.Base(path) == workflow {
wf = file
wp = path
return false, nil
}

// otherwise, look for matching workflow name in the workflow file.
var f struct {
Name string `yaml:"name"`
}

if err := unmarshalContentsToYAML(ctx, file, &f); err != nil {
return false, err
}

if f.Name == workflow {
wf = file
wp = path
return false, nil
}

return true, nil
})
if err != nil {
return nil, err
}
}

return &WorkflowRun{
Runner: g.Runner().Container(ctx, image, container, source, repo, tag, branch),
Config: WorkflowRunConfig{
WorkflowsDir: workflowsDir.GetOr(".github/workflows"),
WorkflowFile: workflowFile.GetOr(nil),
Workflow: workflow.GetOr(""),
WorkflowFile: wf,
Workflow: wp,
Job: job.GetOr(""),
Event: event.GetOr("push"),
EventFile: eventFile.GetOr(dag.Directory().WithNewFile("event.json", "{}").File("event.json")),
RunnerDebug: runnerDebug.GetOr(false),
Token: token.GetOr(nil),
},
}
}, nil
}
12 changes: 9 additions & 3 deletions daggerverse/gale/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ func getWorkflowsDir(source Optional[*Directory], repo, tag, branch, workflowsDi
}

// WorkflowWalkFunc is the type of the function called for each workflow file visited by walkWorkflowDir.
type WorkflowWalkFunc func(ctx context.Context, path string, file *File) error
type WorkflowWalkFunc func(ctx context.Context, path string, file *File) (bool, error)

// walkWorkflowDir walks the workflows directory and calls the given function for each workflow file.
// walkWorkflowDir walks the workflows directory and calls the given function for each workflow file. If walk function
// returns false, the walk stops.
func walkWorkflowDir(ctx context.Context, path Optional[string], dir *Directory, fn WorkflowWalkFunc) error {
entries, err := dir.Entries(ctx)
if err != nil {
Expand All @@ -41,9 +42,14 @@ func walkWorkflowDir(ctx context.Context, path Optional[string], dir *Directory,
file := dir.File(entry)
path := filepath.Join(path.GetOr(".github/workflows"), entry)

if err := fn(ctx, path, file); err != nil {
walk, err := fn(ctx, path, file)
if err != nil {
return err
}

if !walk {
return nil
}
}
}

Expand Down
38 changes: 7 additions & 31 deletions daggerverse/gale/workflow_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ type WorkflowRun struct {

// WorkflowRunConfig holds the configuration of a workflow run.
type WorkflowRunConfig struct {
// Path to the workflow directory.
WorkflowsDir string

// WorkflowFile is external workflow file to run.
WorkflowFile *File

Expand Down Expand Up @@ -148,50 +145,29 @@ func (wr *WorkflowRun) run() (*Container, error) {
container = container.WithEnvVariable("RUNNER_DEBUG", "1")
}

// set workflow config
path := filepath.Join(wrPath, "run", "workflow.yaml")

container = container.WithMountedFile(path, wr.Config.WorkflowFile)
container = container.WithEnvVariable("GHX_WORKFLOW", wr.Config.Workflow)
container = container.WithEnvVariable("GHX_JOB", wr.Config.Job)

// event config
eventPath := filepath.Join(wrPath, "run", "event.json")

container = container.WithEnvVariable("GITHUB_EVENT_NAME", wr.Config.Event)
container = container.WithEnvVariable("GITHUB_EVENT_PATH", eventPath)
container = container.WithMountedFile(eventPath, wr.Config.EventFile)

// configure workflow run configuration
container = container.With(wr.configure)

// workaround for disabling cache
container = container.WithEnvVariable("CACHE_BUSTER", time.Now().Format(time.RFC3339Nano))

// execute the workflow
container = container.WithExec([]string{"ghx"}, ContainerWithExecOpts{ExperimentalPrivilegedNesting: true})

// unloading request scoped configs
container = container.WithoutEnvVariable("GHX_WORKFLOW")
container = container.WithoutEnvVariable("GHX_JOB")
container = container.WithoutEnvVariable("GHX_WORKFLOWS_DIR")

return container, nil
}

func (wr *WorkflowRun) configure(c *Container) *Container {
container := c

if wr.Config.WorkflowFile != nil {
path := "/home/runner/_temp/_github_workflow/.gale/dagger.yaml"

container = container.WithMountedFile(path, wr.Config.WorkflowFile)
container = container.WithEnvVariable("GHX_WORKFLOWS_DIR", filepath.Dir(path))

if wr.Config.Workflow != "" {
container = container.WithEnvVariable("GHX_WORKFLOW", wr.Config.Workflow)
} else {
container = container.WithEnvVariable("GHX_WORKFLOW", path)
}
} else {
container = container.WithEnvVariable("GHX_WORKFLOWS_DIR", wr.Config.WorkflowsDir)
container = container.WithEnvVariable("GHX_WORKFLOW", wr.Config.Workflow)
}

container = container.WithEnvVariable("GHX_JOB", wr.Config.Job)

return container
}
3 changes: 0 additions & 3 deletions ghx/context/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ type GhxConfig struct {
// Job name to run. If not specified, the all jobs will be run.
Job string `env:"GHX_JOB"`

// Directory to look for workflows.
WorkflowsDir string `env:"GHX_WORKFLOWS_DIR" envDefault:".github/workflows"`

// Home directory for the ghx to use for storing execution related files.
HomeDir string `env:"GHX_HOME" envDefault:"/home/runner/_temp/ghx"`

Expand Down
12 changes: 0 additions & 12 deletions ghx/context/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,6 @@ func (c *Context) UnsetWorkflow(result RunResult) {
if err := fs.WriteJSONFile(filepath.Join(dir, "workflow_run.json"), report); err != nil {
log.Errorf("failed to write workflow run", "error", err, "workflow", c.Execution.WorkflowRun.Workflow.Name)
}

// copy file to the workflow run directory
var (
src = c.Execution.WorkflowRun.Workflow.Path
dst = filepath.Join(dir, "workflow.yaml")
)

// copy the workflow file to the workflow run directory to keep the workflow file as it is to prevent potential
// changes when marshaling the workflow file again from context
if err := fs.CopyFile(src, dst); err != nil {
log.Errorf("failed to write workflow", "error", err, "workflow", c.Execution.WorkflowRun.Workflow.Name)
}
}

// SetJob sets the given job to the execution context.
Expand Down
51 changes: 43 additions & 8 deletions ghx/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import (
stdContext "context"
"fmt"
"os"
"path/filepath"

"dagger.io/dagger"

"github.com/aweris/gale/common/fs"
"github.com/aweris/gale/common/model"
"github.com/aweris/gale/ghx/context"
)

Expand All @@ -29,15 +32,9 @@ func main() {
cfg := ctx.GhxConfig

// Load workflow
workflows, err := LoadWorkflows(cfg.WorkflowsDir)
wf, err := LoadWorkflow(cfg, filepath.Join(cfg.HomeDir, "run", "workflow.yaml"))
if err != nil {
fmt.Printf("failed to load workflows: %v", err)
os.Exit(1)
}

wf, ok := workflows[cfg.Workflow]
if !ok {
fmt.Printf("workflow %s not found", cfg.Workflow)
fmt.Printf("could not load workflow: %v", err)
os.Exit(1)
}

Expand All @@ -51,3 +48,41 @@ func main() {
// Run the workflow
runner.Run(ctx)
}

func LoadWorkflow(cfg context.GhxConfig, path string) (model.Workflow, error) {
var workflow model.Workflow

if err := fs.ReadYAMLFile(path, &workflow); err != nil {
return workflow, err
}

// set workflow path
workflow.Path = cfg.Workflow

// if the workflow name is not provided, use the relative path to the workflow file.
if workflow.Name == "" {
workflow.Name = cfg.Workflow
}

// update job ID and names
for idj, job := range workflow.Jobs {
job.ID = idj

if job.Name == "" {
job.Name = idj
}

// update step IDs if not provided
for ids, step := range job.Steps {
if step.ID == "" {
step.ID = fmt.Sprintf("%d", ids)
}

job.Steps[ids] = step
}

workflow.Jobs[idj] = job
}

return workflow, nil
}
70 changes: 0 additions & 70 deletions ghx/workflows.go

This file was deleted.

0 comments on commit 8dfb5af

Please sign in to comment.