Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: update github context #52

Merged
merged 3 commits into from
Aug 2, 2023
Merged
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
1 change: 1 addition & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ linters-settings:
- github.com/Masterminds/semver/v3
- github.com/google/uuid
- github.com/rhysd/actionlint
- github.com/magefile/mage/sh
41 changes: 41 additions & 0 deletions internal/core/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,47 @@ type GithubFilesContext struct {
Path string `json:"path"` // Path is the path to a temporary file that sets the system PATH variable from workflow commands.
}

// GithubWorkflowContext is a context that contains information about the workflow.
type GithubWorkflowContext struct {
Workflow string `json:"workflow"` // Workflow is the name of the workflow. If the workflow file doesn't specify a name, the value of this property is the full path of the workflow file in the repository.
WorkflowRef string `json:"workflow_ref"` // WorkflowRef is the ref path to the workflow. For example, octocat/hello-world/.github/workflows/my-workflow.yml@refs/heads/my_branch.
WorkflowSHA string `json:"workflow_sha"` // WorkflowSHA is the commit SHA for the workflow file.
}

// NewGithubWorkflowContext creates a new GithubWorkflowContext from the given workflow.
func NewGithubWorkflowContext(repo *Repository, workflow *Workflow) GithubWorkflowContext {
return GithubWorkflowContext{
Workflow: workflow.Name,
WorkflowRef: fmt.Sprintf("%s/%s@%s", repo.NameWithOwner, workflow.Path, repo.CurrentRef),
WorkflowSHA: workflow.SHA,
}
}

// Apply applies the GithubWorkflowContext to the given container.
func (c GithubWorkflowContext) Apply(container *dagger.Container) *dagger.Container {
return container.
WithEnvVariable("GITHUB_WORKFLOW", c.Workflow).
WithEnvVariable("GITHUB_WORKFLOW_REF", c.WorkflowRef).
WithEnvVariable("GITHUB_WORKFLOW_SHA", c.WorkflowSHA)
}

// GithubJobInfoContext is a context that contains information about the job.
type GithubJobInfoContext struct {
Job string `json:"job"` // Job is the job_id of the current job. Note: This context property is set by the Actions runner, and is only available within the execution steps of a job. Otherwise, the value of this property will be null.

// TODO: enable these fields when reusable workflows are supported.
// JobWorkflowSHA string // JobWorkflowSHA is for jobs using a reusable workflow, the commit SHA for the reusable workflow file.
}

func NewGithubJobInfoContext(jobID string) GithubJobInfoContext {
return GithubJobInfoContext{Job: jobID}
}

// Apply applies the GithubJobInfoContext to the given container.
func (c GithubJobInfoContext) Apply(container *dagger.Container) *dagger.Container {
return container.WithEnvVariable("GITHUB_JOB", c.Job)
}

// JobContext contains information about the currently running job.
//
// See: https://docs.github.com/en/actions/learn-github-actions/contexts#job-context
Expand Down
35 changes: 32 additions & 3 deletions internal/core/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"gopkg.in/yaml.v3"

"github.com/cli/go-gh/v2"
"github.com/magefile/mage/sh"

"github.com/aweris/gale/internal/config"
)
Expand All @@ -24,6 +25,7 @@ type Repository struct {
URL string
Owner RepositoryOwner
DefaultBranchRef RepositoryBranchRef
CurrentRef string
Dir *dagger.Directory // Dir is the directory where the repository is checked out
}

Expand Down Expand Up @@ -70,7 +72,10 @@ func GetRepository(name string, opts ...GetRepositoryOpts) (*Repository, error)
return nil, fmt.Errorf("failed to unmarshal current repository: %s err: %w", stdout.String(), err)
}

var dir *dagger.Directory
var (
ref string
dir *dagger.Directory
)

opt := GetRepositoryOpts{}
if len(opts) > 0 {
Expand All @@ -83,18 +88,31 @@ func GetRepository(name string, opts ...GetRepositoryOpts) (*Repository, error)
switch {
case opt.Commit != "":
dir = git.Commit(opt.Commit).Tree()

case opt.Tag != "":
dir = git.Tag(opt.Tag).Tree()
ref = fmt.Sprintf("refs/tags/%s", opt.Tag)
case opt.Branch != "":
dir = git.Branch(opt.Branch).Tree()
ref = fmt.Sprintf("refs/heads/%s", opt.Branch)
case name != "":
dir = git.Branch(repo.DefaultBranchRef.Name).Tree()
ref = fmt.Sprintf("refs/heads/%s", repo.DefaultBranchRef.Name)
default:
// TODO: current directory could be a subdirectory of the repository. Should we handle this case?
dir = config.Client().Host().Directory(".")

// get current ref name
rev, err := sh.Output("git", "rev-parse", "--symbolic-full-name", "HEAD")
if err != nil {
return nil, err
}

ref = strings.TrimSpace(rev)
}

repo.Dir = dir
repo.CurrentRef = ref

return &repo, nil
}
Expand Down Expand Up @@ -127,7 +145,7 @@ func (r *Repository) LoadWorkflows(ctx context.Context, opts ...RepositoryLoadWo
if strings.HasSuffix(entry, ".yaml") || strings.HasSuffix(entry, ".yml") {
file := dir.File(entry)

workflow, err := loadWorkflow(ctx, filepath.Join(path, entry), file)
workflow, err := r.loadWorkflow(ctx, filepath.Join(path, entry), file)
if err != nil {
return nil, err
}
Expand All @@ -141,7 +159,7 @@ func (r *Repository) LoadWorkflows(ctx context.Context, opts ...RepositoryLoadWo

// loadWorkflow loads a workflow from a file. If the workflow name is not provided, the relative path to the workflow
// file will be used as the workflow name.
func loadWorkflow(ctx context.Context, path string, file *dagger.File) (*Workflow, error) {
func (r *Repository) loadWorkflow(ctx context.Context, path string, file *dagger.File) (*Workflow, error) {
content, err := file.Contents(ctx)
if err != nil {
return nil, err
Expand All @@ -160,5 +178,16 @@ func loadWorkflow(ctx context.Context, path string, file *dagger.File) (*Workflo
workflow.Name = path
}

// TODO: this is an expensive operation. We should move this to a separate method and call it only when needed.

api := fmt.Sprintf("repos/%s/contents/%s?ref=%s", r.NameWithOwner, path, r.CurrentRef)

stdout, stderr, err := gh.Exec("api", api, "--jq", ".sha")
if err != nil {
return nil, fmt.Errorf("failed to get current repository: %w stderr: %s", err, stderr.String())
}

workflow.SHA = strings.TrimSpace(stdout.String())

return &workflow, nil
}
1 change: 1 addition & 0 deletions internal/core/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type Workflow struct {
Name string `yaml:"name"` // Name is the name of the workflow.
Env map[string]string `yaml:"env"` // Env is the environment variables used in the workflow
Jobs map[string]Job `yaml:"jobs"` // Jobs is the list of jobs in the workflow.
SHA string `yaml:"-"` // SHA is the commit SHA for the workflow file. Set by gale when loading the workflow.

// TBD: add more fields when needed
}
2 changes: 2 additions & 0 deletions pkg/gale/gale.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ func Run(ctx context.Context, workflow, job string, opts ...RunOpts) dagger.With
container = container.With(core.NewGithubRepositoryContext(repo).Apply)
container = container.With(core.NewGithubSecretsContext(token).Apply)
container = container.With(core.NewGithubURLContext().Apply)
container = container.With(core.NewGithubWorkflowContext(repo, wf).Apply)
container = container.With(core.NewGithubJobInfoContext(job).Apply)

// job run configuration
container = container.WithDirectory(config.GhxRunDir(runID), dir)
Expand Down
21 changes: 20 additions & 1 deletion tools/ghx/actions/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var _ expression.VariableProvider = new(ExprContext)

type ExprContext struct {
Github GithubContext // Github context
Runner core.RunnerContext // Runner context
Job core.JobContext // Job context
Steps map[string]core.StepContext // Steps context

Expand Down Expand Up @@ -49,8 +50,24 @@ func NewExprContext() *ExprContext {
GraphqlURL: os.Getenv("GITHUB_GRAPHQL_URL"),
ServerURL: os.Getenv("GITHUB_SERVER_URL"),
},
GithubWorkflowContext: core.GithubWorkflowContext{
Workflow: os.Getenv("GITHUB_WORKFLOW"),
WorkflowRef: os.Getenv("GITHUB_WORKFLOW_REF"),
WorkflowSHA: os.Getenv("GITHUB_WORKFLOW_SHA"),
},
GithubJobInfoContext: core.GithubJobInfoContext{
Job: os.Getenv("GITHUB_JOB"),
},
GithubFilesContext: core.GithubFilesContext{ /* No initial values */ },
},
Runner: core.RunnerContext{
Name: os.Getenv("RUNNER_NAME"),
OS: os.Getenv("RUNNER_OS"),
Arch: os.Getenv("RUNNER_ARCH"),
Temp: os.Getenv("RUNNER_TEMP"),
ToolCache: os.Getenv("RUNNER_TOOL_CACHE"),
Debug: os.Getenv("RUNNER_DEBUG"),
},
Job: core.JobContext{
Status: core.ConclusionSuccess, // start with success status
},
Expand All @@ -70,6 +87,8 @@ type GithubContext struct {
core.GithubRepositoryContext
core.GithubSecretsContext
core.GithubURLContext
core.GithubWorkflowContext
core.GithubJobInfoContext

// Local contexts - these contexts changes at course of the workflow run.
core.GithubFilesContext
Expand All @@ -82,7 +101,7 @@ func (c *ExprContext) GetVariable(name string) (interface{}, error) {
case "github":
return c.Github, nil
case "runner":
return map[string]string{}, nil
return c.Runner, nil
case "env":
return map[string]string{}, nil
case "vars":
Expand Down
Loading