mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-03-20 03:46:09 +08:00
feat: allow configuring gitea schema mode (#23)
* config entries for schema change * remove broken/unused syntetic nodejs action * specify desired schema in model struct that is read by unmarshal * replace params by config * allows gitea context + env names * act --gitea now parses a subset of gitea specific workflows Reviewed-on: https://gitea.com/actions-oss/act-cli/pulls/23 Co-authored-by: Christopher Homberger <christopher.homberger@web.de> Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
This commit is contained in:
committed by
ChristopherHX
parent
faa252c8e9
commit
933c4a5bd5
@@ -66,6 +66,7 @@ type Input struct {
|
||||
validate bool
|
||||
strict bool
|
||||
parallel int
|
||||
gitea bool
|
||||
}
|
||||
|
||||
func (i *Input) resolve(path string) string {
|
||||
|
||||
22
cmd/root.go
22
cmd/root.go
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/actions-oss/act-cli/pkg/gh"
|
||||
"github.com/actions-oss/act-cli/pkg/model"
|
||||
"github.com/actions-oss/act-cli/pkg/runner"
|
||||
"github.com/actions-oss/act-cli/pkg/schema"
|
||||
)
|
||||
|
||||
type Flag struct {
|
||||
@@ -131,6 +132,7 @@ func createRootCommand(ctx context.Context, input *Input, version string) *cobra
|
||||
rootCmd.PersistentFlags().StringVarP(&input.networkName, "network", "", "host", "Sets a docker network name. Defaults to host.")
|
||||
rootCmd.PersistentFlags().StringArrayVarP(&input.localRepository, "local-repository", "", []string{}, "Replaces the specified repository and ref with a local folder (e.g. https://github.com/test/test@v0=/home/act/test or test/test@v0=/home/act/test, the latter matches any hosts or protocols)")
|
||||
rootCmd.PersistentFlags().BoolVar(&input.listOptions, "list-options", false, "Print a json structure of compatible options")
|
||||
rootCmd.PersistentFlags().BoolVar(&input.gitea, "gitea", false, "Use Gitea instead of GitHub")
|
||||
rootCmd.SetArgs(args())
|
||||
return rootCmd
|
||||
}
|
||||
@@ -453,7 +455,18 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
|
||||
matrixes := parseMatrix(input.matrix)
|
||||
log.Debugf("Evaluated matrix inclusions: %v", matrixes)
|
||||
|
||||
planner, err := model.NewWorkflowPlanner(input.WorkflowsPath(), !input.workflowRecurse, input.strict)
|
||||
// TODO switch to Gitea Schema when supported
|
||||
plannerConfig := model.PlannerConfig{
|
||||
Recursive: input.workflowRecurse,
|
||||
Workflow: model.WorkflowConfig{
|
||||
Strict: input.strict,
|
||||
},
|
||||
}
|
||||
if input.gitea {
|
||||
plannerConfig.Workflow.Schema = schema.GetGiteaWorkflowSchema()
|
||||
}
|
||||
|
||||
planner, err := model.NewWorkflowPlanner(input.WorkflowsPath(), plannerConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -653,6 +666,13 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
|
||||
Matrix: matrixes,
|
||||
ContainerNetworkMode: docker_container.NetworkMode(input.networkName),
|
||||
Parallel: input.parallel,
|
||||
Planner: plannerConfig,
|
||||
Action: model.ActionConfig{}, // TODO Gitea Action Schema
|
||||
MainContextNames: []string{"github"},
|
||||
}
|
||||
if input.gitea {
|
||||
config.Action.Schema = schema.GetGiteaActionSchema()
|
||||
config.MainContextNames = append(config.MainContextNames, "gitea")
|
||||
}
|
||||
actionCache := runner.GoGitActionCache{
|
||||
Path: config.ActionCacheDir,
|
||||
|
||||
@@ -34,6 +34,7 @@ type ReadOnlyArray[T any] interface {
|
||||
|
||||
type ReadOnlyObject[T any] interface {
|
||||
Get(key string) T
|
||||
GetKv(key string) (string, T) // Returns the actual key used (for case-insensitive objects)
|
||||
GetEnumerator() map[string]T
|
||||
}
|
||||
|
||||
@@ -54,13 +55,18 @@ func (a BasicArray[T]) GetEnumerator() []T {
|
||||
type CaseInsensitiveObject[T any] map[string]T
|
||||
|
||||
func (o CaseInsensitiveObject[T]) Get(key string) T {
|
||||
_, v := o.GetKv(key)
|
||||
return v
|
||||
}
|
||||
|
||||
func (o CaseInsensitiveObject[T]) GetKv(key string) (k string, v T) {
|
||||
for k, v := range o {
|
||||
if strings.EqualFold(k, key) {
|
||||
return v
|
||||
return k, v
|
||||
}
|
||||
}
|
||||
var zero T
|
||||
return zero
|
||||
return key, zero
|
||||
}
|
||||
|
||||
func (o CaseInsensitiveObject[T]) GetEnumerator() map[string]T {
|
||||
@@ -73,6 +79,10 @@ func (o CaseSensitiveObject[T]) Get(key string) T {
|
||||
return o[key]
|
||||
}
|
||||
|
||||
func (o CaseSensitiveObject[T]) GetKv(key string) (string, T) {
|
||||
return key, o[key]
|
||||
}
|
||||
|
||||
func (o CaseSensitiveObject[T]) GetEnumerator() map[string]T {
|
||||
return o
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ func runTestJobFile(ctx context.Context, t *testing.T, tjfi TestJobFileInfo) {
|
||||
runner, err := runner.New(runnerConfig)
|
||||
assert.Nil(t, err, tjfi.workflowPath)
|
||||
|
||||
planner, err := model.NewWorkflowPlanner(fullWorkflowPath, true, false)
|
||||
planner, err := model.NewWorkflowPlanner(fullWorkflowPath, model.PlannerConfig{})
|
||||
assert.Nil(t, err, fullWorkflowPath)
|
||||
|
||||
plan, err := planner.PlanEvent(tjfi.eventName)
|
||||
|
||||
@@ -38,9 +38,10 @@ type Needs struct {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Run *model.Run
|
||||
WorkingDir string
|
||||
Context string
|
||||
Run *model.Run
|
||||
WorkingDir string
|
||||
Context string
|
||||
MainContextNames []string // e.g. "github", "gitea", "forgejo"
|
||||
}
|
||||
|
||||
type DefaultStatusCheck int
|
||||
@@ -253,7 +254,8 @@ func (impl *interperterImpl) GetFunctions() eval.CaseInsensitiveObject[eval.Func
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) GetVariables() eval.ReadOnlyObject[any] {
|
||||
githubCtx := toRawObj(reflect.ValueOf(impl.env.Github))
|
||||
mainCtx := eval.CaseInsensitiveObject[any](toRawObj(reflect.ValueOf(impl.env.Github)))
|
||||
|
||||
var env any
|
||||
if impl.env.EnvCS {
|
||||
env = eval.CaseSensitiveObject[any](toRawObj(reflect.ValueOf(impl.env.Env)))
|
||||
@@ -261,7 +263,6 @@ func (impl *interperterImpl) GetVariables() eval.ReadOnlyObject[any] {
|
||||
env = eval.CaseInsensitiveObject[any](toRawObj(reflect.ValueOf(impl.env.Env)))
|
||||
}
|
||||
vars := eval.CaseInsensitiveObject[any]{
|
||||
"github": githubCtx,
|
||||
"env": env,
|
||||
"vars": toRawObj(reflect.ValueOf(impl.env.Vars)),
|
||||
"steps": toRawObj(reflect.ValueOf(impl.env.Steps)),
|
||||
@@ -274,17 +275,28 @@ func (impl *interperterImpl) GetVariables() eval.ReadOnlyObject[any] {
|
||||
"jobs": toRawObj(reflect.ValueOf(impl.env.Jobs)),
|
||||
"inputs": toRawObj(reflect.ValueOf(impl.env.Inputs)),
|
||||
}
|
||||
ctxNames := impl.config.MainContextNames
|
||||
if len(ctxNames) == 0 {
|
||||
ctxNames = []string{"github"}
|
||||
}
|
||||
for _, cn := range ctxNames {
|
||||
vars[cn] = mainCtx
|
||||
}
|
||||
for name, cd := range impl.env.CtxData {
|
||||
lowerName := strings.ToLower(name)
|
||||
if serverPayload, ok := cd.(map[string]interface{}); ok {
|
||||
if lowerName == "github" {
|
||||
for k, v := range serverPayload {
|
||||
// skip empty values, because github.workspace was set by Gitea Actions to an empty string
|
||||
if _, ok := githubCtx[k]; !ok || v != "" && v != nil {
|
||||
githubCtx[k] = v
|
||||
if rawOtherCtx := vars.Get(name); rawOtherCtx != nil {
|
||||
res := eval.CreateIntermediateResult(eval.NewEvaluationContext(), rawOtherCtx)
|
||||
if rOtherCd, ok := res.TryGetCollectionInterface(); ok {
|
||||
if otherCd, ok := rOtherCd.(eval.ReadOnlyObject[any]); ok {
|
||||
if rawPayload, ok := cd.(map[string]interface{}); ok {
|
||||
for k, v := range rawPayload {
|
||||
// skip empty values, because github.workspace was set by Gitea Actions to an empty string
|
||||
if mk, _ := otherCd.GetKv(k); v != "" && v != nil {
|
||||
otherCd.GetEnumerator()[mk] = v
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
vars[name] = cd
|
||||
|
||||
@@ -83,13 +83,29 @@ type Action struct {
|
||||
Color string `yaml:"color"`
|
||||
Icon string `yaml:"icon"`
|
||||
} `yaml:"branding"`
|
||||
Definition string `yaml:"-"`
|
||||
Schema *schema.Schema `yaml:"-"`
|
||||
}
|
||||
|
||||
func (a *Action) GetDefinition() string {
|
||||
if a.Definition == "" {
|
||||
return "action-root"
|
||||
}
|
||||
return a.Definition
|
||||
}
|
||||
|
||||
func (a *Action) GetSchema() *schema.Schema {
|
||||
if a.Schema == nil {
|
||||
return schema.GetActionSchema()
|
||||
}
|
||||
return a.Schema
|
||||
}
|
||||
|
||||
func (a *Action) UnmarshalYAML(node *yaml.Node) error {
|
||||
// Validate the schema before deserializing it into our model
|
||||
if err := (&schema.Node{
|
||||
Definition: "action-root",
|
||||
Schema: schema.GetActionSchema(),
|
||||
Definition: a.GetDefinition(),
|
||||
Schema: a.GetSchema(),
|
||||
}).UnmarshalYAML(node); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -110,9 +126,16 @@ type Output struct {
|
||||
Value string `yaml:"value"`
|
||||
}
|
||||
|
||||
type ActionConfig struct {
|
||||
Definition string
|
||||
Schema *schema.Schema
|
||||
}
|
||||
|
||||
// ReadAction reads an action from a reader
|
||||
func ReadAction(in io.Reader) (*Action, error) {
|
||||
func ReadAction(in io.Reader, config ActionConfig) (*Action, error) {
|
||||
a := new(Action)
|
||||
a.Schema = config.Schema
|
||||
a.Definition = config.Definition
|
||||
err := yaml.NewDecoder(in).Decode(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -55,8 +55,13 @@ type WorkflowFiles struct {
|
||||
dirPath string
|
||||
}
|
||||
|
||||
type PlannerConfig struct {
|
||||
Recursive bool
|
||||
Workflow WorkflowConfig
|
||||
}
|
||||
|
||||
// NewWorkflowPlanner will load a specific workflow, all workflows from a directory or all workflows from a directory and its subdirectories
|
||||
func NewWorkflowPlanner(path string, noWorkflowRecurse, strict bool) (WorkflowPlanner, error) {
|
||||
func NewWorkflowPlanner(path string, config PlannerConfig) (WorkflowPlanner, error) {
|
||||
path, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -71,7 +76,7 @@ func NewWorkflowPlanner(path string, noWorkflowRecurse, strict bool) (WorkflowPl
|
||||
|
||||
if fi.IsDir() {
|
||||
log.Debugf("Loading workflows from '%s'", path)
|
||||
if noWorkflowRecurse {
|
||||
if !config.Recursive {
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -124,7 +129,7 @@ func NewWorkflowPlanner(path string, noWorkflowRecurse, strict bool) (WorkflowPl
|
||||
}
|
||||
|
||||
log.Debugf("Reading workflow '%s'", f.Name())
|
||||
workflow, err := ReadWorkflow(f, strict)
|
||||
workflow, err := ReadWorkflow(f, config.Workflow)
|
||||
if err != nil {
|
||||
_ = f.Close()
|
||||
if err == io.EOF {
|
||||
@@ -157,11 +162,11 @@ func NewWorkflowPlanner(path string, noWorkflowRecurse, strict bool) (WorkflowPl
|
||||
return wp, nil
|
||||
}
|
||||
|
||||
func NewSingleWorkflowPlanner(name string, f io.Reader) (WorkflowPlanner, error) {
|
||||
func NewSingleWorkflowPlanner(name string, f io.Reader, config PlannerConfig) (WorkflowPlanner, error) {
|
||||
wp := new(workflowPlanner)
|
||||
|
||||
log.Debugf("Reading workflow %s", name)
|
||||
workflow, err := ReadWorkflow(f, false)
|
||||
workflow, err := ReadWorkflow(f, config.Workflow)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil, fmt.Errorf("unable to read workflow '%s': file is empty: %w", name, err)
|
||||
|
||||
@@ -31,7 +31,9 @@ func TestPlanner(t *testing.T) {
|
||||
assert.NoError(t, err, workdir)
|
||||
for _, table := range tables {
|
||||
fullWorkflowPath := filepath.Join(workdir, table.workflowPath)
|
||||
_, err = NewWorkflowPlanner(fullWorkflowPath, table.noWorkflowRecurse, false)
|
||||
_, err = NewWorkflowPlanner(fullWorkflowPath, PlannerConfig{
|
||||
Recursive: !table.noWorkflowRecurse,
|
||||
})
|
||||
if table.errorMessage == "" {
|
||||
assert.NoError(t, err, "WorkflowPlanner should exit without any error")
|
||||
} else {
|
||||
|
||||
@@ -17,12 +17,30 @@ import (
|
||||
|
||||
// Workflow is the structure of the files in .github/workflows
|
||||
type Workflow struct {
|
||||
File string
|
||||
Name string `yaml:"name"`
|
||||
RawOn yaml.Node `yaml:"on"`
|
||||
Env map[string]string `yaml:"env"`
|
||||
Jobs map[string]*Job `yaml:"jobs"`
|
||||
Defaults Defaults `yaml:"defaults"`
|
||||
File string
|
||||
Name string `yaml:"name"`
|
||||
RawOn yaml.Node `yaml:"on"`
|
||||
Env map[string]string `yaml:"env"`
|
||||
Jobs map[string]*Job `yaml:"jobs"`
|
||||
Defaults Defaults `yaml:"defaults"`
|
||||
Schema *schema.Schema `yaml:"-"`
|
||||
Definition string `yaml:"-"`
|
||||
}
|
||||
|
||||
// GetDefinition gets the schema definition name for the workflow
|
||||
func (w *Workflow) GetDefinition() string {
|
||||
if w.Definition == "" {
|
||||
return "workflow-root"
|
||||
}
|
||||
return w.Definition
|
||||
}
|
||||
|
||||
// GetSchema gets the schema for the workflow
|
||||
func (w *Workflow) GetSchema() *schema.Schema {
|
||||
if w.Schema == nil {
|
||||
return schema.GetWorkflowSchema()
|
||||
}
|
||||
return w.Schema
|
||||
}
|
||||
|
||||
// On events for the workflow
|
||||
@@ -74,27 +92,10 @@ func (w *Workflow) UnmarshalYAML(node *yaml.Node) error {
|
||||
}
|
||||
// Validate the schema before deserializing it into our model
|
||||
if err := (&schema.Node{
|
||||
Definition: "workflow-root",
|
||||
Schema: schema.GetWorkflowSchema(),
|
||||
Definition: w.GetDefinition(),
|
||||
Schema: w.GetSchema(),
|
||||
}).UnmarshalYAML(node); err != nil {
|
||||
return errors.Join(err, fmt.Errorf("actions YAML Schema Validation Error detected:\nFor more information, see: https://actions-oss.github.io/act-docs/usage/schema.html"))
|
||||
}
|
||||
type WorkflowDefault Workflow
|
||||
return node.Decode((*WorkflowDefault)(w))
|
||||
}
|
||||
|
||||
type WorkflowStrict Workflow
|
||||
|
||||
func (w *WorkflowStrict) UnmarshalYAML(node *yaml.Node) error {
|
||||
if err := resolveAliases(node); err != nil {
|
||||
return err
|
||||
}
|
||||
// Validate the schema before deserializing it into our model
|
||||
if err := (&schema.Node{
|
||||
Definition: "workflow-root-strict",
|
||||
Schema: schema.GetWorkflowSchema(),
|
||||
}).UnmarshalYAML(node); err != nil {
|
||||
return errors.Join(err, fmt.Errorf("actions YAML Strict Schema Validation Error detected:\nFor more information, see: https://nektosact.com/usage/schema.html"))
|
||||
return errors.Join(err, fmt.Errorf("actions YAML Schema Validation Error of definition '%s' detected:\nFor more information, see: https://actions-oss.github.io/act-docs/usage/schema.html", w.GetDefinition()))
|
||||
}
|
||||
type WorkflowDefault Workflow
|
||||
return node.Decode((*WorkflowDefault)(w))
|
||||
@@ -708,14 +709,20 @@ func (s *Step) Type() StepType {
|
||||
return StepTypeUsesActionRemote
|
||||
}
|
||||
|
||||
type WorkflowConfig struct {
|
||||
Definition string
|
||||
Schema *schema.Schema
|
||||
Strict bool
|
||||
}
|
||||
|
||||
// ReadWorkflow returns a list of jobs for a given workflow file reader
|
||||
func ReadWorkflow(in io.Reader, strict bool) (*Workflow, error) {
|
||||
if strict {
|
||||
w := new(WorkflowStrict)
|
||||
err := yaml.NewDecoder(in).Decode(w)
|
||||
return (*Workflow)(w), err
|
||||
}
|
||||
func ReadWorkflow(in io.Reader, config WorkflowConfig) (*Workflow, error) {
|
||||
w := new(Workflow)
|
||||
w.Schema = config.Schema
|
||||
w.Definition = config.Definition
|
||||
if config.Strict {
|
||||
w.Definition = "workflow-root-strict"
|
||||
}
|
||||
err := yaml.NewDecoder(in).Decode(w)
|
||||
return w, err
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- uses: ./actions/docker-url
|
||||
`
|
||||
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
|
||||
assert.Len(t, workflow.On(), 1)
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
- uses: ./actions/docker-url
|
||||
`
|
||||
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
|
||||
assert.Len(t, workflow.On(), 2)
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
- uses: ./actions/docker-url
|
||||
`
|
||||
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
assert.Len(t, workflow.On(), 2)
|
||||
assert.Contains(t, workflow.On(), "push")
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
steps:
|
||||
- uses: ./actions/docker-url`
|
||||
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
assert.Equal(t, workflow.Jobs["test"].RunsOn(), []string{"ubuntu-latest"})
|
||||
}
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
steps:
|
||||
- uses: ./actions/docker-url`
|
||||
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
assert.Equal(t, workflow.Jobs["test"].RunsOn(), []string{"ubuntu-latest", "linux"})
|
||||
}
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
- uses: ./actions/docker-url
|
||||
`
|
||||
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
assert.Len(t, workflow.Jobs, 2)
|
||||
assert.Contains(t, workflow.Jobs["test"].Container().Image, "nginx:latest")
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
- uses: ./actions/docker-url
|
||||
`
|
||||
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
assert.Len(t, workflow.Jobs, 1)
|
||||
|
||||
@@ -196,7 +196,7 @@ jobs:
|
||||
uses: ./some/path/to/workflow.yaml
|
||||
`
|
||||
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
assert.Len(t, workflow.Jobs, 6)
|
||||
|
||||
@@ -240,7 +240,7 @@ jobs:
|
||||
uses: some/path/to/workflow.yaml
|
||||
`
|
||||
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
assert.Len(t, workflow.Jobs, 4)
|
||||
|
||||
@@ -282,7 +282,7 @@ jobs:
|
||||
uses: ./local-action
|
||||
`
|
||||
|
||||
_, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
_, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.Error(t, err, "read workflow should fail")
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ jobs:
|
||||
echo "${{ needs.test1.outputs.some-b-key }}"
|
||||
`
|
||||
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
assert.Len(t, workflow.Jobs, 2)
|
||||
|
||||
@@ -329,7 +329,7 @@ jobs:
|
||||
}
|
||||
|
||||
func TestReadWorkflow_Strategy(t *testing.T) {
|
||||
w, err := NewWorkflowPlanner("testdata/strategy/push.yml", true, false)
|
||||
w, err := NewWorkflowPlanner("testdata/strategy/push.yml", PlannerConfig{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
p, err := w.PlanJob("strategy-only-max-parallel")
|
||||
@@ -451,7 +451,7 @@ func TestReadWorkflow_WorkflowDispatchConfig(t *testing.T) {
|
||||
steps:
|
||||
- run: echo Test
|
||||
`
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
workflowDispatch := workflow.WorkflowDispatchConfig()
|
||||
assert.Nil(t, workflowDispatch)
|
||||
@@ -465,7 +465,7 @@ func TestReadWorkflow_WorkflowDispatchConfig(t *testing.T) {
|
||||
steps:
|
||||
- run: echo Test
|
||||
`
|
||||
workflow, err = ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err = ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
workflowDispatch = workflow.WorkflowDispatchConfig()
|
||||
assert.NotNil(t, workflowDispatch)
|
||||
@@ -480,7 +480,7 @@ func TestReadWorkflow_WorkflowDispatchConfig(t *testing.T) {
|
||||
steps:
|
||||
- run: echo Test
|
||||
`
|
||||
workflow, err = ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err = ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
workflowDispatch = workflow.WorkflowDispatchConfig()
|
||||
assert.Nil(t, workflowDispatch)
|
||||
@@ -494,7 +494,7 @@ func TestReadWorkflow_WorkflowDispatchConfig(t *testing.T) {
|
||||
steps:
|
||||
- run: echo Test
|
||||
`
|
||||
workflow, err = ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err = ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
workflowDispatch = workflow.WorkflowDispatchConfig()
|
||||
assert.NotNil(t, workflowDispatch)
|
||||
@@ -511,7 +511,7 @@ func TestReadWorkflow_WorkflowDispatchConfig(t *testing.T) {
|
||||
steps:
|
||||
- run: echo Test
|
||||
`
|
||||
workflow, err = ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err = ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
workflowDispatch = workflow.WorkflowDispatchConfig()
|
||||
assert.NotNil(t, workflowDispatch)
|
||||
@@ -528,7 +528,7 @@ func TestReadWorkflow_WorkflowDispatchConfig(t *testing.T) {
|
||||
steps:
|
||||
- run: echo Test
|
||||
`
|
||||
workflow, err = ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err = ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
workflowDispatch = workflow.WorkflowDispatchConfig()
|
||||
assert.Nil(t, workflowDispatch)
|
||||
@@ -555,7 +555,7 @@ func TestReadWorkflow_WorkflowDispatchConfig(t *testing.T) {
|
||||
steps:
|
||||
- run: echo Test
|
||||
`
|
||||
workflow, err = ReadWorkflow(strings.NewReader(yaml), false)
|
||||
workflow, err = ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
workflowDispatch = workflow.WorkflowDispatchConfig()
|
||||
assert.NotNil(t, workflowDispatch)
|
||||
@@ -584,7 +584,7 @@ jobs:
|
||||
- uses: ./actions/docker-url
|
||||
`
|
||||
|
||||
_, err := ReadWorkflow(strings.NewReader(yaml), true)
|
||||
_, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{Strict: true})
|
||||
assert.Error(t, err, "read workflow should succeed")
|
||||
}
|
||||
|
||||
@@ -603,7 +603,7 @@ jobs:
|
||||
- uses: *checkout
|
||||
`
|
||||
|
||||
w, err := ReadWorkflow(strings.NewReader(yaml), true)
|
||||
w, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{Strict: true})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
|
||||
for _, job := range w.Jobs {
|
||||
@@ -631,7 +631,7 @@ jobs:
|
||||
on: push #*trigger
|
||||
`
|
||||
|
||||
w, err := ReadWorkflow(strings.NewReader(yaml), false)
|
||||
w, err := ReadWorkflow(strings.NewReader(yaml), WorkflowConfig{})
|
||||
assert.NoError(t, err, "read workflow should succeed")
|
||||
|
||||
for _, job := range w.Jobs {
|
||||
|
||||
@@ -2,14 +2,11 @@ package runner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -33,23 +30,18 @@ type actionStep interface {
|
||||
maybeCopyToActionDir(ctx context.Context) error
|
||||
}
|
||||
|
||||
type readAction func(ctx context.Context, step *model.Step, actionDir string, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error)
|
||||
type readAction func(ctx context.Context, step *model.Step, readFile actionYamlReader, config model.ActionConfig) (*model.Action, error)
|
||||
|
||||
type actionYamlReader func(filename string) (io.Reader, io.Closer, error)
|
||||
|
||||
type fileWriter func(filename string, data []byte, perm fs.FileMode) error
|
||||
|
||||
type runAction func(step actionStep) common.Executor
|
||||
|
||||
//go:embed res/trampoline.js
|
||||
var trampoline embed.FS
|
||||
|
||||
func readActionImpl(ctx context.Context, step *model.Step, actionDir string, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error) {
|
||||
func readActionImpl(ctx context.Context, step *model.Step, readFile actionYamlReader, config model.ActionConfig) (*model.Action, error) {
|
||||
logger := common.Logger(ctx)
|
||||
allErrors := []error{}
|
||||
addError := func(fileName string, err error) {
|
||||
if err != nil {
|
||||
allErrors = append(allErrors, fmt.Errorf("failed to read '%s' from action '%s' with path '%s' of step %w", fileName, step.String(), actionPath, err))
|
||||
allErrors = append(allErrors, fmt.Errorf("failed to read '%s' from action '%s': %w", fileName, step.String(), err))
|
||||
} else {
|
||||
// One successful read, clear error state
|
||||
allErrors = nil
|
||||
@@ -75,39 +67,6 @@ func readActionImpl(ctx context.Context, step *model.Step, actionDir string, act
|
||||
logger.Debugf("Using synthetic action %v for Dockerfile", action)
|
||||
return action, nil
|
||||
}
|
||||
if step.With != nil {
|
||||
if val, ok := step.With["args"]; ok {
|
||||
var b []byte
|
||||
if b, err = trampoline.ReadFile("res/trampoline.js"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err2 := writeFile(filepath.Join(actionDir, actionPath, "trampoline.js"), b, 0o400)
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
action := &model.Action{
|
||||
Name: "(Synthetic)",
|
||||
Inputs: map[string]model.Input{
|
||||
"cwd": {
|
||||
Description: "(Actual working directory)",
|
||||
Required: false,
|
||||
Default: filepath.Join(actionDir, actionPath),
|
||||
},
|
||||
"command": {
|
||||
Description: "(Actual program)",
|
||||
Required: false,
|
||||
Default: val,
|
||||
},
|
||||
},
|
||||
Runs: model.ActionRuns{
|
||||
Using: "node12",
|
||||
Main: "trampoline.js",
|
||||
},
|
||||
}
|
||||
logger.Debugf("Using synthetic action %v", action)
|
||||
return action, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if allErrors != nil {
|
||||
@@ -115,8 +74,8 @@ func readActionImpl(ctx context.Context, step *model.Step, actionDir string, act
|
||||
}
|
||||
defer closer.Close()
|
||||
|
||||
action, err := model.ReadAction(reader)
|
||||
logger.Debugf("Read action %v from '%s'", action, "Unknown")
|
||||
action, err := model.ReadAction(reader, config)
|
||||
logger.Debugf("Read action %v", action)
|
||||
return action, err
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ func evaluateCompositeInputAndEnv(ctx context.Context, parent *RunContext, step
|
||||
}
|
||||
}
|
||||
gh := step.getGithubContext(ctx)
|
||||
env["GITHUB_ACTION_REPOSITORY"] = gh.ActionRepository
|
||||
env["GITHUB_ACTION_REF"] = gh.ActionRef
|
||||
step.getRunContext().setMainCtxVars(env, "ACTION_REPOSITORY", gh.ActionRepository)
|
||||
step.getRunContext().setMainCtxVars(env, "ACTION_REF", gh.ActionRef)
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
@@ -79,33 +79,6 @@ runs:
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "readWithArgs",
|
||||
step: &model.Step{
|
||||
With: map[string]string{
|
||||
"args": "cmd",
|
||||
},
|
||||
},
|
||||
expected: &model.Action{
|
||||
Name: "(Synthetic)",
|
||||
Inputs: map[string]model.Input{
|
||||
"cwd": {
|
||||
Description: "(Actual working directory)",
|
||||
Required: false,
|
||||
Default: "actionDir/actionPath",
|
||||
},
|
||||
"command": {
|
||||
Description: "(Actual program)",
|
||||
Required: false,
|
||||
Default: "cmd",
|
||||
},
|
||||
},
|
||||
Runs: model.ActionRuns{
|
||||
Using: "node12",
|
||||
Main: "trampoline.js",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range table {
|
||||
@@ -120,17 +93,11 @@ runs:
|
||||
return strings.NewReader(tt.fileContent), closerMock, nil
|
||||
}
|
||||
|
||||
writeFile := func(filename string, _ []byte, perm fs.FileMode) error {
|
||||
assert.Equal(t, "actionDir/actionPath/trampoline.js", filename)
|
||||
assert.Equal(t, fs.FileMode(0400), perm)
|
||||
return nil
|
||||
}
|
||||
|
||||
if tt.filename != "" {
|
||||
closerMock.On("Close")
|
||||
}
|
||||
|
||||
action, err := readActionImpl(context.Background(), tt.step, "actionDir", "actionPath", readFile, writeFile)
|
||||
action, err := readActionImpl(context.Background(), tt.step, readFile, model.ActionConfig{})
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tt.expected, action)
|
||||
|
||||
@@ -98,9 +98,10 @@ func (rc *RunContext) NewExpressionEvaluatorWithEnv(ctx context.Context, env map
|
||||
}
|
||||
return expressionEvaluator{
|
||||
interpreter: exprparser.NewInterpeter(ee, exprparser.Config{
|
||||
Run: rc.Run,
|
||||
WorkingDir: rc.Config.Workdir,
|
||||
Context: "job",
|
||||
Run: rc.Run,
|
||||
WorkingDir: rc.Config.Workdir,
|
||||
Context: "job",
|
||||
MainContextNames: rc.Config.MainContextNames,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -164,9 +165,10 @@ func (rc *RunContext) newStepExpressionEvaluator(ctx context.Context, step step,
|
||||
}
|
||||
return expressionEvaluator{
|
||||
interpreter: exprparser.NewInterpeter(ee, exprparser.Config{
|
||||
Run: rc.Run,
|
||||
WorkingDir: rc.Config.Workdir,
|
||||
Context: "step",
|
||||
Run: rc.Run,
|
||||
WorkingDir: rc.Config.Workdir,
|
||||
Context: "step",
|
||||
MainContextNames: rc.Config.MainContextNames,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
const { spawnSync } = require('child_process')
|
||||
const spawnArguments = {
|
||||
cwd: process.env.INPUT_CWD,
|
||||
stdio: [
|
||||
process.stdin,
|
||||
process.stdout,
|
||||
process.stderr
|
||||
]
|
||||
}
|
||||
const child = spawnSync(
|
||||
'/bin/sh',
|
||||
['-c'].concat(process.env.INPUT_COMMAND),
|
||||
spawnArguments)
|
||||
process.exit(child.status)
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func newLocalReusableWorkflowExecutor(rc *RunContext) common.Executor {
|
||||
return newReusableWorkflowExecutor(rc, rc.Config.Workdir, rc.Run.Job().Uses)
|
||||
return newReusableWorkflowExecutor(rc, rc.Config.Workdir, rc.Run.Job().Uses, rc.Config.Planner)
|
||||
}
|
||||
|
||||
func newRemoteReusableWorkflowExecutor(rc *RunContext) common.Executor {
|
||||
@@ -28,10 +28,10 @@ func newRemoteReusableWorkflowExecutor(rc *RunContext) common.Executor {
|
||||
// multiple reusable workflows from the same repository and ref since for each workflow we won't have to clone it again
|
||||
filename := fmt.Sprintf("%s/%s@%s", remoteReusableWorkflow.Org, remoteReusableWorkflow.Repo, remoteReusableWorkflow.Ref)
|
||||
|
||||
return newActionCacheReusableWorkflowExecutor(rc, filename, remoteReusableWorkflow)
|
||||
return newActionCacheReusableWorkflowExecutor(rc, filename, remoteReusableWorkflow, rc.Config.Planner)
|
||||
}
|
||||
|
||||
func newActionCacheReusableWorkflowExecutor(rc *RunContext, filename string, remoteReusableWorkflow *remoteReusableWorkflow) common.Executor {
|
||||
func newActionCacheReusableWorkflowExecutor(rc *RunContext, filename string, remoteReusableWorkflow *remoteReusableWorkflow, config model.PlannerConfig) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
ghctx := rc.getGithubContext(ctx)
|
||||
remoteReusableWorkflow.URL = ghctx.ServerURL
|
||||
@@ -49,7 +49,7 @@ func newActionCacheReusableWorkflowExecutor(rc *RunContext, filename string, rem
|
||||
if _, err = treader.Next(); err != nil {
|
||||
return err
|
||||
}
|
||||
planner, err := model.NewSingleWorkflowPlanner(remoteReusableWorkflow.Filename, treader)
|
||||
planner, err := model.NewSingleWorkflowPlanner(remoteReusableWorkflow.Filename, treader, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -67,9 +67,9 @@ func newActionCacheReusableWorkflowExecutor(rc *RunContext, filename string, rem
|
||||
}
|
||||
}
|
||||
|
||||
func newReusableWorkflowExecutor(rc *RunContext, directory string, workflow string) common.Executor {
|
||||
func newReusableWorkflowExecutor(rc *RunContext, directory string, workflow string, config model.PlannerConfig) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
planner, err := model.NewWorkflowPlanner(path.Join(directory, workflow), true, false)
|
||||
planner, err := model.NewWorkflowPlanner(path.Join(directory, workflow), config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1109,36 +1109,47 @@ func nestedMapLookup(m map[string]interface{}, ks ...string) (rval interface{})
|
||||
return nestedMapLookup(m, ks[1:]...)
|
||||
}
|
||||
|
||||
func (rc *RunContext) setMainCtxVars(env map[string]string, name string, value string) {
|
||||
// Use this to set GITHUB_<name> and any additional main context names in env
|
||||
ctxNames := rc.Config.MainContextNames
|
||||
if len(ctxNames) == 0 {
|
||||
ctxNames = []string{"github"}
|
||||
}
|
||||
for _, ctxName := range ctxNames {
|
||||
env[strings.ToUpper(ctxName)+"_"+name] = value
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *RunContext) withGithubEnv(ctx context.Context, github *model.GithubContext, env map[string]string) map[string]string {
|
||||
env["CI"] = "true"
|
||||
env["GITHUB_WORKFLOW"] = github.Workflow
|
||||
env["GITHUB_RUN_ATTEMPT"] = github.RunAttempt
|
||||
env["GITHUB_RUN_ID"] = github.RunID
|
||||
env["GITHUB_RUN_NUMBER"] = github.RunNumber
|
||||
env["GITHUB_ACTION"] = github.Action
|
||||
env["GITHUB_ACTION_PATH"] = github.ActionPath
|
||||
env["GITHUB_ACTION_REPOSITORY"] = github.ActionRepository
|
||||
env["GITHUB_ACTION_REF"] = github.ActionRef
|
||||
env["GITHUB_ACTIONS"] = "true"
|
||||
env["GITHUB_ACTOR"] = github.Actor
|
||||
env["GITHUB_REPOSITORY"] = github.Repository
|
||||
env["GITHUB_EVENT_NAME"] = github.EventName
|
||||
env["GITHUB_EVENT_PATH"] = github.EventPath
|
||||
env["GITHUB_WORKSPACE"] = github.Workspace
|
||||
env["GITHUB_SHA"] = github.Sha
|
||||
env["GITHUB_REF"] = github.Ref
|
||||
env["GITHUB_REF_NAME"] = github.RefName
|
||||
env["GITHUB_REF_TYPE"] = github.RefType
|
||||
env["GITHUB_JOB"] = github.Job
|
||||
env["GITHUB_REPOSITORY_OWNER"] = github.RepositoryOwner
|
||||
env["GITHUB_RETENTION_DAYS"] = github.RetentionDays
|
||||
rc.setMainCtxVars(env, "WORKFLOW", github.Workflow)
|
||||
rc.setMainCtxVars(env, "RUN_ATTEMPT", github.RunAttempt)
|
||||
rc.setMainCtxVars(env, "RUN_ID", github.RunID)
|
||||
rc.setMainCtxVars(env, "RUN_NUMBER", github.RunNumber)
|
||||
rc.setMainCtxVars(env, "ACTION", github.Action)
|
||||
rc.setMainCtxVars(env, "ACTION_PATH", github.ActionPath)
|
||||
rc.setMainCtxVars(env, "ACTION_REPOSITORY", github.ActionRepository)
|
||||
rc.setMainCtxVars(env, "ACTION_REF", github.ActionRef)
|
||||
rc.setMainCtxVars(env, "ACTIONS", "true")
|
||||
rc.setMainCtxVars(env, "ACTOR", github.Actor)
|
||||
rc.setMainCtxVars(env, "REPOSITORY", github.Repository)
|
||||
rc.setMainCtxVars(env, "EVENT_NAME", github.EventName)
|
||||
rc.setMainCtxVars(env, "EVENT_PATH", github.EventPath)
|
||||
rc.setMainCtxVars(env, "WORKSPACE", github.Workspace)
|
||||
rc.setMainCtxVars(env, "SHA", github.Sha)
|
||||
rc.setMainCtxVars(env, "REF", github.Ref)
|
||||
rc.setMainCtxVars(env, "REF_NAME", github.RefName)
|
||||
rc.setMainCtxVars(env, "REF_TYPE", github.RefType)
|
||||
rc.setMainCtxVars(env, "JOB", github.Job)
|
||||
rc.setMainCtxVars(env, "REPOSITORY_OWNER", github.RepositoryOwner)
|
||||
rc.setMainCtxVars(env, "RETENTION_DAYS", github.RetentionDays)
|
||||
env["RUNNER_PERFLOG"] = github.RunnerPerflog
|
||||
env["RUNNER_TRACKING_ID"] = github.RunnerTrackingID
|
||||
env["GITHUB_BASE_REF"] = github.BaseRef
|
||||
env["GITHUB_HEAD_REF"] = github.HeadRef
|
||||
env["GITHUB_SERVER_URL"] = github.ServerURL
|
||||
env["GITHUB_API_URL"] = github.APIURL
|
||||
env["GITHUB_GRAPHQL_URL"] = github.GraphQLURL
|
||||
rc.setMainCtxVars(env, "BASE_REF", github.BaseRef)
|
||||
rc.setMainCtxVars(env, "HEAD_REF", github.HeadRef)
|
||||
rc.setMainCtxVars(env, "SERVER_URL", github.ServerURL)
|
||||
rc.setMainCtxVars(env, "API_URL", github.APIURL)
|
||||
rc.setMainCtxVars(env, "GRAPHQL_URL", github.GraphQLURL)
|
||||
|
||||
if rc.Config.ArtifactServerPath != "" {
|
||||
setActionRuntimeVars(rc, env)
|
||||
|
||||
@@ -68,9 +68,12 @@ type Config struct {
|
||||
ActionCache ActionCache // Use a custom ActionCache Implementation
|
||||
HostEnvironmentDir string // Custom folder for host environment, parallel jobs must be 1
|
||||
|
||||
CustomExecutor map[model.JobType]func(*RunContext) common.Executor // Custom executor to run jobs
|
||||
semaphore *semaphore.Weighted
|
||||
Parallel int // Number of parallel jobs to run
|
||||
CustomExecutor map[model.JobType]func(*RunContext) common.Executor // Custom executor to run jobs
|
||||
semaphore *semaphore.Weighted
|
||||
Parallel int // Number of parallel jobs to run
|
||||
Planner model.PlannerConfig // Configuration for the workflow planner
|
||||
Action model.ActionConfig // Configuration for action reading
|
||||
MainContextNames []string // e.g. "github", "gitea", "forgejo"
|
||||
}
|
||||
|
||||
func (runnerConfig *Config) GetGitHubServerURL() string {
|
||||
|
||||
@@ -56,7 +56,7 @@ func init() {
|
||||
}
|
||||
|
||||
func TestNoWorkflowsFoundByPlanner(t *testing.T) {
|
||||
planner, err := model.NewWorkflowPlanner("res", true, false)
|
||||
planner, err := model.NewWorkflowPlanner("hashfiles", model.PlannerConfig{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
out := log.StandardLogger().Out
|
||||
@@ -76,7 +76,7 @@ func TestNoWorkflowsFoundByPlanner(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGraphMissingEvent(t *testing.T) {
|
||||
planner, err := model.NewWorkflowPlanner("testdata/issue-1595/no-event.yml", true, false)
|
||||
planner, err := model.NewWorkflowPlanner("testdata/issue-1595/no-event.yml", model.PlannerConfig{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
out := log.StandardLogger().Out
|
||||
@@ -94,7 +94,7 @@ func TestGraphMissingEvent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGraphMissingFirst(t *testing.T) {
|
||||
planner, err := model.NewWorkflowPlanner("testdata/issue-1595/no-first.yml", true, false)
|
||||
planner, err := model.NewWorkflowPlanner("testdata/issue-1595/no-first.yml", model.PlannerConfig{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
plan, err := planner.PlanEvent("push")
|
||||
@@ -104,7 +104,7 @@ func TestGraphMissingFirst(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGraphWithMissing(t *testing.T) {
|
||||
planner, err := model.NewWorkflowPlanner("testdata/issue-1595/missing.yml", true, false)
|
||||
planner, err := model.NewWorkflowPlanner("testdata/issue-1595/missing.yml", model.PlannerConfig{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
out := log.StandardLogger().Out
|
||||
@@ -123,7 +123,7 @@ func TestGraphWithMissing(t *testing.T) {
|
||||
func TestGraphWithSomeMissing(t *testing.T) {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
|
||||
planner, err := model.NewWorkflowPlanner("testdata/issue-1595/", true, false)
|
||||
planner, err := model.NewWorkflowPlanner("testdata/issue-1595/", model.PlannerConfig{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
out := log.StandardLogger().Out
|
||||
@@ -141,7 +141,7 @@ func TestGraphWithSomeMissing(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGraphEvent(t *testing.T) {
|
||||
planner, err := model.NewWorkflowPlanner("testdata/basic", true, false)
|
||||
planner, err := model.NewWorkflowPlanner("testdata/basic", model.PlannerConfig{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
plan, err := planner.PlanEvent("push")
|
||||
@@ -200,7 +200,7 @@ func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config
|
||||
runner, err := New(runnerConfig)
|
||||
assert.Nil(t, err, j.workflowPath)
|
||||
|
||||
planner, err := model.NewWorkflowPlanner(fullWorkflowPath, true, false)
|
||||
planner, err := model.NewWorkflowPlanner(fullWorkflowPath, model.PlannerConfig{})
|
||||
if j.errorMessage != "" && err != nil {
|
||||
assert.Error(t, err, j.errorMessage)
|
||||
} else if assert.Nil(t, err, fullWorkflowPath) {
|
||||
|
||||
@@ -141,19 +141,19 @@ func runStepExecutor(step step, stage stepStage, executor common.Executor) commo
|
||||
actPath := rc.JobContainer.GetActPath()
|
||||
|
||||
outputFileCommand := path.Join("workflow", "outputcmd.txt")
|
||||
(*step.getEnv())["GITHUB_OUTPUT"] = path.Join(actPath, outputFileCommand)
|
||||
rc.setMainCtxVars(*step.getEnv(), "OUTPUT", path.Join(actPath, outputFileCommand))
|
||||
|
||||
stateFileCommand := path.Join("workflow", "statecmd.txt")
|
||||
(*step.getEnv())["GITHUB_STATE"] = path.Join(actPath, stateFileCommand)
|
||||
rc.setMainCtxVars(*step.getEnv(), "STATE", path.Join(actPath, stateFileCommand))
|
||||
|
||||
pathFileCommand := path.Join("workflow", "pathcmd.txt")
|
||||
(*step.getEnv())["GITHUB_PATH"] = path.Join(actPath, pathFileCommand)
|
||||
rc.setMainCtxVars(*step.getEnv(), "PATH", path.Join(actPath, pathFileCommand))
|
||||
|
||||
envFileCommand := path.Join("workflow", "envs.txt")
|
||||
(*step.getEnv())["GITHUB_ENV"] = path.Join(actPath, envFileCommand)
|
||||
rc.setMainCtxVars(*step.getEnv(), "ENV", path.Join(actPath, envFileCommand))
|
||||
|
||||
summaryFileCommand := path.Join("workflow", "SUMMARY.md")
|
||||
(*step.getEnv())["GITHUB_STEP_SUMMARY"] = path.Join(actPath, summaryFileCommand)
|
||||
rc.setMainCtxVars(*step.getEnv(), "STEP_SUMMARY", path.Join(actPath, summaryFileCommand))
|
||||
|
||||
_ = rc.JobContainer.Copy(actPath, &container.FileEntry{
|
||||
Name: outputFileCommand,
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/actions-oss/act-cli/pkg/common"
|
||||
"github.com/actions-oss/act-cli/pkg/model"
|
||||
@@ -40,17 +39,7 @@ func (sal *stepActionLocal) main() common.Executor {
|
||||
return nil
|
||||
}
|
||||
|
||||
workdir := sal.getRunContext().Config.Workdir
|
||||
actionDir := filepath.Join(workdir, sal.Step.Uses)
|
||||
|
||||
localReader := func(ctx context.Context) actionYamlReader {
|
||||
// In case we want to limit resolving symlinks, folders are resolved by archive function
|
||||
// _, cpath = sal.getContainerActionPathsExt(".")
|
||||
roots := []string{
|
||||
".", // Allow everything, other code permits it already
|
||||
// path.Dir(cpath), // Allow RUNNER_WORKSPACE e.g. GITHUB_WORKSPACE/../
|
||||
// sal.RunContext.JobContainer.GetActPath(), // Allow remote action folders
|
||||
}
|
||||
_, cpath := sal.getContainerActionPaths()
|
||||
return func(filename string) (io.Reader, io.Closer, error) {
|
||||
spath := path.Join(cpath, filename)
|
||||
@@ -69,7 +58,7 @@ func (sal *stepActionLocal) main() common.Executor {
|
||||
return nil, nil, err
|
||||
}
|
||||
if header.FileInfo().Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
spath, err = symlinkJoin(spath, header.Linkname, roots...)
|
||||
spath, err = symlinkJoin(spath, header.Linkname, ".")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -81,7 +70,7 @@ func (sal *stepActionLocal) main() common.Executor {
|
||||
}
|
||||
}
|
||||
|
||||
actionModel, err := sal.readAction(ctx, sal.Step, actionDir, "", localReader(ctx), os.WriteFile)
|
||||
actionModel, err := sal.readAction(ctx, sal.Step, localReader(ctx), sal.RunContext.Config.Action)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -24,8 +23,8 @@ func (salm *stepActionLocalMocks) runAction(step actionStep) common.Executor {
|
||||
return args.Get(0).(func(context.Context) error)
|
||||
}
|
||||
|
||||
func (salm *stepActionLocalMocks) readAction(_ context.Context, step *model.Step, actionDir string, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error) {
|
||||
args := salm.Called(step, actionDir, actionPath, readFile, writeFile)
|
||||
func (salm *stepActionLocalMocks) readAction(_ context.Context, step *model.Step, readFile actionYamlReader, config model.ActionConfig) (*model.Action, error) {
|
||||
args := salm.Called(step, readFile, config)
|
||||
return args.Get(0).(*model.Action), args.Error(1)
|
||||
}
|
||||
|
||||
@@ -66,7 +65,7 @@ func TestStepActionLocalTest(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
salm.On("readAction", sal.Step, filepath.Clean("/tmp/path/to/action"), "", mock.Anything, mock.Anything).
|
||||
salm.On("readAction", sal.Step, mock.Anything, sal.RunContext.Config.Action).
|
||||
Return(&model.Action{}, nil)
|
||||
|
||||
cm.On("Copy", "/var/run/act", mock.AnythingOfType("[]*container.FileEntry")).Return(func(_ context.Context) error {
|
||||
|
||||
@@ -92,7 +92,7 @@ func (sar *stepActionRemote) prepareActionExecutor() common.Executor {
|
||||
}
|
||||
}
|
||||
|
||||
actionModel, err := sar.readAction(ctx, sar.Step, sar.resolvedSha, sar.remoteAction.Path, remoteReader(ctx), os.WriteFile)
|
||||
actionModel, err := sar.readAction(ctx, sar.Step, remoteReader(ctx), sar.RunContext.Config.Action)
|
||||
sar.action = actionModel
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ type stepActionRemoteMocks struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (sarm *stepActionRemoteMocks) readAction(_ context.Context, step *model.Step, actionDir string, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error) {
|
||||
args := sarm.Called(step, actionDir, actionPath, readFile, writeFile)
|
||||
func (sarm *stepActionRemoteMocks) readAction(_ context.Context, step *model.Step, readFile actionYamlReader, config model.ActionConfig) (*model.Action, error) {
|
||||
args := sarm.Called(step, readFile, config)
|
||||
return args.Get(0).(*model.Action), args.Error(1)
|
||||
}
|
||||
|
||||
@@ -175,6 +175,9 @@ func TestStepActionRemote(t *testing.T) {
|
||||
GitHubServerURL: tt.gitHubServerURL,
|
||||
GitHubAPIServerURL: tt.gitHubAPIServerURL,
|
||||
GitHubGraphQlAPIServerURL: tt.gitHubGraphQlAPIServerURL,
|
||||
Action: model.ActionConfig{
|
||||
Definition: "action-root",
|
||||
},
|
||||
},
|
||||
Run: &model.Run{
|
||||
JobID: "1",
|
||||
@@ -201,7 +204,7 @@ func TestStepActionRemote(t *testing.T) {
|
||||
cacheMock.Mock.On("Fetch", ctx, mock.AnythingOfType("string"), serverURL+"/remote/action", "v1", "").Return("someval")
|
||||
|
||||
if tt.mocks.read {
|
||||
sarm.Mock.On("readAction", sar.Step, "someval", "", mock.Anything, mock.Anything).Return(&model.Action{}, nil)
|
||||
sarm.Mock.On("readAction", sar.Step, mock.Anything, sar.RunContext.Config.Action).Return(&model.Action{}, nil)
|
||||
}
|
||||
if tt.mocks.run {
|
||||
remoteAction := newRemoteAction(sar.Step.Uses)
|
||||
@@ -282,7 +285,7 @@ func TestStepActionRemotePre(t *testing.T) {
|
||||
readAction: sarm.readAction,
|
||||
}
|
||||
|
||||
sarm.Mock.On("readAction", sar.Step, "someval", "path", mock.Anything, mock.Anything).Return(&model.Action{}, nil)
|
||||
sarm.Mock.On("readAction", sar.Step, mock.Anything, sar.RunContext.Config.Action).Return(&model.Action{}, nil)
|
||||
cacheMock.Mock.On("Fetch", ctx, mock.AnythingOfType("string"), "https://github.com/org/repo", "ref", "").Return("someval")
|
||||
|
||||
err := sar.pre()(ctx)
|
||||
@@ -335,7 +338,7 @@ func TestStepActionRemotePreThroughAction(t *testing.T) {
|
||||
readAction: sarm.readAction,
|
||||
}
|
||||
|
||||
sarm.Mock.On("readAction", sar.Step, mock.AnythingOfType("string"), "path", mock.Anything, mock.Anything).Return(&model.Action{}, nil)
|
||||
sarm.Mock.On("readAction", sar.Step, mock.Anything, sar.RunContext.Config.Action).Return(&model.Action{}, nil)
|
||||
cacheMock.Mock.On("Fetch", ctx, mock.AnythingOfType("string"), "https://github.com/org/repo", "ref", "").Return("someval")
|
||||
|
||||
err := sar.pre()(ctx)
|
||||
@@ -389,7 +392,7 @@ func TestStepActionRemotePreThroughActionToken(t *testing.T) {
|
||||
readAction: sarm.readAction,
|
||||
}
|
||||
|
||||
sarm.Mock.On("readAction", sar.Step, mock.AnythingOfType("string"), "path", mock.Anything, mock.Anything).Return(&model.Action{}, nil)
|
||||
sarm.Mock.On("readAction", sar.Step, mock.Anything, sar.RunContext.Config.Action).Return(&model.Action{}, nil)
|
||||
cacheMock.Mock.On("Fetch", ctx, mock.AnythingOfType("string"), "https://github.com/org/repo", "ref", "PRIVATE_ACTIONS_TOKEN_ON_GITHUB").Return("someval")
|
||||
|
||||
err := sar.pre()(ctx)
|
||||
|
||||
@@ -29,3 +29,16 @@ func updateUses(mapping *MappingDefinition) {
|
||||
uses.Type = "string-strategy-context"
|
||||
mapping.Properties["uses"] = uses
|
||||
}
|
||||
|
||||
func GetGiteaActionSchema() *Schema {
|
||||
schema := GetActionSchema()
|
||||
in := schema.Definitions
|
||||
schema.Definitions = map[string]Definition{}
|
||||
for k, v := range in {
|
||||
if v.Context != nil && slices.Contains(v.Context, "github") {
|
||||
v.Context = append(v.Context, "gitea", "env")
|
||||
}
|
||||
schema.Definitions[k] = v
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user