diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/dynamic_variable.go b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/dynamic_variable.go index 44312c566f..12bb834ebe 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/dynamic_variable.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/dynamic_variable.go @@ -45,7 +45,8 @@ func getJobVariableKey(currentJobName, serviceName, moduleName, key string, getA return resp } -func renderScriptedVariableOptions(serviceName, moduleName, script, callFunction string, userInput map[string]string) ([]string, error) { +// Render Scripted Variable Options +func RenderScriptedVariableOptions(serviceName, moduleName, script, callFunction string, userInput map[string]string) ([]string, error) { if script == "" || callFunction == "" { return []string{}, nil } @@ -60,12 +61,13 @@ func renderScriptedVariableOptions(serviceName, moduleName, script, callFunction } t.Option("missingkey=error") + renderInput := make(map[string]string, len(userInput)) for key, val := range userInput { - userInput[key] = "`" + val + "`" + renderInput[key] = "`" + val + "`" } var realCallFunc bytes.Buffer - err = t.Execute(&realCallFunc, userInput) + err = t.Execute(&realCallFunc, renderInput) if err != nil { return nil, fmt.Errorf("渲染调用函数失败, 错误: %v", err) } diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_build.go b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_build.go index 848cea549b..90f4409232 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_build.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_build.go @@ -1076,7 +1076,7 @@ func (j BuildJobController) RenderDynamicVariableOptions(key string, option *Ren // Find the KeyVal with the given key for _, kv := range targetBuild.KeyVals { if kv.Key == key { - resp, err := renderScriptedVariableOptions(option.ServiceName, option.ServiceModule, kv.Script, kv.CallFunction, option.Values) + resp, err := RenderScriptedVariableOptions(option.ServiceName, option.ServiceModule, kv.Script, kv.CallFunction, option.Values) if err != nil { err = fmt.Errorf("Failed to render kv for key: %s, error: %s", key, err) return nil, err diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_freestyle.go b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_freestyle.go index 94fdf65d1b..47a931235f 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_freestyle.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_freestyle.go @@ -343,7 +343,7 @@ func (j FreestyleJobController) GetUsedRepos() ([]*types.Repository, error) { func (j FreestyleJobController) RenderDynamicVariableOptions(key string, option *RenderDynamicVariableValue) ([]string, error) { for _, kv := range j.jobSpec.Envs { if kv.Key == key { - resp, err := renderScriptedVariableOptions(option.ServiceName, option.ServiceModule, kv.Script, kv.CallFunction, option.Values) + resp, err := RenderScriptedVariableOptions(option.ServiceName, option.ServiceModule, kv.Script, kv.CallFunction, option.Values) if err != nil { err = fmt.Errorf("Failed to render kv for key: %s, error: %s", key, err) return nil, err diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_plugin.go b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_plugin.go index 119bd493bf..86ee177cf2 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_plugin.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_plugin.go @@ -189,7 +189,7 @@ func (j PluginJobController) GetUsedRepos() ([]*types.Repository, error) { func (j PluginJobController) RenderDynamicVariableOptions(key string, option *RenderDynamicVariableValue) ([]string, error) { for _, kv := range j.jobSpec.Plugin.Inputs { if kv.Name == key { - resp, err := renderScriptedVariableOptions(option.ServiceName, option.ServiceModule, kv.Script, kv.CallFunction, option.Values) + resp, err := RenderScriptedVariableOptions(option.ServiceName, option.ServiceModule, kv.Script, kv.CallFunction, option.Values) if err != nil { err = fmt.Errorf("Failed to render kv for key: %s, error: %s", key, err) return nil, err diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/controller/workflow.go b/pkg/microservice/aslan/core/workflow/service/workflow/controller/workflow.go index cc48b3b19e..3326165549 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/workflow.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/workflow.go @@ -394,6 +394,101 @@ func (w *Workflow) getWorkflowDefaultParams(taskID int64, creator, account, uid return resp, nil } +// sanitizeDynamicVariableKey sanitizes the dynamic variable key by replacing special characters with underscores. +func sanitizeDynamicVariableKey(key string) string { + key = strings.ReplaceAll(key, "-", "_") + key = strings.ReplaceAll(key, ".", "_") + return key +} + +// GetWorkflowParamReferableVariables returns the workflow param referable variables. +func (w *Workflow) GetWorkflowParamReferableVariables(taskID int64, creator, account, uid string, releasePlan *commonmodels.ReleasePlanRef) ([]*commonmodels.KeyVal, error) { + globalParams, err := w.getWorkflowDefaultParams(taskID, creator, account, uid, releasePlan) + if err != nil { + return nil, fmt.Errorf("get workflow default params error: %v", err) + } + + resp := make([]*commonmodels.KeyVal, 0, len(globalParams)) + for _, param := range globalParams { + if param.ParamsType == "repo" || param.ParamsType == "file" { + continue + } + + resp = append(resp, &commonmodels.KeyVal{ + Key: param.Name, + Value: param.GetValue(), + Type: "string", + IsCredential: param.IsCredential, + }) + } + + return resp, nil +} + +func (w *Workflow) getWorkflowParamDynamicValueMap(taskID int64, creator, account, uid string, releasePlan *commonmodels.ReleasePlanRef) (map[string]string, error) { + globalParams, err := w.GetWorkflowParamReferableVariables(taskID, creator, account, uid, releasePlan) + if err != nil { + return nil, err + } + + resp := make(map[string]string, len(globalParams)) + for _, param := range globalParams { + if param.Value == "" { + continue + } + resp[sanitizeDynamicVariableKey(param.Key)] = param.Value + } + + return resp, nil +} + +// RenderWorkflowDynamicParams renders the workflow dynamic params. +func (w *Workflow) RenderWorkflowDynamicParams(taskID int64, creator, account, uid string, releasePlan *commonmodels.ReleasePlanRef) error { + valueMap, err := w.getWorkflowParamDynamicValueMap(taskID, creator, account, uid, releasePlan) + if err != nil { + return fmt.Errorf("get workflow param dynamic value map error: %v", err) + } + + for _, param := range w.Params { + if param.ParamsType == "repo" || param.ParamsType == "file" || param.Script == "" || param.CallFunction == "" { + continue + } + + resp, err := jobctrl.RenderScriptedVariableOptions("", "", param.Script, param.CallFunction, valueMap) + if err != nil { + return fmt.Errorf("render workflow param %s dynamic options error: %v", param.Name, err) + } + + param.ChoiceOption = resp + if param.GetValue() != "" { + valueMap[sanitizeDynamicVariableKey(strings.Join([]string{"workflow", "params", param.Name}, "."))] = param.GetValue() + } + } + + return nil +} + +func (w *Workflow) GetWorkflowParamDynamicValues(taskID int64, creator, account, uid string, key string, releasePlan *commonmodels.ReleasePlanRef) ([]string, error) { + valueMap, err := w.getWorkflowParamDynamicValueMap(taskID, creator, account, uid, releasePlan) + if err != nil { + return nil, fmt.Errorf("get workflow param dynamic value map error: %v", err) + } + + for _, param := range w.Params { + if param.Name != key && strings.Join([]string{"workflow", "params", param.Name}, ".") != key { + continue + } + + resp, err := jobctrl.RenderScriptedVariableOptions("", "", param.Script, param.CallFunction, valueMap) + if err != nil { + return nil, fmt.Errorf("render workflow param %s dynamic options error: %v", param.Name, err) + } + return resp, nil + } + + return nil, fmt.Errorf("workflow param %s not found", key) +} + func (w *Workflow) Validate(isExecution bool) error { if w.Project == "" { err := fmt.Errorf("project should not be empty") diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/types.go b/pkg/microservice/aslan/core/workflow/service/workflow/types.go index ddc0b45bea..3c5365b51f 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/types.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/types.go @@ -1578,6 +1578,8 @@ type Param struct { Repo *types.Repository `bson:"repo" json:"repo" yaml:"repo,omitempty"` ChoiceOption []string `bson:"choice_option,omitempty" json:"choice_option,omitempty" yaml:"choice_option,omitempty"` ChoiceValue []string `bson:"choice_value,omitempty" json:"choice_value,omitempty" yaml:"choice_value,omitempty"` + Script string `bson:"script,omitempty" json:"script,omitempty" yaml:"script,omitempty"` + CallFunction string `bson:"call_function,omitempty" json:"call_function,omitempty" yaml:"call_function,omitempty"` Default string `bson:"default" json:"default" yaml:"default"` IsCredential bool `bson:"is_credential" json:"is_credential" yaml:"is_credential"` Source config.ParamSourceType `bson:"source,omitempty" json:"source,omitempty" yaml:"source,omitempty"` diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go index 5e95e09336..22331dd3c4 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go @@ -248,6 +248,7 @@ type DistributeImageJobSpec struct { DistributeTarget []*step.DistributeTaskTarget `bson:"distribute_target" json:"distribute_target"` } +// GetWorkflowV4Preset returns the workflow preset. func GetWorkflowV4Preset(encryptedKey, workflowName, uid, username, ticketID string, log *zap.SugaredLogger) (*commonmodels.WorkflowV4, error) { workflow, err := commonrepo.NewWorkflowV4Coll().Find(workflowName) if err != nil { @@ -270,6 +271,12 @@ func GetWorkflowV4Preset(encryptedKey, workflowName, uid, username, ticketID str return nil, e.ErrPresetWorkflow.AddDesc(err.Error()) } + // Render workflow dynamic params + if err := workflowCtrl.RenderWorkflowDynamicParams(0, username, username, uid, nil); err != nil { + log.Errorf("failed to render workflow dynamic params for workflow: %s, the error is: %v", workflowName, err) + return nil, e.ErrPresetWorkflow.AddDesc(err.Error()) + } + if err := ensureWorkflowV4Resp(encryptedKey, workflow, log); err != nil { return workflow, err } @@ -281,6 +288,22 @@ func GetAvailableWorkflowV4DynamicVariable(ctx *internalhandler.Context, workflo resp := make([]string, 0) workflowCtrl := workflowController.CreateWorkflowController(workflow) + + if jobName == "" { + variables, err := workflowCtrl.GetWorkflowParamReferableVariables(0, "", "", "", nil) + if err != nil { + err = fmt.Errorf("failed to get workflow param dynamic variables, error: %v", err) + ctx.Logger.Error(err) + return nil, err + } + + for _, kv := range variables { + resp = append(resp, fmt.Sprintf("{{.%s}}", kv.Key)) + } + + return resp, nil + } + variables, err := workflowCtrl.GetReferableVariables(jobName, workflowController.GetWorkflowVariablesOption{ GetAggregatedVariables: false, GetRuntimeVariables: false, @@ -305,6 +328,18 @@ func GetWorkflowV4DynamicVariableValues(ctx *internalhandler.Context, workflow * resp := make([]string, 0) workflowCtrl := workflowController.CreateWorkflowController(workflow) + + // When jobName is empty, render dynamic options for workflow-level params instead of job inputs. + if jobName == "" { + resp, err := workflowCtrl.GetWorkflowParamDynamicValues(0, "", "", "", key, nil) + if err != nil { + err = fmt.Errorf("failed to render workflow param dynamic variables, error: %v", err) + ctx.Logger.Error(err) + return nil, err + } + return resp, nil + } + variables, err := workflowCtrl.GetReferableVariables(jobName, workflowController.GetWorkflowVariablesOption{ GetAggregatedVariables: false, GetRuntimeVariables: false,