github.com/nektos/act@v0.2.83/pkg/runner/action.go (about) 1 package runner 2 3 import ( 4 "context" 5 "embed" 6 "errors" 7 "fmt" 8 "io" 9 "io/fs" 10 "os" 11 "path" 12 "path/filepath" 13 "regexp" 14 "runtime" 15 "strings" 16 17 "github.com/kballard/go-shellquote" 18 19 "github.com/nektos/act/pkg/common" 20 "github.com/nektos/act/pkg/container" 21 "github.com/nektos/act/pkg/model" 22 ) 23 24 type actionStep interface { 25 step 26 27 getActionModel() *model.Action 28 getCompositeRunContext(context.Context) *RunContext 29 getCompositeSteps() *compositeSteps 30 } 31 32 type readAction func(ctx context.Context, step *model.Step, actionDir string, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error) 33 34 type actionYamlReader func(filename string) (io.Reader, io.Closer, error) 35 36 type fileWriter func(filename string, data []byte, perm fs.FileMode) error 37 38 type runAction func(step actionStep, actionDir string, remoteAction *remoteAction) common.Executor 39 40 //go:embed res/trampoline.js 41 var trampoline embed.FS 42 43 func readActionImpl(ctx context.Context, step *model.Step, actionDir string, actionPath string, readFile actionYamlReader, writeFile fileWriter) (*model.Action, error) { 44 logger := common.Logger(ctx) 45 allErrors := []error{} 46 addError := func(fileName string, err error) { 47 if err != nil { 48 allErrors = append(allErrors, fmt.Errorf("failed to read '%s' from action '%s' with path '%s' of step: %w", fileName, step.String(), actionPath, err)) 49 } else { 50 // One successful read, clear error state 51 allErrors = nil 52 } 53 } 54 reader, closer, err := readFile("action.yml") 55 addError("action.yml", err) 56 if os.IsNotExist(err) { 57 reader, closer, err = readFile("action.yaml") 58 addError("action.yaml", err) 59 if os.IsNotExist(err) { 60 _, closer, err := readFile("Dockerfile") 61 addError("Dockerfile", err) 62 if err == nil { 63 closer.Close() 64 action := &model.Action{ 65 Name: "(Synthetic)", 66 Runs: model.ActionRuns{ 67 Using: "docker", 68 Image: "Dockerfile", 69 }, 70 } 71 logger.Debugf("Using synthetic action %v for Dockerfile", action) 72 return action, nil 73 } 74 if step.With != nil { 75 if val, ok := step.With["args"]; ok { 76 var b []byte 77 if b, err = trampoline.ReadFile("res/trampoline.js"); err != nil { 78 return nil, err 79 } 80 err2 := writeFile(filepath.Join(actionDir, actionPath, "trampoline.js"), b, 0o400) 81 if err2 != nil { 82 return nil, err2 83 } 84 action := &model.Action{ 85 Name: "(Synthetic)", 86 Inputs: map[string]model.Input{ 87 "cwd": { 88 Description: "(Actual working directory)", 89 Required: false, 90 Default: filepath.Join(actionDir, actionPath), 91 }, 92 "command": { 93 Description: "(Actual program)", 94 Required: false, 95 Default: val, 96 }, 97 }, 98 Runs: model.ActionRuns{ 99 Using: "node12", 100 Main: "trampoline.js", 101 }, 102 } 103 logger.Debugf("Using synthetic action %v", action) 104 return action, nil 105 } 106 } 107 } 108 } 109 if allErrors != nil { 110 return nil, errors.Join(allErrors...) 111 } 112 defer closer.Close() 113 114 action, err := model.ReadAction(reader) 115 logger.Debugf("Read action %v from '%s'", action, "Unknown") 116 return action, err 117 } 118 119 func maybeCopyToActionDir(ctx context.Context, step actionStep, actionDir string, actionPath string, containerActionDir string) error { 120 logger := common.Logger(ctx) 121 rc := step.getRunContext() 122 stepModel := step.getStepModel() 123 124 if stepModel.Type() != model.StepTypeUsesActionRemote { 125 return nil 126 } 127 128 var containerActionDirCopy string 129 containerActionDirCopy = strings.TrimSuffix(containerActionDir, actionPath) 130 logger.Debug(containerActionDirCopy) 131 132 if !strings.HasSuffix(containerActionDirCopy, `/`) { 133 containerActionDirCopy += `/` 134 } 135 136 if rc.Config != nil && rc.Config.ActionCache != nil { 137 raction := step.(*stepActionRemote) 138 ta, err := rc.Config.ActionCache.GetTarArchive(ctx, raction.cacheDir, raction.resolvedSha, "") 139 if err != nil { 140 return err 141 } 142 defer ta.Close() 143 return rc.JobContainer.CopyTarStream(ctx, containerActionDirCopy, ta) 144 } 145 146 if err := removeGitIgnore(ctx, actionDir); err != nil { 147 return err 148 } 149 150 return rc.JobContainer.CopyDir(containerActionDirCopy, actionDir+"/", rc.Config.UseGitIgnore)(ctx) 151 } 152 153 func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction) common.Executor { 154 rc := step.getRunContext() 155 stepModel := step.getStepModel() 156 157 return func(ctx context.Context) error { 158 logger := common.Logger(ctx) 159 actionPath := "" 160 if remoteAction != nil && remoteAction.Path != "" { 161 actionPath = remoteAction.Path 162 } 163 164 action := step.getActionModel() 165 logger.Debugf("About to run action %v", action) 166 167 err := setupActionEnv(ctx, step, remoteAction) 168 if err != nil { 169 return err 170 } 171 172 actionLocation := path.Join(actionDir, actionPath) 173 actionName, containerActionDir := getContainerActionPaths(stepModel, actionLocation, rc) 174 175 logger.Debugf("type=%v actionDir=%s actionPath=%s workdir=%s actionCacheDir=%s actionName=%s containerActionDir=%s", stepModel.Type(), actionDir, actionPath, rc.Config.Workdir, rc.ActionCacheDir(), actionName, containerActionDir) 176 177 x := action.Runs.Using 178 switch { 179 case x.IsNode(): 180 if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil { 181 return err 182 } 183 containerArgs := []string{rc.GetNodeToolFullPath(ctx), path.Join(containerActionDir, action.Runs.Main)} 184 logger.Debugf("executing remote job container: %s", containerArgs) 185 186 rc.ApplyExtraPath(ctx, step.getEnv()) 187 188 return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx) 189 case x.IsDocker(): 190 if remoteAction == nil { 191 actionDir = "" 192 actionPath = containerActionDir 193 } 194 return execAsDocker(ctx, step, actionName, actionDir, actionPath, remoteAction == nil, "entrypoint") 195 case x.IsComposite(): 196 if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil { 197 return err 198 } 199 200 return execAsComposite(step)(ctx) 201 default: 202 return fmt.Errorf("The runs.using key must be one of: %v, got %s", []string{ 203 model.ActionRunsUsingDocker, 204 model.ActionRunsUsingNode12, 205 model.ActionRunsUsingNode16, 206 model.ActionRunsUsingNode20, 207 model.ActionRunsUsingNode24, 208 model.ActionRunsUsingComposite, 209 }, action.Runs.Using) 210 } 211 } 212 } 213 214 func setupActionEnv(ctx context.Context, step actionStep, _ *remoteAction) error { 215 rc := step.getRunContext() 216 217 // A few fields in the environment (e.g. GITHUB_ACTION_REPOSITORY) 218 // are dependent on the action. That means we can complete the 219 // setup only after resolving the whole action model and cloning 220 // the action 221 rc.withGithubEnv(ctx, step.getGithubContext(ctx), *step.getEnv()) 222 populateEnvsFromSavedState(step.getEnv(), step, rc) 223 populateEnvsFromInput(ctx, step.getEnv(), step.getActionModel(), rc) 224 225 return nil 226 } 227 228 // https://github.com/nektos/act/issues/228#issuecomment-629709055 229 // files in .gitignore are not copied in a Docker container 230 // this causes issues with actions that ignore other important resources 231 // such as `node_modules` for example 232 func removeGitIgnore(ctx context.Context, directory string) error { 233 gitIgnorePath := path.Join(directory, ".gitignore") 234 if _, err := os.Stat(gitIgnorePath); err == nil { 235 // .gitignore exists 236 common.Logger(ctx).Debugf("Removing %s before docker cp", gitIgnorePath) 237 err := os.Remove(gitIgnorePath) 238 if err != nil { 239 return err 240 } 241 } 242 return nil 243 } 244 245 // TODO: break out parts of function to reduce complexicity 246 // 247 //nolint:gocyclo 248 func execAsDocker(ctx context.Context, step actionStep, actionName, basedir, subpath string, localAction bool, entrypointType string) error { 249 logger := common.Logger(ctx) 250 rc := step.getRunContext() 251 action := step.getActionModel() 252 253 var prepImage common.Executor 254 var image string 255 forcePull := false 256 if strings.HasPrefix(action.Runs.Image, "docker://") { 257 image = strings.TrimPrefix(action.Runs.Image, "docker://") 258 // Apply forcePull only for prebuild docker images 259 forcePull = rc.Config.ForcePull 260 } else { 261 // "-dockeraction" enshures that "./", "./test " won't get converted to "act-:latest", "act-test-:latest" which are invalid docker image names 262 image = fmt.Sprintf("%s-dockeraction:%s", regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(actionName, "-"), "latest") 263 image = fmt.Sprintf("act-%s", strings.TrimLeft(image, "-")) 264 image = strings.ToLower(image) 265 contextDir, fileName := path.Split(path.Join(subpath, action.Runs.Image)) 266 267 anyArchExists, err := container.ImageExistsLocally(ctx, image, "any") 268 if err != nil { 269 return err 270 } 271 272 correctArchExists, err := container.ImageExistsLocally(ctx, image, rc.Config.ContainerArchitecture) 273 if err != nil { 274 return err 275 } 276 277 if anyArchExists && !correctArchExists { 278 wasRemoved, err := container.RemoveImage(ctx, image, true, true) 279 if err != nil { 280 return err 281 } 282 if !wasRemoved { 283 return fmt.Errorf("failed to remove image '%s'", image) 284 } 285 } 286 287 if !correctArchExists || rc.Config.ForceRebuild { 288 logger.Debugf("image '%s' for architecture '%s' will be built from context '%s", image, rc.Config.ContainerArchitecture, contextDir) 289 var buildContext io.ReadCloser 290 if localAction { 291 buildContext, err = rc.JobContainer.GetContainerArchive(ctx, contextDir+"/.") 292 if err != nil { 293 return err 294 } 295 defer buildContext.Close() 296 } else if rc.Config.ActionCache != nil { 297 rstep := step.(*stepActionRemote) 298 buildContext, err = rc.Config.ActionCache.GetTarArchive(ctx, rstep.cacheDir, rstep.resolvedSha, contextDir) 299 if err != nil { 300 return err 301 } 302 defer buildContext.Close() 303 } 304 prepImage = container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{ 305 ContextDir: filepath.Join(basedir, contextDir), 306 Dockerfile: fileName, 307 ImageTag: image, 308 BuildContext: buildContext, 309 Platform: rc.Config.ContainerArchitecture, 310 }) 311 } else { 312 logger.Debugf("image '%s' for architecture '%s' already exists", image, rc.Config.ContainerArchitecture) 313 } 314 } 315 eval := rc.NewStepExpressionEvaluator(ctx, step) 316 cmd, err := shellquote.Split(eval.Interpolate(ctx, step.getStepModel().With["args"])) 317 if err != nil { 318 return err 319 } 320 if len(cmd) == 0 { 321 cmd = action.Runs.Args 322 evalDockerArgs(ctx, step, action, &cmd) 323 } 324 325 entrypoint := strings.Fields(eval.Interpolate(ctx, step.getStepModel().With[entrypointType])) 326 if len(entrypoint) == 0 { 327 if entrypointType == "pre-entrypoint" && action.Runs.PreEntrypoint != "" { 328 entrypoint, err = shellquote.Split(action.Runs.PreEntrypoint) 329 if err != nil { 330 return err 331 } 332 } else if entrypointType == "entrypoint" && action.Runs.Entrypoint != "" { 333 entrypoint, err = shellquote.Split(action.Runs.Entrypoint) 334 if err != nil { 335 return err 336 } 337 } else if entrypointType == "post-entrypoint" && action.Runs.PostEntrypoint != "" { 338 entrypoint, err = shellquote.Split(action.Runs.PostEntrypoint) 339 if err != nil { 340 return err 341 } 342 } else { 343 entrypoint = nil 344 } 345 } 346 stepContainer := newStepContainer(ctx, step, image, cmd, entrypoint) 347 return common.NewPipelineExecutor( 348 prepImage, 349 stepContainer.Pull(forcePull), 350 stepContainer.Remove().IfBool(!rc.Config.ReuseContainers), 351 stepContainer.Create(rc.Config.ContainerCapAdd, rc.Config.ContainerCapDrop), 352 stepContainer.Start(true), 353 ).Finally( 354 stepContainer.Remove().IfBool(!rc.Config.ReuseContainers), 355 ).Finally(stepContainer.Close())(ctx) 356 } 357 358 func evalDockerArgs(ctx context.Context, step step, action *model.Action, cmd *[]string) { 359 rc := step.getRunContext() 360 stepModel := step.getStepModel() 361 362 inputs := make(map[string]string) 363 eval := rc.NewExpressionEvaluator(ctx) 364 // Set Defaults 365 for k, input := range action.Inputs { 366 inputs[k] = eval.Interpolate(ctx, input.Default) 367 } 368 if stepModel.With != nil { 369 for k, v := range stepModel.With { 370 inputs[k] = eval.Interpolate(ctx, v) 371 } 372 } 373 mergeIntoMap(step, step.getEnv(), inputs) 374 375 stepEE := rc.NewStepExpressionEvaluator(ctx, step) 376 for i, v := range *cmd { 377 (*cmd)[i] = stepEE.Interpolate(ctx, v) 378 } 379 mergeIntoMap(step, step.getEnv(), action.Runs.Env) 380 381 ee := rc.NewStepExpressionEvaluator(ctx, step) 382 for k, v := range *step.getEnv() { 383 (*step.getEnv())[k] = ee.Interpolate(ctx, v) 384 } 385 } 386 387 func newStepContainer(ctx context.Context, step step, image string, cmd []string, entrypoint []string) container.Container { 388 rc := step.getRunContext() 389 stepModel := step.getStepModel() 390 rawLogger := common.Logger(ctx).WithField("raw_output", true) 391 logWriter := common.NewLineWriter(rc.commandHandler(ctx), func(s string) bool { 392 if rc.Config.LogOutput { 393 rawLogger.Infof("%s", s) 394 } else { 395 rawLogger.Debugf("%s", s) 396 } 397 return true 398 }) 399 envList := make([]string, 0) 400 for k, v := range *step.getEnv() { 401 envList = append(envList, fmt.Sprintf("%s=%s", k, v)) 402 } 403 404 envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_TOOL_CACHE", "/opt/hostedtoolcache")) 405 envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_OS", "Linux")) 406 envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_ARCH", container.RunnerArch(ctx))) 407 envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_TEMP", "/tmp")) 408 409 binds, mounts := rc.GetBindsAndMounts() 410 networkMode := fmt.Sprintf("container:%s", rc.jobContainerName()) 411 var workdir string 412 if rc.IsHostEnv(ctx) { 413 networkMode = "default" 414 ext := container.LinuxContainerEnvironmentExtensions{} 415 workdir = ext.ToContainerPath(rc.Config.Workdir) 416 } else { 417 workdir = rc.JobContainer.ToContainerPath(rc.Config.Workdir) 418 } 419 stepContainer := container.NewContainer(&container.NewContainerInput{ 420 Cmd: cmd, 421 Entrypoint: entrypoint, 422 WorkingDir: workdir, 423 Image: image, 424 Username: rc.Config.Secrets["DOCKER_USERNAME"], 425 Password: rc.Config.Secrets["DOCKER_PASSWORD"], 426 Name: createContainerName(rc.jobContainerName(), stepModel.ID), 427 Env: envList, 428 Mounts: mounts, 429 NetworkMode: networkMode, 430 Binds: binds, 431 Stdout: logWriter, 432 Stderr: logWriter, 433 Privileged: rc.Config.Privileged, 434 UsernsMode: rc.Config.UsernsMode, 435 Platform: rc.Config.ContainerArchitecture, 436 Options: rc.Config.ContainerOptions, 437 }) 438 return stepContainer 439 } 440 441 func populateEnvsFromSavedState(env *map[string]string, step actionStep, rc *RunContext) { 442 state, ok := rc.IntraActionState[step.getStepModel().ID] 443 if ok { 444 for name, value := range state { 445 envName := fmt.Sprintf("STATE_%s", name) 446 (*env)[envName] = value 447 } 448 } 449 } 450 451 func populateEnvsFromInput(ctx context.Context, env *map[string]string, action *model.Action, rc *RunContext) { 452 eval := rc.NewExpressionEvaluator(ctx) 453 for inputID, input := range action.Inputs { 454 envKey := regexp.MustCompile("[^A-Z0-9-]").ReplaceAllString(strings.ToUpper(inputID), "_") 455 envKey = fmt.Sprintf("INPUT_%s", envKey) 456 if _, ok := (*env)[envKey]; !ok { 457 (*env)[envKey] = eval.Interpolate(ctx, input.Default) 458 } 459 } 460 } 461 462 func getContainerActionPaths(step *model.Step, actionDir string, rc *RunContext) (string, string) { 463 actionName := "" 464 containerActionDir := "." 465 if step.Type() != model.StepTypeUsesActionRemote { 466 actionName = getOsSafeRelativePath(actionDir, rc.Config.Workdir) 467 containerActionDir = rc.JobContainer.ToContainerPath(rc.Config.Workdir) + "/" + actionName 468 actionName = "./" + actionName 469 } else if step.Type() == model.StepTypeUsesActionRemote { 470 actionName = getOsSafeRelativePath(actionDir, rc.ActionCacheDir()) 471 containerActionDir = rc.JobContainer.GetActPath() + "/actions/" + actionName 472 } 473 474 if actionName == "" { 475 actionName = filepath.Base(actionDir) 476 if runtime.GOOS == "windows" { 477 actionName = strings.ReplaceAll(actionName, "\\", "/") 478 } 479 } 480 return actionName, containerActionDir 481 } 482 483 func getOsSafeRelativePath(s, prefix string) string { 484 actionName := strings.TrimPrefix(s, prefix) 485 if runtime.GOOS == "windows" { 486 actionName = strings.ReplaceAll(actionName, "\\", "/") 487 } 488 actionName = strings.TrimPrefix(actionName, "/") 489 490 return actionName 491 } 492 493 func shouldRunPreStep(step actionStep) common.Conditional { 494 return func(ctx context.Context) bool { 495 log := common.Logger(ctx) 496 497 if step.getActionModel() == nil { 498 log.Debugf("skip pre step for '%s': no action model available", step.getStepModel()) 499 return false 500 } 501 502 return true 503 } 504 } 505 506 func hasPreStep(step actionStep) common.Conditional { 507 return func(_ context.Context) bool { 508 action := step.getActionModel() 509 return action.Runs.Using.IsComposite() || 510 (action.Runs.Using.IsNode() && 511 action.Runs.Pre != "") || 512 (action.Runs.Using.IsDocker() && 513 action.Runs.PreEntrypoint != "") 514 } 515 } 516 517 func runPreStep(step actionStep) common.Executor { 518 return func(ctx context.Context) error { 519 logger := common.Logger(ctx) 520 logger.Debugf("run pre step for '%s'", step.getStepModel()) 521 522 rc := step.getRunContext() 523 stepModel := step.getStepModel() 524 action := step.getActionModel() 525 526 // defaults in pre steps were missing, however provided inputs are available 527 populateEnvsFromInput(ctx, step.getEnv(), action, rc) 528 529 // todo: refactor into step 530 var actionDir string 531 var actionPath string 532 var remoteAction *stepActionRemote 533 if remote, ok := step.(*stepActionRemote); ok { 534 actionPath = newRemoteAction(stepModel.Uses).Path 535 actionDir = fmt.Sprintf("%s/%s", rc.ActionCacheDir(), safeFilename(stepModel.Uses)) 536 remoteAction = remote 537 } else { 538 actionDir = filepath.Join(rc.Config.Workdir, stepModel.Uses) 539 actionPath = "" 540 } 541 542 actionLocation := "" 543 if actionPath != "" { 544 actionLocation = path.Join(actionDir, actionPath) 545 } else { 546 actionLocation = actionDir 547 } 548 549 actionName, containerActionDir := getContainerActionPaths(stepModel, actionLocation, rc) 550 551 x := action.Runs.Using 552 switch { 553 case x.IsNode(): 554 if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil { 555 return err 556 } 557 558 containerArgs := []string{rc.GetNodeToolFullPath(ctx), path.Join(containerActionDir, action.Runs.Pre)} 559 logger.Debugf("executing remote job container: %s", containerArgs) 560 561 rc.ApplyExtraPath(ctx, step.getEnv()) 562 563 return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx) 564 565 case x.IsDocker(): 566 if remoteAction == nil { 567 actionDir = "" 568 actionPath = containerActionDir 569 } 570 return execAsDocker(ctx, step, actionName, actionDir, actionPath, remoteAction == nil, "pre-entrypoint") 571 572 case x.IsComposite(): 573 if step.getCompositeSteps() == nil { 574 step.getCompositeRunContext(ctx) 575 } 576 577 if steps := step.getCompositeSteps(); steps != nil && steps.pre != nil { 578 return steps.pre(ctx) 579 } 580 return fmt.Errorf("missing steps in composite action") 581 582 default: 583 return nil 584 } 585 } 586 } 587 588 func shouldRunPostStep(step actionStep) common.Conditional { 589 return func(ctx context.Context) bool { 590 log := common.Logger(ctx) 591 stepResults := step.getRunContext().getStepsContext() 592 stepResult := stepResults[step.getStepModel().ID] 593 594 if stepResult == nil { 595 log.WithField("stepResult", model.StepStatusSkipped).Debugf("skipping post step for '%s'; step was not executed", step.getStepModel()) 596 return false 597 } 598 599 if stepResult.Conclusion == model.StepStatusSkipped { 600 log.WithField("stepResult", model.StepStatusSkipped).Debugf("skipping post step for '%s'; main step was skipped", step.getStepModel()) 601 return false 602 } 603 604 if step.getActionModel() == nil { 605 log.WithField("stepResult", model.StepStatusSkipped).Debugf("skipping post step for '%s': no action model available", step.getStepModel()) 606 return false 607 } 608 609 return true 610 } 611 } 612 613 func hasPostStep(step actionStep) common.Conditional { 614 return func(_ context.Context) bool { 615 action := step.getActionModel() 616 return action.Runs.Using.IsComposite() || 617 (action.Runs.Using.IsNode() && 618 action.Runs.Post != "") || 619 (action.Runs.Using.IsDocker() && 620 action.Runs.PostEntrypoint != "") 621 } 622 } 623 624 func runPostStep(step actionStep) common.Executor { 625 return func(ctx context.Context) error { 626 logger := common.Logger(ctx) 627 logger.Debugf("run post step for '%s'", step.getStepModel()) 628 629 rc := step.getRunContext() 630 stepModel := step.getStepModel() 631 action := step.getActionModel() 632 633 // todo: refactor into step 634 var actionDir string 635 var actionPath string 636 var remoteAction *stepActionRemote 637 if remote, ok := step.(*stepActionRemote); ok { 638 actionPath = newRemoteAction(stepModel.Uses).Path 639 actionDir = fmt.Sprintf("%s/%s", rc.ActionCacheDir(), safeFilename(stepModel.Uses)) 640 remoteAction = remote 641 } else { 642 actionDir = filepath.Join(rc.Config.Workdir, stepModel.Uses) 643 actionPath = "" 644 } 645 646 actionLocation := "" 647 if actionPath != "" { 648 actionLocation = path.Join(actionDir, actionPath) 649 } else { 650 actionLocation = actionDir 651 } 652 653 actionName, containerActionDir := getContainerActionPaths(stepModel, actionLocation, rc) 654 655 x := action.Runs.Using 656 switch { 657 case x.IsNode(): 658 populateEnvsFromSavedState(step.getEnv(), step, rc) 659 populateEnvsFromInput(ctx, step.getEnv(), step.getActionModel(), rc) 660 661 containerArgs := []string{rc.GetNodeToolFullPath(ctx), path.Join(containerActionDir, action.Runs.Post)} 662 logger.Debugf("executing remote job container: %s", containerArgs) 663 664 rc.ApplyExtraPath(ctx, step.getEnv()) 665 666 return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx) 667 668 case x.IsDocker(): 669 if remoteAction == nil { 670 actionDir = "" 671 actionPath = containerActionDir 672 } 673 return execAsDocker(ctx, step, actionName, actionDir, actionPath, remoteAction == nil, "post-entrypoint") 674 675 case x.IsComposite(): 676 if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil { 677 return err 678 } 679 680 if steps := step.getCompositeSteps(); steps != nil && steps.post != nil { 681 return steps.post(ctx) 682 } 683 return fmt.Errorf("missing steps in composite action") 684 685 default: 686 return nil 687 } 688 } 689 }