feat: allow to spawn and run a local reusable workflow (#1423)
* feat: allow to spawn and run a local reusable workflow This change contains the ability to parse/plan/run a local reusable workflow. There are still numerous things missing: - inputs - secrets - outputs * feat: add workflow_call inputs * test: improve inputs test * feat: add input defaults * feat: allow expressions in inputs * feat: use context specific expression evaluator * refactor: prepare for better re-usability * feat: add secrets for reusable workflows * test: use secrets during test run * feat: handle reusable workflow outputs Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
d281230cce
commit
a8e05cded6
10 changed files with 470 additions and 137 deletions
|
@ -100,6 +100,44 @@ func (w *Workflow) WorkflowDispatchConfig() *WorkflowDispatch {
|
||||||
return &config
|
return &config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WorkflowCallInput struct {
|
||||||
|
Description string `yaml:"description"`
|
||||||
|
Required bool `yaml:"required"`
|
||||||
|
Default string `yaml:"default"`
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkflowCallOutput struct {
|
||||||
|
Description string `yaml:"description"`
|
||||||
|
Value string `yaml:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkflowCall struct {
|
||||||
|
Inputs map[string]WorkflowCallInput `yaml:"inputs"`
|
||||||
|
Outputs map[string]WorkflowCallOutput `yaml:"outputs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Workflow) WorkflowCallConfig() *WorkflowCall {
|
||||||
|
if w.RawOn.Kind != yaml.MappingNode {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var val map[string]yaml.Node
|
||||||
|
err := w.RawOn.Decode(&val)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var config WorkflowCall
|
||||||
|
node := val["workflow_call"]
|
||||||
|
err = node.Decode(&config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config
|
||||||
|
}
|
||||||
|
|
||||||
// Job is the structure of one job in a workflow
|
// Job is the structure of one job in a workflow
|
||||||
type Job struct {
|
type Job struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
|
@ -115,6 +153,8 @@ type Job struct {
|
||||||
Defaults Defaults `yaml:"defaults"`
|
Defaults Defaults `yaml:"defaults"`
|
||||||
Outputs map[string]string `yaml:"outputs"`
|
Outputs map[string]string `yaml:"outputs"`
|
||||||
Uses string `yaml:"uses"`
|
Uses string `yaml:"uses"`
|
||||||
|
With map[string]interface{} `yaml:"with"`
|
||||||
|
RawSecrets yaml.Node `yaml:"secrets"`
|
||||||
Result string
|
Result string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +209,34 @@ func (s Strategy) GetFailFast() bool {
|
||||||
return failFast
|
return failFast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j *Job) InheritSecrets() bool {
|
||||||
|
if j.RawSecrets.Kind != yaml.ScalarNode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var val string
|
||||||
|
err := j.RawSecrets.Decode(&val)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return val == "inherit"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *Job) Secrets() map[string]string {
|
||||||
|
if j.RawSecrets.Kind != yaml.MappingNode {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var val map[string]string
|
||||||
|
err := j.RawSecrets.Decode(&val)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
// Container details for the job
|
// Container details for the job
|
||||||
func (j *Job) Container() *ContainerSpec {
|
func (j *Job) Container() *ContainerSpec {
|
||||||
var val *ContainerSpec
|
var val *ContainerSpec
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (rc *RunContext) NewExpressionEvaluatorWithEnv(ctx context.Context, env map
|
||||||
// todo: should be unavailable
|
// todo: should be unavailable
|
||||||
// but required to interpolate/evaluate the step outputs on the job
|
// but required to interpolate/evaluate the step outputs on the job
|
||||||
Steps: rc.getStepsContext(),
|
Steps: rc.getStepsContext(),
|
||||||
Secrets: rc.Config.Secrets,
|
Secrets: getWorkflowSecrets(ctx, rc),
|
||||||
Strategy: strategy,
|
Strategy: strategy,
|
||||||
Matrix: rc.Matrix,
|
Matrix: rc.Matrix,
|
||||||
Needs: using,
|
Needs: using,
|
||||||
|
@ -101,7 +101,7 @@ func (rc *RunContext) NewStepExpressionEvaluator(ctx context.Context, step step)
|
||||||
Env: *step.getEnv(),
|
Env: *step.getEnv(),
|
||||||
Job: rc.getJobContext(),
|
Job: rc.getJobContext(),
|
||||||
Steps: rc.getStepsContext(),
|
Steps: rc.getStepsContext(),
|
||||||
Secrets: rc.Config.Secrets,
|
Secrets: getWorkflowSecrets(ctx, rc),
|
||||||
Strategy: strategy,
|
Strategy: strategy,
|
||||||
Matrix: rc.Matrix,
|
Matrix: rc.Matrix,
|
||||||
Needs: using,
|
Needs: using,
|
||||||
|
@ -315,6 +315,8 @@ func rewriteSubExpression(ctx context.Context, in string, forceFormat bool) (str
|
||||||
func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *model.GithubContext) map[string]interface{} {
|
func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *model.GithubContext) map[string]interface{} {
|
||||||
inputs := map[string]interface{}{}
|
inputs := map[string]interface{}{}
|
||||||
|
|
||||||
|
setupWorkflowInputs(ctx, &inputs, rc)
|
||||||
|
|
||||||
var env map[string]string
|
var env map[string]string
|
||||||
if step != nil {
|
if step != nil {
|
||||||
env = *step.getEnv()
|
env = *step.getEnv()
|
||||||
|
@ -347,3 +349,54 @@ func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *mod
|
||||||
|
|
||||||
return inputs
|
return inputs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupWorkflowInputs(ctx context.Context, inputs *map[string]interface{}, rc *RunContext) {
|
||||||
|
if rc.caller != nil {
|
||||||
|
config := rc.Run.Workflow.WorkflowCallConfig()
|
||||||
|
|
||||||
|
for name, input := range config.Inputs {
|
||||||
|
value := rc.caller.runContext.Run.Job().With[name]
|
||||||
|
if value != nil {
|
||||||
|
if str, ok := value.(string); ok {
|
||||||
|
// evaluate using the calling RunContext (outside)
|
||||||
|
value = rc.caller.runContext.ExprEval.Interpolate(ctx, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == nil && config != nil && config.Inputs != nil {
|
||||||
|
value = input.Default
|
||||||
|
if rc.ExprEval != nil {
|
||||||
|
if str, ok := value.(string); ok {
|
||||||
|
// evaluate using the called RunContext (inside)
|
||||||
|
value = rc.ExprEval.Interpolate(ctx, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(*inputs)[name] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWorkflowSecrets(ctx context.Context, rc *RunContext) map[string]string {
|
||||||
|
if rc.caller != nil {
|
||||||
|
job := rc.caller.runContext.Run.Job()
|
||||||
|
secrets := job.Secrets()
|
||||||
|
|
||||||
|
if secrets == nil && job.InheritSecrets() {
|
||||||
|
secrets = rc.caller.runContext.Config.Secrets
|
||||||
|
}
|
||||||
|
|
||||||
|
if secrets == nil {
|
||||||
|
secrets = map[string]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range secrets {
|
||||||
|
secrets[k] = rc.caller.runContext.ExprEval.Interpolate(ctx, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return secrets
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc.Config.Secrets
|
||||||
|
}
|
||||||
|
|
|
@ -104,6 +104,8 @@ func newJobExecutor(info jobInfo, sf stepFactory, rc *RunContext) common.Executo
|
||||||
err = info.stopContainer()(ctx)
|
err = info.stopContainer()(ctx)
|
||||||
}
|
}
|
||||||
setJobResult(ctx, info, rc, jobError == nil)
|
setJobResult(ctx, info, rc, jobError == nil)
|
||||||
|
setJobOutputs(ctx, rc)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -135,9 +137,27 @@ func setJobResult(ctx context.Context, info jobInfo, rc *RunContext, success boo
|
||||||
jobResultMessage = "failed"
|
jobResultMessage = "failed"
|
||||||
}
|
}
|
||||||
info.result(jobResult)
|
info.result(jobResult)
|
||||||
|
if rc.caller != nil {
|
||||||
|
// set reusable workflow job result
|
||||||
|
rc.caller.runContext.result(jobResult)
|
||||||
|
}
|
||||||
logger.WithField("jobResult", jobResult).Infof("\U0001F3C1 Job %s", jobResultMessage)
|
logger.WithField("jobResult", jobResult).Infof("\U0001F3C1 Job %s", jobResultMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setJobOutputs(ctx context.Context, rc *RunContext) {
|
||||||
|
if rc.caller != nil {
|
||||||
|
// map outputs for reusable workflows
|
||||||
|
callerOutputs := make(map[string]string)
|
||||||
|
|
||||||
|
ee := rc.NewExpressionEvaluator(ctx)
|
||||||
|
for k, v := range rc.Run.Job().Outputs {
|
||||||
|
callerOutputs[k] = ee.Interpolate(ctx, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
rc.caller.runContext.Run.Job().Outputs = callerOutputs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func useStepLogger(rc *RunContext, stepModel *model.Step, stage stepStage, executor common.Executor) common.Executor {
|
func useStepLogger(rc *RunContext, stepModel *model.Step, stage stepStage, executor common.Executor) common.Executor {
|
||||||
return func(ctx context.Context) error {
|
return func(ctx context.Context) error {
|
||||||
ctx = withStepLogger(ctx, stepModel.ID, rc.ExprEval.Interpolate(ctx, stepModel.String()), stage.String())
|
ctx = withStepLogger(ctx, stepModel.ID, rc.ExprEval.Interpolate(ctx, stepModel.String()), stage.String())
|
||||||
|
|
|
@ -15,15 +15,15 @@ import (
|
||||||
|
|
||||||
func TestJobExecutor(t *testing.T) {
|
func TestJobExecutor(t *testing.T) {
|
||||||
tables := []TestJobFileInfo{
|
tables := []TestJobFileInfo{
|
||||||
{workdir, "uses-and-run-in-one-step", "push", "Invalid run/uses syntax for job:test step:Test", platforms},
|
{workdir, "uses-and-run-in-one-step", "push", "Invalid run/uses syntax for job:test step:Test", platforms, secrets},
|
||||||
{workdir, "uses-github-empty", "push", "Expected format {org}/{repo}[/path]@ref", platforms},
|
{workdir, "uses-github-empty", "push", "Expected format {org}/{repo}[/path]@ref", platforms, secrets},
|
||||||
{workdir, "uses-github-noref", "push", "Expected format {org}/{repo}[/path]@ref", platforms},
|
{workdir, "uses-github-noref", "push", "Expected format {org}/{repo}[/path]@ref", platforms, secrets},
|
||||||
{workdir, "uses-github-root", "push", "", platforms},
|
{workdir, "uses-github-root", "push", "", platforms, secrets},
|
||||||
{workdir, "uses-github-path", "push", "", platforms},
|
{workdir, "uses-github-path", "push", "", platforms, secrets},
|
||||||
{workdir, "uses-docker-url", "push", "", platforms},
|
{workdir, "uses-docker-url", "push", "", platforms, secrets},
|
||||||
{workdir, "uses-github-full-sha", "push", "", platforms},
|
{workdir, "uses-github-full-sha", "push", "", platforms, secrets},
|
||||||
{workdir, "uses-github-short-sha", "push", "Unable to resolve action `actions/hello-world-docker-action@b136eb8`, the provided ref `b136eb8` is the shortened version of a commit SHA, which is not supported. Please use the full commit SHA `b136eb8894c5cb1dd5807da824be97ccdf9b5423` instead", platforms},
|
{workdir, "uses-github-short-sha", "push", "Unable to resolve action `actions/hello-world-docker-action@b136eb8`, the provided ref `b136eb8` is the shortened version of a commit SHA, which is not supported. Please use the full commit SHA `b136eb8894c5cb1dd5807da824be97ccdf9b5423` instead", platforms, secrets},
|
||||||
{workdir, "job-nil-step", "push", "invalid Step 0: missing run or uses key", platforms},
|
{workdir, "job-nil-step", "push", "invalid Step 0: missing run or uses key", platforms, secrets},
|
||||||
}
|
}
|
||||||
// These tests are sufficient to only check syntax.
|
// These tests are sufficient to only check syntax.
|
||||||
ctx := common.WithDryrun(context.Background(), true)
|
ctx := common.WithDryrun(context.Background(), true)
|
||||||
|
|
45
pkg/runner/reusable_workflow.go
Normal file
45
pkg/runner/reusable_workflow.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/nektos/act/pkg/common"
|
||||||
|
"github.com/nektos/act/pkg/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newLocalReusableWorkflowExecutor(rc *RunContext) common.Executor {
|
||||||
|
return newReusableWorkflowExecutor(rc, rc.Config.Workdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRemoteReusableWorkflowExecutor(rc *RunContext) common.Executor {
|
||||||
|
return common.NewErrorExecutor(fmt.Errorf("remote reusable workflows are currently not supported (see https://github.com/nektos/act/issues/826 for updates)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReusableWorkflowExecutor(rc *RunContext, directory string) common.Executor {
|
||||||
|
planner, err := model.NewWorkflowPlanner(path.Join(directory, rc.Run.Job().Uses), true)
|
||||||
|
if err != nil {
|
||||||
|
return common.NewErrorExecutor(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
plan := planner.PlanEvent("workflow_call")
|
||||||
|
|
||||||
|
runner, err := NewReusableWorkflowRunner(rc)
|
||||||
|
if err != nil {
|
||||||
|
return common.NewErrorExecutor(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return runner.NewPlanExecutor(plan)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReusableWorkflowRunner(rc *RunContext) (Runner, error) {
|
||||||
|
runner := &runnerImpl{
|
||||||
|
config: rc.Config,
|
||||||
|
eventJSON: rc.EventJSON,
|
||||||
|
caller: &caller{
|
||||||
|
runContext: rc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return runner.configure()
|
||||||
|
}
|
|
@ -46,6 +46,7 @@ type RunContext struct {
|
||||||
Parent *RunContext
|
Parent *RunContext
|
||||||
Masks []string
|
Masks []string
|
||||||
cleanUpJobContainer common.Executor
|
cleanUpJobContainer common.Executor
|
||||||
|
caller *caller // job calling this RunContext (reusable workflows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rc *RunContext) AddMask(mask string) {
|
func (rc *RunContext) AddMask(mask string) {
|
||||||
|
@ -58,7 +59,13 @@ type MappableOutput struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rc *RunContext) String() string {
|
func (rc *RunContext) String() string {
|
||||||
return fmt.Sprintf("%s/%s", rc.Run.Workflow.Name, rc.Name)
|
name := fmt.Sprintf("%s/%s", rc.Run.Workflow.Name, rc.Name)
|
||||||
|
if rc.caller != nil {
|
||||||
|
// prefix the reusable workflow with the caller job
|
||||||
|
// this is required to create unique container names
|
||||||
|
name = fmt.Sprintf("%s/%s", rc.caller.runContext.Run.JobID, name)
|
||||||
|
}
|
||||||
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEnv returns the env for the context
|
// GetEnv returns the env for the context
|
||||||
|
@ -399,16 +406,25 @@ func (rc *RunContext) steps() []*model.Step {
|
||||||
|
|
||||||
// Executor returns a pipeline executor for all the steps in the job
|
// Executor returns a pipeline executor for all the steps in the job
|
||||||
func (rc *RunContext) Executor() common.Executor {
|
func (rc *RunContext) Executor() common.Executor {
|
||||||
|
var executor common.Executor
|
||||||
|
|
||||||
|
switch rc.Run.Job().Type() {
|
||||||
|
case model.JobTypeDefault:
|
||||||
|
executor = newJobExecutor(rc, &stepFactoryImpl{}, rc)
|
||||||
|
case model.JobTypeReusableWorkflowLocal:
|
||||||
|
executor = newLocalReusableWorkflowExecutor(rc)
|
||||||
|
case model.JobTypeReusableWorkflowRemote:
|
||||||
|
executor = newRemoteReusableWorkflowExecutor(rc)
|
||||||
|
}
|
||||||
|
|
||||||
return func(ctx context.Context) error {
|
return func(ctx context.Context) error {
|
||||||
isEnabled, err := rc.isEnabled(ctx)
|
res, err := rc.isEnabled(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if res {
|
||||||
if isEnabled {
|
return executor(ctx)
|
||||||
return newJobExecutor(rc, &stepFactoryImpl{}, rc)(ctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -458,6 +474,10 @@ func (rc *RunContext) isEnabled(ctx context.Context) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if job.Type() != model.JobTypeDefault {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
img := rc.platformImage(ctx)
|
img := rc.platformImage(ctx)
|
||||||
if img == "" {
|
if img == "" {
|
||||||
if job.RunsOn() == nil {
|
if job.RunsOn() == nil {
|
||||||
|
|
|
@ -53,9 +53,14 @@ type Config struct {
|
||||||
ReplaceGheActionTokenWithGithubCom string // Token of private action repo on GitHub.
|
ReplaceGheActionTokenWithGithubCom string // Token of private action repo on GitHub.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type caller struct {
|
||||||
|
runContext *RunContext
|
||||||
|
}
|
||||||
|
|
||||||
type runnerImpl struct {
|
type runnerImpl struct {
|
||||||
config *Config
|
config *Config
|
||||||
eventJSON string
|
eventJSON string
|
||||||
|
caller *caller // the job calling this runner (caller of a reusable workflow)
|
||||||
}
|
}
|
||||||
|
|
||||||
// New Creates a new Runner
|
// New Creates a new Runner
|
||||||
|
@ -64,8 +69,12 @@ func New(runnerConfig *Config) (Runner, error) {
|
||||||
config: runnerConfig,
|
config: runnerConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return runner.configure()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runner *runnerImpl) configure() (Runner, error) {
|
||||||
runner.eventJSON = "{}"
|
runner.eventJSON = "{}"
|
||||||
if runnerConfig.EventPath != "" {
|
if runner.config.EventPath != "" {
|
||||||
log.Debugf("Reading event.json from %s", runner.config.EventPath)
|
log.Debugf("Reading event.json from %s", runner.config.EventPath)
|
||||||
eventJSONBytes, err := os.ReadFile(runner.config.EventPath)
|
eventJSONBytes, err := os.ReadFile(runner.config.EventPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -89,10 +98,6 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor {
|
||||||
stageExecutor := make([]common.Executor, 0)
|
stageExecutor := make([]common.Executor, 0)
|
||||||
job := run.Job()
|
job := run.Job()
|
||||||
|
|
||||||
if job.Uses != "" {
|
|
||||||
return fmt.Errorf("reusable workflows are currently not supported (see https://github.com/nektos/act/issues/826 for updates)")
|
|
||||||
}
|
|
||||||
|
|
||||||
if job.Strategy != nil {
|
if job.Strategy != nil {
|
||||||
strategyRc := runner.newRunContext(ctx, run, nil)
|
strategyRc := runner.newRunContext(ctx, run, nil)
|
||||||
if err := strategyRc.NewExpressionEvaluator(ctx).EvaluateYamlNode(ctx, &job.Strategy.RawMatrix); err != nil {
|
if err := strategyRc.NewExpressionEvaluator(ctx).EvaluateYamlNode(ctx, &job.Strategy.RawMatrix); err != nil {
|
||||||
|
@ -161,8 +166,10 @@ func (runner *runnerImpl) newRunContext(ctx context.Context, run *model.Run, mat
|
||||||
EventJSON: runner.eventJSON,
|
EventJSON: runner.eventJSON,
|
||||||
StepResults: make(map[string]*model.StepResult),
|
StepResults: make(map[string]*model.StepResult),
|
||||||
Matrix: matrix,
|
Matrix: matrix,
|
||||||
|
caller: runner.caller,
|
||||||
}
|
}
|
||||||
rc.ExprEval = rc.NewExpressionEvaluator(ctx)
|
rc.ExprEval = rc.NewExpressionEvaluator(ctx)
|
||||||
rc.Name = rc.ExprEval.Interpolate(ctx, run.String())
|
rc.Name = rc.ExprEval.Interpolate(ctx, run.String())
|
||||||
|
|
||||||
return rc
|
return rc
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ var (
|
||||||
platforms map[string]string
|
platforms map[string]string
|
||||||
logLevel = log.DebugLevel
|
logLevel = log.DebugLevel
|
||||||
workdir = "testdata"
|
workdir = "testdata"
|
||||||
|
secrets map[string]string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -44,6 +45,8 @@ func init() {
|
||||||
if wd, err := filepath.Abs(workdir); err == nil {
|
if wd, err := filepath.Abs(workdir); err == nil {
|
||||||
workdir = wd
|
workdir = wd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secrets = map[string]string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGraphEvent(t *testing.T) {
|
func TestGraphEvent(t *testing.T) {
|
||||||
|
@ -70,6 +73,7 @@ type TestJobFileInfo struct {
|
||||||
eventName string
|
eventName string
|
||||||
errorMessage string
|
errorMessage string
|
||||||
platforms map[string]string
|
platforms map[string]string
|
||||||
|
secrets map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config) {
|
func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config) {
|
||||||
|
@ -121,84 +125,87 @@ func TestRunEvent(t *testing.T) {
|
||||||
|
|
||||||
tables := []TestJobFileInfo{
|
tables := []TestJobFileInfo{
|
||||||
// Shells
|
// Shells
|
||||||
{workdir, "shells/defaults", "push", "", platforms},
|
{workdir, "shells/defaults", "push", "", platforms, secrets},
|
||||||
// TODO: figure out why it fails
|
// TODO: figure out why it fails
|
||||||
// {workdir, "shells/custom", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}, }, // custom image with pwsh
|
// {workdir, "shells/custom", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}, }, // custom image with pwsh
|
||||||
{workdir, "shells/pwsh", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}}, // custom image with pwsh
|
{workdir, "shells/pwsh", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}, secrets}, // custom image with pwsh
|
||||||
{workdir, "shells/bash", "push", "", platforms},
|
{workdir, "shells/bash", "push", "", platforms, secrets},
|
||||||
{workdir, "shells/python", "push", "", map[string]string{"ubuntu-latest": "node:16-buster"}}, // slim doesn't have python
|
{workdir, "shells/python", "push", "", map[string]string{"ubuntu-latest": "node:16-buster"}, secrets}, // slim doesn't have python
|
||||||
{workdir, "shells/sh", "push", "", platforms},
|
{workdir, "shells/sh", "push", "", platforms, secrets},
|
||||||
|
|
||||||
// Local action
|
// Local action
|
||||||
{workdir, "local-action-docker-url", "push", "", platforms},
|
{workdir, "local-action-docker-url", "push", "", platforms, secrets},
|
||||||
{workdir, "local-action-dockerfile", "push", "", platforms},
|
{workdir, "local-action-dockerfile", "push", "", platforms, secrets},
|
||||||
{workdir, "local-action-via-composite-dockerfile", "push", "", platforms},
|
{workdir, "local-action-via-composite-dockerfile", "push", "", platforms, secrets},
|
||||||
{workdir, "local-action-js", "push", "", platforms},
|
{workdir, "local-action-js", "push", "", platforms, secrets},
|
||||||
|
|
||||||
// Uses
|
// Uses
|
||||||
{workdir, "uses-composite", "push", "", platforms},
|
{workdir, "uses-composite", "push", "", platforms, secrets},
|
||||||
{workdir, "uses-composite-with-error", "push", "Job 'failing-composite-action' failed", platforms},
|
{workdir, "uses-composite-with-error", "push", "Job 'failing-composite-action' failed", platforms, secrets},
|
||||||
{workdir, "uses-nested-composite", "push", "", platforms},
|
{workdir, "uses-nested-composite", "push", "", platforms, secrets},
|
||||||
{workdir, "remote-action-composite-js-pre-with-defaults", "push", "", platforms},
|
{workdir, "remote-action-composite-js-pre-with-defaults", "push", "", platforms, secrets},
|
||||||
{workdir, "uses-workflow", "push", "reusable workflows are currently not supported (see https://github.com/nektos/act/issues/826 for updates)", platforms},
|
{workdir, "uses-workflow", "push", "reusable workflows are currently not supported (see https://github.com/nektos/act/issues/826 for updates)", platforms, secrets},
|
||||||
{workdir, "uses-docker-url", "push", "", platforms},
|
{workdir, "uses-workflow", "pull_request", "", platforms, map[string]string{"secret": "keep_it_private"}},
|
||||||
{workdir, "act-composite-env-test", "push", "", platforms},
|
{workdir, "uses-docker-url", "push", "", platforms, secrets},
|
||||||
|
{workdir, "act-composite-env-test", "push", "", platforms, secrets},
|
||||||
|
|
||||||
// Eval
|
// Eval
|
||||||
{workdir, "evalmatrix", "push", "", platforms},
|
{workdir, "evalmatrix", "push", "", platforms, secrets},
|
||||||
{workdir, "evalmatrixneeds", "push", "", platforms},
|
{workdir, "evalmatrixneeds", "push", "", platforms, secrets},
|
||||||
{workdir, "evalmatrixneeds2", "push", "", platforms},
|
{workdir, "evalmatrixneeds2", "push", "", platforms, secrets},
|
||||||
{workdir, "evalmatrix-merge-map", "push", "", platforms},
|
{workdir, "evalmatrix-merge-map", "push", "", platforms, secrets},
|
||||||
{workdir, "evalmatrix-merge-array", "push", "", platforms},
|
{workdir, "evalmatrix-merge-array", "push", "", platforms, secrets},
|
||||||
{workdir, "issue-1195", "push", "", platforms},
|
{workdir, "issue-1195", "push", "", platforms, secrets},
|
||||||
|
|
||||||
{workdir, "basic", "push", "", platforms},
|
{workdir, "basic", "push", "", platforms, secrets},
|
||||||
{workdir, "fail", "push", "exit with `FAILURE`: 1", platforms},
|
{workdir, "fail", "push", "exit with `FAILURE`: 1", platforms, secrets},
|
||||||
{workdir, "runs-on", "push", "", platforms},
|
{workdir, "runs-on", "push", "", platforms, secrets},
|
||||||
{workdir, "checkout", "push", "", platforms},
|
{workdir, "checkout", "push", "", platforms, secrets},
|
||||||
{workdir, "job-container", "push", "", platforms},
|
{workdir, "job-container", "push", "", platforms, secrets},
|
||||||
{workdir, "job-container-non-root", "push", "", platforms},
|
{workdir, "job-container-non-root", "push", "", platforms, secrets},
|
||||||
{workdir, "job-container-invalid-credentials", "push", "failed to handle credentials: failed to interpolate container.credentials.password", platforms},
|
{workdir, "job-container-invalid-credentials", "push", "failed to handle credentials: failed to interpolate container.credentials.password", platforms, secrets},
|
||||||
{workdir, "container-hostname", "push", "", platforms},
|
{workdir, "container-hostname", "push", "", platforms, secrets},
|
||||||
{workdir, "remote-action-docker", "push", "", platforms},
|
{workdir, "remote-action-docker", "push", "", platforms, secrets},
|
||||||
{workdir, "remote-action-js", "push", "", platforms},
|
{workdir, "remote-action-js", "push", "", platforms, secrets},
|
||||||
{workdir, "remote-action-js", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:runner-latest"}}, // Test if this works with non root container
|
{workdir, "remote-action-js", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:runner-latest"}, secrets}, // Test if this works with non root container
|
||||||
{workdir, "matrix", "push", "", platforms},
|
{workdir, "matrix", "push", "", platforms, secrets},
|
||||||
{workdir, "matrix-include-exclude", "push", "", platforms},
|
{workdir, "matrix-include-exclude", "push", "", platforms, secrets},
|
||||||
{workdir, "commands", "push", "", platforms},
|
{workdir, "commands", "push", "", platforms, secrets},
|
||||||
{workdir, "workdir", "push", "", platforms},
|
{workdir, "workdir", "push", "", platforms, secrets},
|
||||||
{workdir, "defaults-run", "push", "", platforms},
|
{workdir, "defaults-run", "push", "", platforms, secrets},
|
||||||
{workdir, "composite-fail-with-output", "push", "", platforms},
|
{workdir, "composite-fail-with-output", "push", "", platforms, secrets},
|
||||||
{workdir, "issue-597", "push", "", platforms},
|
{workdir, "issue-597", "push", "", platforms, secrets},
|
||||||
{workdir, "issue-598", "push", "", platforms},
|
{workdir, "issue-598", "push", "", platforms, secrets},
|
||||||
{workdir, "if-env-act", "push", "", platforms},
|
{workdir, "if-env-act", "push", "", platforms, secrets},
|
||||||
{workdir, "env-and-path", "push", "", platforms},
|
{workdir, "env-and-path", "push", "", platforms, secrets},
|
||||||
{workdir, "environment-files", "push", "", platforms},
|
{workdir, "environment-files", "push", "", platforms, secrets},
|
||||||
{workdir, "GITHUB_STATE", "push", "", platforms},
|
{workdir, "GITHUB_STATE", "push", "", platforms, secrets},
|
||||||
{workdir, "environment-files-parser-bug", "push", "", platforms},
|
{workdir, "environment-files-parser-bug", "push", "", platforms, secrets},
|
||||||
{workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms},
|
{workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms, secrets},
|
||||||
{workdir, "outputs", "push", "", platforms},
|
{workdir, "outputs", "push", "", platforms, secrets},
|
||||||
{workdir, "networking", "push", "", platforms},
|
{workdir, "networking", "push", "", platforms, secrets},
|
||||||
{workdir, "steps-context/conclusion", "push", "", platforms},
|
{workdir, "steps-context/conclusion", "push", "", platforms, secrets},
|
||||||
{workdir, "steps-context/outcome", "push", "", platforms},
|
{workdir, "steps-context/outcome", "push", "", platforms, secrets},
|
||||||
{workdir, "job-status-check", "push", "job 'fail' failed", platforms},
|
{workdir, "job-status-check", "push", "job 'fail' failed", platforms, secrets},
|
||||||
{workdir, "if-expressions", "push", "Job 'mytest' failed", platforms},
|
{workdir, "if-expressions", "push", "Job 'mytest' failed", platforms, secrets},
|
||||||
{workdir, "actions-environment-and-context-tests", "push", "", platforms},
|
{workdir, "actions-environment-and-context-tests", "push", "", platforms, secrets},
|
||||||
{workdir, "uses-action-with-pre-and-post-step", "push", "", platforms},
|
{workdir, "uses-action-with-pre-and-post-step", "push", "", platforms, secrets},
|
||||||
{workdir, "evalenv", "push", "", platforms},
|
{workdir, "evalenv", "push", "", platforms, secrets},
|
||||||
{workdir, "ensure-post-steps", "push", "Job 'second-post-step-should-fail' failed", platforms},
|
{workdir, "ensure-post-steps", "push", "Job 'second-post-step-should-fail' failed", platforms, secrets},
|
||||||
{workdir, "workflow_dispatch", "workflow_dispatch", "", platforms},
|
{workdir, "workflow_dispatch", "workflow_dispatch", "", platforms, secrets},
|
||||||
{workdir, "workflow_dispatch_no_inputs_mapping", "workflow_dispatch", "", platforms},
|
{workdir, "workflow_dispatch_no_inputs_mapping", "workflow_dispatch", "", platforms, secrets},
|
||||||
{workdir, "workflow_dispatch-scalar", "workflow_dispatch", "", platforms},
|
{workdir, "workflow_dispatch-scalar", "workflow_dispatch", "", platforms, secrets},
|
||||||
{workdir, "workflow_dispatch-scalar-composite-action", "workflow_dispatch", "", platforms},
|
{workdir, "workflow_dispatch-scalar-composite-action", "workflow_dispatch", "", platforms, secrets},
|
||||||
{"../model/testdata", "strategy", "push", "", platforms}, // TODO: move all testdata into pkg so we can validate it with planner and runner
|
{"../model/testdata", "strategy", "push", "", platforms, secrets}, // TODO: move all testdata into pkg so we can validate it with planner and runner
|
||||||
// {"testdata", "issue-228", "push", "", platforms, }, // TODO [igni]: Remove this once everything passes
|
// {"testdata", "issue-228", "push", "", platforms, }, // TODO [igni]: Remove this once everything passes
|
||||||
{"../model/testdata", "container-volumes", "push", "", platforms},
|
{"../model/testdata", "container-volumes", "push", "", platforms, secrets},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
t.Run(table.workflowPath, func(t *testing.T) {
|
t.Run(table.workflowPath, func(t *testing.T) {
|
||||||
config := &Config{}
|
config := &Config{
|
||||||
|
Secrets: table.secrets,
|
||||||
|
}
|
||||||
|
|
||||||
eventFile := filepath.Join(workdir, table.workflowPath, "event.json")
|
eventFile := filepath.Join(workdir, table.workflowPath, "event.json")
|
||||||
if _, err := os.Stat(eventFile); err == nil {
|
if _, err := os.Stat(eventFile); err == nil {
|
||||||
|
@ -226,51 +233,51 @@ func TestRunEventHostEnvironment(t *testing.T) {
|
||||||
|
|
||||||
tables = append(tables, []TestJobFileInfo{
|
tables = append(tables, []TestJobFileInfo{
|
||||||
// Shells
|
// Shells
|
||||||
{workdir, "shells/defaults", "push", "", platforms},
|
{workdir, "shells/defaults", "push", "", platforms, secrets},
|
||||||
{workdir, "shells/pwsh", "push", "", platforms},
|
{workdir, "shells/pwsh", "push", "", platforms, secrets},
|
||||||
{workdir, "shells/bash", "push", "", platforms},
|
{workdir, "shells/bash", "push", "", platforms, secrets},
|
||||||
{workdir, "shells/python", "push", "", platforms},
|
{workdir, "shells/python", "push", "", platforms, secrets},
|
||||||
{workdir, "shells/sh", "push", "", platforms},
|
{workdir, "shells/sh", "push", "", platforms, secrets},
|
||||||
|
|
||||||
// Local action
|
// Local action
|
||||||
{workdir, "local-action-js", "push", "", platforms},
|
{workdir, "local-action-js", "push", "", platforms, secrets},
|
||||||
|
|
||||||
// Uses
|
// Uses
|
||||||
{workdir, "uses-composite", "push", "", platforms},
|
{workdir, "uses-composite", "push", "", platforms, secrets},
|
||||||
{workdir, "uses-composite-with-error", "push", "Job 'failing-composite-action' failed", platforms},
|
{workdir, "uses-composite-with-error", "push", "Job 'failing-composite-action' failed", platforms, secrets},
|
||||||
{workdir, "uses-nested-composite", "push", "", platforms},
|
{workdir, "uses-nested-composite", "push", "", platforms, secrets},
|
||||||
{workdir, "act-composite-env-test", "push", "", platforms},
|
{workdir, "act-composite-env-test", "push", "", platforms, secrets},
|
||||||
|
|
||||||
// Eval
|
// Eval
|
||||||
{workdir, "evalmatrix", "push", "", platforms},
|
{workdir, "evalmatrix", "push", "", platforms, secrets},
|
||||||
{workdir, "evalmatrixneeds", "push", "", platforms},
|
{workdir, "evalmatrixneeds", "push", "", platforms, secrets},
|
||||||
{workdir, "evalmatrixneeds2", "push", "", platforms},
|
{workdir, "evalmatrixneeds2", "push", "", platforms, secrets},
|
||||||
{workdir, "evalmatrix-merge-map", "push", "", platforms},
|
{workdir, "evalmatrix-merge-map", "push", "", platforms, secrets},
|
||||||
{workdir, "evalmatrix-merge-array", "push", "", platforms},
|
{workdir, "evalmatrix-merge-array", "push", "", platforms, secrets},
|
||||||
{workdir, "issue-1195", "push", "", platforms},
|
{workdir, "issue-1195", "push", "", platforms, secrets},
|
||||||
|
|
||||||
{workdir, "fail", "push", "exit with `FAILURE`: 1", platforms},
|
{workdir, "fail", "push", "exit with `FAILURE`: 1", platforms, secrets},
|
||||||
{workdir, "runs-on", "push", "", platforms},
|
{workdir, "runs-on", "push", "", platforms, secrets},
|
||||||
{workdir, "checkout", "push", "", platforms},
|
{workdir, "checkout", "push", "", platforms, secrets},
|
||||||
{workdir, "remote-action-js", "push", "", platforms},
|
{workdir, "remote-action-js", "push", "", platforms, secrets},
|
||||||
{workdir, "matrix", "push", "", platforms},
|
{workdir, "matrix", "push", "", platforms, secrets},
|
||||||
{workdir, "matrix-include-exclude", "push", "", platforms},
|
{workdir, "matrix-include-exclude", "push", "", platforms, secrets},
|
||||||
{workdir, "commands", "push", "", platforms},
|
{workdir, "commands", "push", "", platforms, secrets},
|
||||||
{workdir, "defaults-run", "push", "", platforms},
|
{workdir, "defaults-run", "push", "", platforms, secrets},
|
||||||
{workdir, "composite-fail-with-output", "push", "", platforms},
|
{workdir, "composite-fail-with-output", "push", "", platforms, secrets},
|
||||||
{workdir, "issue-597", "push", "", platforms},
|
{workdir, "issue-597", "push", "", platforms, secrets},
|
||||||
{workdir, "issue-598", "push", "", platforms},
|
{workdir, "issue-598", "push", "", platforms, secrets},
|
||||||
{workdir, "if-env-act", "push", "", platforms},
|
{workdir, "if-env-act", "push", "", platforms, secrets},
|
||||||
{workdir, "env-and-path", "push", "", platforms},
|
{workdir, "env-and-path", "push", "", platforms, secrets},
|
||||||
{workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms},
|
{workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms, secrets},
|
||||||
{workdir, "outputs", "push", "", platforms},
|
{workdir, "outputs", "push", "", platforms, secrets},
|
||||||
{workdir, "steps-context/conclusion", "push", "", platforms},
|
{workdir, "steps-context/conclusion", "push", "", platforms, secrets},
|
||||||
{workdir, "steps-context/outcome", "push", "", platforms},
|
{workdir, "steps-context/outcome", "push", "", platforms, secrets},
|
||||||
{workdir, "job-status-check", "push", "job 'fail' failed", platforms},
|
{workdir, "job-status-check", "push", "job 'fail' failed", platforms, secrets},
|
||||||
{workdir, "if-expressions", "push", "Job 'mytest' failed", platforms},
|
{workdir, "if-expressions", "push", "Job 'mytest' failed", platforms, secrets},
|
||||||
{workdir, "uses-action-with-pre-and-post-step", "push", "", platforms},
|
{workdir, "uses-action-with-pre-and-post-step", "push", "", platforms, secrets},
|
||||||
{workdir, "evalenv", "push", "", platforms},
|
{workdir, "evalenv", "push", "", platforms, secrets},
|
||||||
{workdir, "ensure-post-steps", "push", "Job 'second-post-step-should-fail' failed", platforms},
|
{workdir, "ensure-post-steps", "push", "Job 'second-post-step-should-fail' failed", platforms, secrets},
|
||||||
}...)
|
}...)
|
||||||
}
|
}
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
|
@ -279,8 +286,8 @@ func TestRunEventHostEnvironment(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tables = append(tables, []TestJobFileInfo{
|
tables = append(tables, []TestJobFileInfo{
|
||||||
{workdir, "windows-prepend-path", "push", "", platforms},
|
{workdir, "windows-prepend-path", "push", "", platforms, secrets},
|
||||||
{workdir, "windows-add-env", "push", "", platforms},
|
{workdir, "windows-add-env", "push", "", platforms, secrets},
|
||||||
}...)
|
}...)
|
||||||
} else {
|
} else {
|
||||||
platforms := map[string]string{
|
platforms := map[string]string{
|
||||||
|
@ -288,8 +295,8 @@ func TestRunEventHostEnvironment(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tables = append(tables, []TestJobFileInfo{
|
tables = append(tables, []TestJobFileInfo{
|
||||||
{workdir, "nix-prepend-path", "push", "", platforms},
|
{workdir, "nix-prepend-path", "push", "", platforms, secrets},
|
||||||
{workdir, "inputs-via-env-context", "push", "", platforms},
|
{workdir, "inputs-via-env-context", "push", "", platforms, secrets},
|
||||||
}...)
|
}...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,17 +316,17 @@ func TestDryrunEvent(t *testing.T) {
|
||||||
|
|
||||||
tables := []TestJobFileInfo{
|
tables := []TestJobFileInfo{
|
||||||
// Shells
|
// Shells
|
||||||
{workdir, "shells/defaults", "push", "", platforms},
|
{workdir, "shells/defaults", "push", "", platforms, secrets},
|
||||||
{workdir, "shells/pwsh", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}}, // custom image with pwsh
|
{workdir, "shells/pwsh", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}, secrets}, // custom image with pwsh
|
||||||
{workdir, "shells/bash", "push", "", platforms},
|
{workdir, "shells/bash", "push", "", platforms, secrets},
|
||||||
{workdir, "shells/python", "push", "", map[string]string{"ubuntu-latest": "node:16-buster"}}, // slim doesn't have python
|
{workdir, "shells/python", "push", "", map[string]string{"ubuntu-latest": "node:16-buster"}, secrets}, // slim doesn't have python
|
||||||
{workdir, "shells/sh", "push", "", platforms},
|
{workdir, "shells/sh", "push", "", platforms, secrets},
|
||||||
|
|
||||||
// Local action
|
// Local action
|
||||||
{workdir, "local-action-docker-url", "push", "", platforms},
|
{workdir, "local-action-docker-url", "push", "", platforms, secrets},
|
||||||
{workdir, "local-action-dockerfile", "push", "", platforms},
|
{workdir, "local-action-dockerfile", "push", "", platforms, secrets},
|
||||||
{workdir, "local-action-via-composite-dockerfile", "push", "", platforms},
|
{workdir, "local-action-via-composite-dockerfile", "push", "", platforms, secrets},
|
||||||
{workdir, "local-action-js", "push", "", platforms},
|
{workdir, "local-action-js", "push", "", platforms, secrets},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
|
|
77
pkg/runner/testdata/.github/workflows/local-reusable-workflow.yml
vendored
Normal file
77
pkg/runner/testdata/.github/workflows/local-reusable-workflow.yml
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
name: reusable
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
string_required:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
string_optional:
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: string
|
||||||
|
bool_required:
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
bool_optional:
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
number_required:
|
||||||
|
required: true
|
||||||
|
type: number
|
||||||
|
number_optional:
|
||||||
|
required: false
|
||||||
|
type: number
|
||||||
|
default: ${{ 1 }}
|
||||||
|
outputs:
|
||||||
|
output:
|
||||||
|
description: "A workflow output"
|
||||||
|
value: ${{ jobs.reusable_workflow_job.outputs.output }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
reusable_workflow_job:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: test required string
|
||||||
|
run: |
|
||||||
|
echo inputs.string_required=${{ inputs.string_required }}
|
||||||
|
[[ "${{ inputs.string_required == 'string' }}" = "true" ]] || exit 1
|
||||||
|
|
||||||
|
- name: test optional string
|
||||||
|
run: |
|
||||||
|
echo inputs.string_optional=${{ inputs.string_optional }}
|
||||||
|
[[ "${{ inputs.string_optional == 'string' }}" = "true" ]] || exit 1
|
||||||
|
|
||||||
|
- name: test required bool
|
||||||
|
run: |
|
||||||
|
echo inputs.bool_required=${{ inputs.bool_required }}
|
||||||
|
[[ "${{ inputs.bool_required }}" = "true" ]] || exit 1
|
||||||
|
|
||||||
|
- name: test optional bool
|
||||||
|
run: |
|
||||||
|
echo inputs.bool_optional=${{ inputs.bool_optional }}
|
||||||
|
[[ "${{ inputs.bool_optional }}" = "true" ]] || exit 1
|
||||||
|
|
||||||
|
- name: test required number
|
||||||
|
run: |
|
||||||
|
echo inputs.number_required=${{ inputs.number_required }}
|
||||||
|
[[ "${{ inputs.number_required == 1 }}" = "true" ]] || exit 1
|
||||||
|
|
||||||
|
- name: test optional number
|
||||||
|
run: |
|
||||||
|
echo inputs.number_optional=${{ inputs.number_optional }}
|
||||||
|
[[ "${{ inputs.number_optional == 1 }}" = "true" ]] || exit 1
|
||||||
|
|
||||||
|
- name: test secret
|
||||||
|
run: |
|
||||||
|
echo secrets.secret=${{ secrets.secret }}
|
||||||
|
[[ "${{ secrets.secret == 'keep_it_private' }}" = "true" ]] || exit 1
|
||||||
|
|
||||||
|
- name: test output
|
||||||
|
id: output_test
|
||||||
|
run: |
|
||||||
|
echo "value=${{ inputs.string_required }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
output: ${{ steps.output_test.outputs.value }}
|
36
pkg/runner/testdata/uses-workflow/local-workflow.yml
vendored
Normal file
36
pkg/runner/testdata/uses-workflow/local-workflow.yml
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
name: local-reusable-workflows
|
||||||
|
on: pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
reusable-workflow:
|
||||||
|
uses: ./.github/workflows/local-reusable-workflow.yml
|
||||||
|
with:
|
||||||
|
string_required: string
|
||||||
|
bool_required: ${{ true }}
|
||||||
|
number_required: 1
|
||||||
|
secrets:
|
||||||
|
secret: keep_it_private
|
||||||
|
|
||||||
|
reusable-workflow-with-inherited-secrets:
|
||||||
|
uses: ./.github/workflows/local-reusable-workflow.yml
|
||||||
|
with:
|
||||||
|
string_required: string
|
||||||
|
bool_required: ${{ true }}
|
||||||
|
number_required: 1
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
output-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- reusable-workflow
|
||||||
|
- reusable-workflow-with-inherited-secrets
|
||||||
|
steps:
|
||||||
|
- name: output with secrets map
|
||||||
|
run: |
|
||||||
|
echo reusable-workflow.output=${{ needs.reusable-workflow.outputs.output }}
|
||||||
|
[[ "${{ needs.reusable-workflow.outputs.output == 'string' }}" = "true" ]] || exit 1
|
||||||
|
|
||||||
|
- name: output with inherited secrets
|
||||||
|
run: |
|
||||||
|
echo reusable-workflow-with-inherited-secrets.output=${{ needs.reusable-workflow-with-inherited-secrets.outputs.output }}
|
||||||
|
[[ "${{ needs.reusable-workflow-with-inherited-secrets.outputs.output == 'string' }}" = "true" ]] || exit 1
|
Loading…
Reference in a new issue