diff --git a/engine/worker/internal/runV2.go b/engine/worker/internal/runV2.go index 0a80c0bf8a..ae27bb5b33 100644 --- a/engine/worker/internal/runV2.go +++ b/engine/worker/internal/runV2.go @@ -649,7 +649,16 @@ func (w *CurrentWorker) runJobStepAction(ctx context.Context, step sdk.ActionSte return w.failJob(ctx, actionError.Error()), stepPostAction } - if err := w.updateParentStepStatusWithOutputs(ctx, parentStepStatus, parentStepName, *actionContext, w.actions[name].Outputs); err != nil { + outputs := w.actions[name].Outputs + resolvedOutputs, err := w.computeOutputs(ctx, *actionContext, outputs) + if err != nil { + return w.failJob(ctx, err.Error()), stepPostAction + } + + if err := w.collectActionOutputs(outputs, resolvedOutputs); err != nil { + return w.failJob(ctx, err.Error()), stepPostAction + } + if err := w.updateParentStepStatusWithOutputs(ctx, parentStepStatus, parentStepName, *actionContext, resolvedOutputs); err != nil { return w.failJob(ctx, err.Error()), stepPostAction } @@ -669,21 +678,31 @@ func (w *CurrentWorker) runJobStepAction(ctx context.Context, step sdk.ActionSte return actionResult, stepPostAction } -func (w *CurrentWorker) updateParentStepStatusWithOutputs(ctx context.Context, parentStepStatus sdk.JobStepsStatus, parentStepName string, actionContext sdk.WorkflowRunJobsContext, outputs map[string]sdk.ActionOutput) error { +func (w *CurrentWorker) collectActionOutputs(outputs map[string]sdk.ActionOutput, resolvedOutputs map[string]string) error { + for k, output := range outputs { + switch output.Type { + case sdk.ActionOutputTypePath: + if v, ok := resolvedOutputs[k]; ok { + w.paths = append(w.paths, v) + } + default: + // noop + } + } + return nil +} + +func (w *CurrentWorker) updateParentStepStatusWithOutputs(ctx context.Context, parentStepStatus sdk.JobStepsStatus, parentStepName string, actionContext sdk.WorkflowRunJobsContext, outputs map[string]string) error { parentStep := parentStepStatus[parentStepName] parentStep.Outputs = sdk.JobResultOutput{} - resolvedOutpus, err := w.computeOutputs(ctx, actionContext, outputs) - if err != nil { - return err - } - - for name, value := range resolvedOutpus { + for name, value := range outputs { parentStep.Outputs[name] = value } parentStepStatus[parentStepName] = parentStep return nil } + func (w *CurrentWorker) computeOutputs(ctx context.Context, actionContext sdk.WorkflowRunJobsContext, outputs map[string]sdk.ActionOutput) (map[string]string, error) { resolvedOutput := make(map[string]string) @@ -798,7 +817,7 @@ func (w *CurrentWorker) failJob(ctx context.Context, reason string) sdk.V2Workfl return res } -// For actions, only pass CDS/GIT/ENV/Integration context from parrent +// For actions, only pass CDS/GIT/ENV/Integration context from parent func (w *CurrentWorker) computeContextForAction(ctx context.Context, parentContext sdk.WorkflowRunJobsContext, inputs map[string]string) (*sdk.WorkflowRunJobsContext, error) { actionContext := sdk.WorkflowRunJobsContext{ WorkflowRunContext: sdk.WorkflowRunContext{ @@ -894,10 +913,37 @@ func (w *CurrentWorker) GetEnvVariable(ctx context.Context, contexts sdk.Workflo newEnvVar[k] = v } } + if len(w.paths) != 0 { + // Inject paths from the previous steps outputs. + pathList := filepath.SplitList(os.Getenv("PATH")) + if len(pathList) == 0 { + pathList = []string{""} + } + // Add current worker paths to the path list. + pathList = append(pathList, w.paths...) + + // Remove duplicate paths. + pathList = removeDuplicate(pathList) + // Inject supercharged PATH environ variable. + newEnvVar["PATH"] = strings.Join(pathList, string(filepath.ListSeparator)) + } return newEnvVar, nil } +func removeDuplicate[T comparable](s []T) []T { + all := make(map[T]bool) + + var list []T + for _, item := range s { + if _, ok := all[item]; !ok { + all[item] = true + list = append(list, item) + } + } + return list +} + func computeIntegrationConfigToEnvVar(integ sdk.JobIntegrationsContext, prefix string) map[string]string { envVars := make(map[string]string) configValues := flatMap(integ.Config) diff --git a/engine/worker/internal/types.go b/engine/worker/internal/types.go index 5642433975..759a8d7aba 100644 --- a/engine/worker/internal/types.go +++ b/engine/worker/internal/types.go @@ -92,6 +92,7 @@ type CurrentWorker struct { client cdsclient.WorkerInterface blur *sdk.Blur hooks []workerHook + paths []string } type workerHook struct { @@ -398,7 +399,7 @@ func (wk *CurrentWorker) Environ() []string { newEnv = append(newEnv, e) } - newEnv = append(newEnv, "CDS_KEY=********") //We have to let it here for some legacy reason + newEnv = append(newEnv, "CDS_KEY=********") // We have to let it here for some legacy reason newEnv = append(newEnv, fmt.Sprintf("%s=%d", WorkerServerPort, wk.HTTPPort())) newEnv = append(newEnv, fmt.Sprintf("%s=%s", CDSApiUrl, wk.cfg.APIEndpoint)) newEnv = append(newEnv, fmt.Sprintf("%s=%s", CDSCDNUrl, wk.cfg.CDNEndpoint)) @@ -427,7 +428,7 @@ func (wk *CurrentWorker) Environ() []string { newEnv = append(newEnv, "BASEDIR="+wk.cfg.Basedir) } - //set up environment variables from pipeline build job parameters + // set up environment variables from pipeline build job parameters for _, p := range wk.currentJob.params { // avoid put private key in environment var as it's a binary value if strings.HasPrefix(p.Name, "cds.key.") && strings.HasSuffix(p.Name, ".priv") { @@ -449,7 +450,7 @@ func (wk *CurrentWorker) Environ() []string { newEnv = append(newEnv, fmt.Sprintf("%s=%s", envName, sdk.OneLineValue(p.Value))) } - //Set env variables from hooks + // Set env variables from hooks for k, v := range wk.currentJob.envFromHooks { newEnv = append(newEnv, k+"="+sdk.OneLineValue(v)) } diff --git a/sdk/v2_action.go b/sdk/v2_action.go index 1bd9cd847d..32bbdb5eb2 100644 --- a/sdk/v2_action.go +++ b/sdk/v2_action.go @@ -29,9 +29,17 @@ type ActionInput struct { Default string `json:"default,omitempty" jsonschema_extras:"order=1" jsonschema_description:"Default input value used if the caller do not specified anything"` } +type ActionOutputType string + +const ( + ActionOutputTypeDefault ActionOutputType = "" + ActionOutputTypePath = "path" +) + type ActionOutput struct { - Description string `json:"description,omitempty" jsonschema_extras:"order=2"` - Value string `json:"value" jsonschema_extras:"order=1"` + Description string `json:"description,omitempty" jsonschema_extras:"order=2"` + Value string `json:"value" jsonschema_extras:"order=1"` + Type ActionOutputType `json:"type,omitempty" jsonschema_extras:"order=3"` } type ActionStep struct {