refactor: export and move shared contexts into pkg/model (#931)

This commit moves the githubContext, jobContext and stepResult structs
from the runner package to the model package in preparation for #908
because the expression.go file lives in the runner package and would
introduce cyclic dependencies with the exprparser package.

Co-authored-by: Markus Wolf <markus.wolf@new-work.se>

Co-authored-by: Markus Wolf <markus.wolf@new-work.se>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
Björn Brauer 2021-12-22 20:52:09 +01:00 committed by GitHub
parent 558081242c
commit ed01f464ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 132 additions and 124 deletions

View file

@ -0,0 +1,28 @@
package model
type GithubContext struct {
Event map[string]interface{} `json:"event"`
EventPath string `json:"event_path"`
Workflow string `json:"workflow"`
RunID string `json:"run_id"`
RunNumber string `json:"run_number"`
Actor string `json:"actor"`
Repository string `json:"repository"`
EventName string `json:"event_name"`
Sha string `json:"sha"`
Ref string `json:"ref"`
HeadRef string `json:"head_ref"`
BaseRef string `json:"base_ref"`
Token string `json:"token"`
Workspace string `json:"workspace"`
Action string `json:"action"`
ActionPath string `json:"action_path"`
ActionRef string `json:"action_ref"`
ActionRepository string `json:"action_repository"`
Job string `json:"job"`
JobName string `json:"job_name"`
RepositoryOwner string `json:"repository_owner"`
RetentionDays string `json:"retention_days"`
RunnerPerflog string `json:"runner_perflog"`
RunnerTrackingID string `json:"runner_tracking_id"`
}

12
pkg/model/job_context.go Normal file
View file

@ -0,0 +1,12 @@
package model
type JobContext struct {
Status string `json:"status"`
Container struct {
ID string `json:"id"`
Network string `json:"network"`
} `json:"container"`
Services map[string]struct {
ID string `json:"id"`
} `json:"services"`
}

43
pkg/model/step_result.go Normal file
View file

@ -0,0 +1,43 @@
package model
import "fmt"
type stepStatus int
const (
StepStatusSuccess stepStatus = iota
StepStatusFailure
)
var stepStatusStrings = [...]string{
"success",
"failure",
}
func (s stepStatus) MarshalText() ([]byte, error) {
return []byte(s.String()), nil
}
func (s *stepStatus) UnmarshalText(b []byte) error {
str := string(b)
for i, name := range stepStatusStrings {
if name == str {
*s = stepStatus(i)
return nil
}
}
return fmt.Errorf("invalid step status %q", str)
}
func (s stepStatus) String() string {
if int(s) >= len(stepStatusStrings) {
return ""
}
return stepStatusStrings[s]
}
type StepResult struct {
Outputs map[string]string `json:"outputs"`
Conclusion stepStatus `json:"conclusion"`
Outcome stepStatus `json:"outcome"`
}

View file

@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/common"
"github.com/nektos/act/pkg/model"
) )
func TestSetEnv(t *testing.T) { func TestSetEnv(t *testing.T) {
@ -24,11 +25,11 @@ func TestSetOutput(t *testing.T) {
a := assert.New(t) a := assert.New(t)
ctx := context.Background() ctx := context.Background()
rc := new(RunContext) rc := new(RunContext)
rc.StepResults = make(map[string]*stepResult) rc.StepResults = make(map[string]*model.StepResult)
handler := rc.commandHandler(ctx) handler := rc.commandHandler(ctx)
rc.CurrentStep = "my-step" rc.CurrentStep = "my-step"
rc.StepResults[rc.CurrentStep] = &stepResult{ rc.StepResults[rc.CurrentStep] = &model.StepResult{
Outputs: make(map[string]string), Outputs: make(map[string]string),
} }
handler("::set-output name=x::valz\n") handler("::set-output name=x::valz\n")

View file

@ -47,24 +47,24 @@ func TestEvaluate(t *testing.T) {
"os": "Linux", "os": "Linux",
"foo": "bar", "foo": "bar",
}, },
StepResults: map[string]*stepResult{ StepResults: map[string]*model.StepResult{
"idwithnothing": { "idwithnothing": {
Conclusion: stepStatusSuccess, Conclusion: model.StepStatusSuccess,
Outcome: stepStatusFailure, Outcome: model.StepStatusFailure,
Outputs: map[string]string{ Outputs: map[string]string{
"foowithnothing": "barwithnothing", "foowithnothing": "barwithnothing",
}, },
}, },
"id-with-hyphens": { "id-with-hyphens": {
Conclusion: stepStatusSuccess, Conclusion: model.StepStatusSuccess,
Outcome: stepStatusFailure, Outcome: model.StepStatusFailure,
Outputs: map[string]string{ Outputs: map[string]string{
"foo-with-hyphens": "bar-with-hyphens", "foo-with-hyphens": "bar-with-hyphens",
}, },
}, },
"id_with_underscores": { "id_with_underscores": {
Conclusion: stepStatusSuccess, Conclusion: model.StepStatusSuccess,
Outcome: stepStatusFailure, Outcome: model.StepStatusFailure,
Outputs: map[string]string{ Outputs: map[string]string{
"foo_with_underscores": "bar_with_underscores", "foo_with_underscores": "bar_with_underscores",
}, },
@ -232,6 +232,7 @@ func updateTestExpressionWorkflow(t *testing.T, tables []struct {
envs += fmt.Sprintf(" %s: %s\n", k, rc.Env[k]) envs += fmt.Sprintf(" %s: %s\n", k, rc.Env[k])
} }
// editorconfig-checker-disable
workflow := fmt.Sprintf(` workflow := fmt.Sprintf(`
name: "Test how expressions are handled on GitHub" name: "Test how expressions are handled on GitHub"
on: push on: push
@ -244,6 +245,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
`, envs) `, envs)
// editorconfig-checker-enable
for _, table := range tables { for _, table := range tables {
expressionPattern = regexp.MustCompile(`\${{\s*(.+?)\s*}}`) expressionPattern = regexp.MustCompile(`\${{\s*(.+?)\s*}}`)

View file

@ -35,7 +35,7 @@ type RunContext struct {
Env map[string]string Env map[string]string
ExtraPath []string ExtraPath []string
CurrentStep string CurrentStep string
StepResults map[string]*stepResult StepResults map[string]*model.StepResult
ExprEval ExpressionEvaluator ExprEval ExpressionEvaluator
JobContainer container.Container JobContainer container.Container
OutputMappings map[MappableOutput]MappableOutput OutputMappings map[MappableOutput]MappableOutput
@ -53,7 +53,7 @@ func (rc *RunContext) Clone() *RunContext {
clone.CurrentStep = "" clone.CurrentStep = ""
clone.Composite = nil clone.Composite = nil
clone.Inputs = nil clone.Inputs = nil
clone.StepResults = make(map[string]*stepResult) clone.StepResults = make(map[string]*model.StepResult)
clone.Parent = rc clone.Parent = rc
return &clone return &clone
} }
@ -67,46 +67,6 @@ func (rc *RunContext) String() string {
return fmt.Sprintf("%s/%s", rc.Run.Workflow.Name, rc.Name) return fmt.Sprintf("%s/%s", rc.Run.Workflow.Name, rc.Name)
} }
type stepStatus int
const (
stepStatusSuccess stepStatus = iota
stepStatusFailure
)
var stepStatusStrings = [...]string{
"success",
"failure",
}
func (s stepStatus) MarshalText() ([]byte, error) {
return []byte(s.String()), nil
}
func (s *stepStatus) UnmarshalText(b []byte) error {
str := string(b)
for i, name := range stepStatusStrings {
if name == str {
*s = stepStatus(i)
return nil
}
}
return fmt.Errorf("invalid step status %q", str)
}
func (s stepStatus) String() string {
if int(s) >= len(stepStatusStrings) {
return ""
}
return stepStatusStrings[s]
}
type stepResult struct {
Outputs map[string]string `json:"outputs"`
Conclusion stepStatus `json:"conclusion"`
Outcome stepStatus `json:"outcome"`
}
// GetEnv returns the env for the context // GetEnv returns the env for the context
func (rc *RunContext) GetEnv() map[string]string { func (rc *RunContext) GetEnv() map[string]string {
if rc.Env == nil { if rc.Env == nil {
@ -349,16 +309,16 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor {
} }
return func(ctx context.Context) error { return func(ctx context.Context) error {
rc.CurrentStep = sc.Step.ID rc.CurrentStep = sc.Step.ID
rc.StepResults[rc.CurrentStep] = &stepResult{ rc.StepResults[rc.CurrentStep] = &model.StepResult{
Outcome: stepStatusSuccess, Outcome: model.StepStatusSuccess,
Conclusion: stepStatusSuccess, Conclusion: model.StepStatusSuccess,
Outputs: make(map[string]string), Outputs: make(map[string]string),
} }
runStep, err := sc.isEnabled(ctx) runStep, err := sc.isEnabled(ctx)
if err != nil { if err != nil {
rc.StepResults[rc.CurrentStep].Conclusion = stepStatusFailure rc.StepResults[rc.CurrentStep].Conclusion = model.StepStatusFailure
rc.StepResults[rc.CurrentStep].Outcome = stepStatusFailure rc.StepResults[rc.CurrentStep].Outcome = model.StepStatusFailure
return err return err
} }
@ -380,13 +340,13 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor {
} else { } else {
common.Logger(ctx).Errorf(" \u274C Failure - %s", sc.Step) common.Logger(ctx).Errorf(" \u274C Failure - %s", sc.Step)
rc.StepResults[rc.CurrentStep].Outcome = stepStatusFailure rc.StepResults[rc.CurrentStep].Outcome = model.StepStatusFailure
if sc.Step.ContinueOnError { if sc.Step.ContinueOnError {
common.Logger(ctx).Infof("Failed but continue next step") common.Logger(ctx).Infof("Failed but continue next step")
err = nil err = nil
rc.StepResults[rc.CurrentStep].Conclusion = stepStatusSuccess rc.StepResults[rc.CurrentStep].Conclusion = model.StepStatusSuccess
} else { } else {
rc.StepResults[rc.CurrentStep].Conclusion = stepStatusFailure rc.StepResults[rc.CurrentStep].Conclusion = model.StepStatusFailure
} }
} }
return err return err
@ -522,31 +482,20 @@ func trimToLen(s string, l int) string {
return s return s
} }
type jobContext struct { func (rc *RunContext) getJobContext() *model.JobContext {
Status string `json:"status"`
Container struct {
ID string `json:"id"`
Network string `json:"network"`
} `json:"container"`
Services map[string]struct {
ID string `json:"id"`
} `json:"services"`
}
func (rc *RunContext) getJobContext() *jobContext {
jobStatus := "success" jobStatus := "success"
for _, stepStatus := range rc.StepResults { for _, stepStatus := range rc.StepResults {
if stepStatus.Conclusion == stepStatusFailure { if stepStatus.Conclusion == model.StepStatusFailure {
jobStatus = "failure" jobStatus = "failure"
break break
} }
} }
return &jobContext{ return &model.JobContext{
Status: jobStatus, Status: jobStatus,
} }
} }
func (rc *RunContext) getStepsContext() map[string]*stepResult { func (rc *RunContext) getStepsContext() map[string]*model.StepResult {
return rc.StepResults return rc.StepResults
} }
@ -561,35 +510,8 @@ func (rc *RunContext) getNeedsTransitive(job *model.Job) []string {
return needs return needs
} }
type githubContext struct { func (rc *RunContext) getGithubContext() *model.GithubContext {
Event map[string]interface{} `json:"event"` ghc := &model.GithubContext{
EventPath string `json:"event_path"`
Workflow string `json:"workflow"`
RunID string `json:"run_id"`
RunNumber string `json:"run_number"`
Actor string `json:"actor"`
Repository string `json:"repository"`
EventName string `json:"event_name"`
Sha string `json:"sha"`
Ref string `json:"ref"`
HeadRef string `json:"head_ref"`
BaseRef string `json:"base_ref"`
Token string `json:"token"`
Workspace string `json:"workspace"`
Action string `json:"action"`
ActionPath string `json:"action_path"`
ActionRef string `json:"action_ref"`
ActionRepository string `json:"action_repository"`
Job string `json:"job"`
JobName string `json:"job_name"`
RepositoryOwner string `json:"repository_owner"`
RetentionDays string `json:"retention_days"`
RunnerPerflog string `json:"runner_perflog"`
RunnerTrackingID string `json:"runner_tracking_id"`
}
func (rc *RunContext) getGithubContext() *githubContext {
ghc := &githubContext{
Event: make(map[string]interface{}), Event: make(map[string]interface{}),
EventPath: ActPath + "/workflow/event.json", EventPath: ActPath + "/workflow/event.json",
Workflow: rc.Run.Workflow.Name, Workflow: rc.Run.Workflow.Name,
@ -685,7 +607,7 @@ func (rc *RunContext) getGithubContext() *githubContext {
return ghc return ghc
} }
func (ghc *githubContext) isLocalCheckout(step *model.Step) bool { func isLocalCheckout(ghc *model.GithubContext, step *model.Step) bool {
if step.Type() == model.StepTypeInvalid { if step.Type() == model.StepTypeInvalid {
// This will be errored out by the executor later, we need this here to avoid a null panic though // This will be errored out by the executor later, we need this here to avoid a null panic though
return false return false
@ -839,7 +761,7 @@ func setActionRuntimeVars(rc *RunContext, env map[string]string) {
func (rc *RunContext) localCheckoutPath() (string, bool) { func (rc *RunContext) localCheckoutPath() (string, bool) {
ghContext := rc.getGithubContext() ghContext := rc.getGithubContext()
for _, step := range rc.Run.Job().Steps { for _, step := range rc.Run.Job().Steps {
if ghContext.isLocalCheckout(step) { if isLocalCheckout(ghContext, step) {
return step.With["path"], true return step.With["path"], true
} }
} }

View file

@ -53,10 +53,10 @@ func TestRunContext_EvalBool(t *testing.T) {
"os": "Linux", "os": "Linux",
"foo": "bar", "foo": "bar",
}, },
StepResults: map[string]*stepResult{ StepResults: map[string]*model.StepResult{
"id1": { "id1": {
Conclusion: stepStatusSuccess, Conclusion: model.StepStatusSuccess,
Outcome: stepStatusFailure, Outcome: model.StepStatusFailure,
Outputs: map[string]string{ Outputs: map[string]string{
"foo": "bar", "foo": "bar",
}, },
@ -312,7 +312,7 @@ func TestGetGitHubContext(t *testing.T) {
Matrix: map[string]interface{}{}, Matrix: map[string]interface{}{},
Env: map[string]string{}, Env: map[string]string{},
ExtraPath: []string{}, ExtraPath: []string{},
StepResults: map[string]*stepResult{}, StepResults: map[string]*model.StepResult{},
OutputMappings: map[MappableOutput]MappableOutput{}, OutputMappings: map[MappableOutput]MappableOutput{},
} }

View file

@ -188,7 +188,7 @@ func (runner *runnerImpl) newRunContext(run *model.Run, matrix map[string]interf
Config: runner.config, Config: runner.config,
Run: run, Run: run,
EventJSON: runner.eventJSON, EventJSON: runner.eventJSON,
StepResults: make(map[string]*stepResult), StepResults: make(map[string]*model.StepResult),
Matrix: matrix, Matrix: matrix,
} }
rc.ExprEval = rc.NewExpressionEvaluator() rc.ExprEval = rc.NewExpressionEvaluator()

View file

@ -77,7 +77,7 @@ func (sc *StepContext) Executor(ctx context.Context) common.Executor {
remoteAction.URL = rc.Config.GitHubInstance remoteAction.URL = rc.Config.GitHubInstance
github := rc.getGithubContext() github := rc.getGithubContext()
if remoteAction.IsCheckout() && github.isLocalCheckout(step) { if remoteAction.IsCheckout() && isLocalCheckout(github, step) {
return func(ctx context.Context) error { return func(ctx context.Context) error {
common.Logger(ctx).Debugf("Skipping local actions/checkout because workdir was already copied") common.Logger(ctx).Debugf("Skipping local actions/checkout because workdir was already copied")
return nil return nil

View file

@ -46,7 +46,7 @@ func createIfTestStepContext(t *testing.T, input string) *StepContext {
"ubuntu-latest": "ubuntu-latest", "ubuntu-latest": "ubuntu-latest",
}, },
}, },
StepResults: map[string]*stepResult{}, StepResults: map[string]*model.StepResult{},
Env: map[string]string{}, Env: map[string]string{},
Run: &model.Run{ Run: &model.Run{
JobID: "job1", JobID: "job1",
@ -71,14 +71,14 @@ func TestStepContextIsEnabled(t *testing.T) {
assertObject.True(sc.isEnabled(context.Background())) assertObject.True(sc.isEnabled(context.Background()))
sc = createIfTestStepContext(t, "if: success()") sc = createIfTestStepContext(t, "if: success()")
sc.RunContext.StepResults["a"] = &stepResult{ sc.RunContext.StepResults["a"] = &model.StepResult{
Conclusion: stepStatusSuccess, Conclusion: model.StepStatusSuccess,
} }
assertObject.True(sc.isEnabled(context.Background())) assertObject.True(sc.isEnabled(context.Background()))
sc = createIfTestStepContext(t, "if: success()") sc = createIfTestStepContext(t, "if: success()")
sc.RunContext.StepResults["a"] = &stepResult{ sc.RunContext.StepResults["a"] = &model.StepResult{
Conclusion: stepStatusFailure, Conclusion: model.StepStatusFailure,
} }
assertObject.False(sc.isEnabled(context.Background())) assertObject.False(sc.isEnabled(context.Background()))
@ -87,14 +87,14 @@ func TestStepContextIsEnabled(t *testing.T) {
assertObject.False(sc.isEnabled(context.Background())) assertObject.False(sc.isEnabled(context.Background()))
sc = createIfTestStepContext(t, "if: failure()") sc = createIfTestStepContext(t, "if: failure()")
sc.RunContext.StepResults["a"] = &stepResult{ sc.RunContext.StepResults["a"] = &model.StepResult{
Conclusion: stepStatusSuccess, Conclusion: model.StepStatusSuccess,
} }
assertObject.False(sc.isEnabled(context.Background())) assertObject.False(sc.isEnabled(context.Background()))
sc = createIfTestStepContext(t, "if: failure()") sc = createIfTestStepContext(t, "if: failure()")
sc.RunContext.StepResults["a"] = &stepResult{ sc.RunContext.StepResults["a"] = &model.StepResult{
Conclusion: stepStatusFailure, Conclusion: model.StepStatusFailure,
} }
assertObject.True(sc.isEnabled(context.Background())) assertObject.True(sc.isEnabled(context.Background()))
@ -103,14 +103,14 @@ func TestStepContextIsEnabled(t *testing.T) {
assertObject.True(sc.isEnabled(context.Background())) assertObject.True(sc.isEnabled(context.Background()))
sc = createIfTestStepContext(t, "if: always()") sc = createIfTestStepContext(t, "if: always()")
sc.RunContext.StepResults["a"] = &stepResult{ sc.RunContext.StepResults["a"] = &model.StepResult{
Conclusion: stepStatusSuccess, Conclusion: model.StepStatusSuccess,
} }
assertObject.True(sc.isEnabled(context.Background())) assertObject.True(sc.isEnabled(context.Background()))
sc = createIfTestStepContext(t, "if: always()") sc = createIfTestStepContext(t, "if: always()")
sc.RunContext.StepResults["a"] = &stepResult{ sc.RunContext.StepResults["a"] = &model.StepResult{
Conclusion: stepStatusFailure, Conclusion: model.StepStatusFailure,
} }
assertObject.True(sc.isEnabled(context.Background())) assertObject.True(sc.isEnabled(context.Background()))
} }