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:
Christopher Homberger
2026-02-14 16:23:59 +00:00
committed by ChristopherHX
parent faa252c8e9
commit 933c4a5bd5
25 changed files with 265 additions and 253 deletions

View File

@@ -66,6 +66,7 @@ type Input struct {
validate bool
strict bool
parallel int
gitea bool
}
func (i *Input) resolve(path string) string {

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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,
}),
}
}

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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
}