From be148630dc9c3a28eb290fe1427611ca1f7a2f8c Mon Sep 17 00:00:00 2001 From: YuTang Song <2313186065@qq.com> Date: Wed, 20 May 2026 15:06:45 +0800 Subject: [PATCH 1/4] feat(workflow): add workflow dynamic variables Signed-off-by: YuTang Song <2313186065@qq.com> --- .../controller/job/dynamic_variable.go | 8 +- .../workflow/controller/job/job_build.go | 2 +- .../workflow/controller/job/job_freestyle.go | 2 +- .../workflow/controller/job/job_plugin.go | 2 +- .../service/workflow/controller/workflow.go | 92 +++++++++++++++++++ .../core/workflow/service/workflow/types.go | 2 + .../service/workflow/workflow_task_v4.go | 33 +++++++ 7 files changed, 135 insertions(+), 6 deletions(-) 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 966aaa461b..472eaebdec 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/workflow.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/workflow.go @@ -393,6 +393,98 @@ func (w *Workflow) getWorkflowDefaultParams(taskID int64, creator, account, uid return resp, nil } +func sanitizeDynamicVariableKey(key string) string { + key = strings.ReplaceAll(key, "-", "_") + key = strings.ReplaceAll(key, ".", "_") + return key +} + +func (w *Workflow) GetWorkflowParamReferableVariables(taskID int64, creator, account, uid string) ([]*commonmodels.KeyVal, error) { + globalParams, err := w.getWorkflowDefaultParams(taskID, creator, account, uid) + 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) (map[string]string, error) { + globalParams, err := w.GetWorkflowParamReferableVariables(taskID, creator, account, uid) + 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 +} + +func (w *Workflow) RenderWorkflowDynamicParams(taskID int64, creator, account, uid string) error { + valueMap, err := w.getWorkflowParamDynamicValueMap(taskID, creator, account, uid) + 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) ([]string, error) { + valueMap, err := w.getWorkflowParamDynamicValueMap(taskID, creator, account, uid) + 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 f891568b20..9ae1fe0693 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 @@ -270,6 +270,11 @@ func GetWorkflowV4Preset(encryptedKey, workflowName, uid, username, ticketID str return nil, e.ErrPresetWorkflow.AddDesc(err.Error()) } + if err := workflowCtrl.RenderWorkflowDynamicParams(0, username, username, uid); 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 +286,22 @@ func GetAvailableWorkflowV4DynamicVariable(ctx *internalhandler.Context, workflo resp := make([]string, 0) workflowCtrl := workflowController.CreateWorkflowController(workflow) + + if jobName == "" { + variables, err := workflowCtrl.GetWorkflowParamReferableVariables(0, "", "", "") + 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 +326,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) + 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, From 73543be49f2b5943158b124d69f8dfba49cf86a1 Mon Sep 17 00:00:00 2001 From: YuTang Song <2313186065@qq.com> Date: Wed, 20 May 2026 15:06:45 +0800 Subject: [PATCH 2/4] feat(workflow): add workflow dynamic variables Signed-off-by: YuTang Song <2313186065@qq.com> --- .../controller/job/dynamic_variable.go | 8 +- .../workflow/controller/job/job_build.go | 2 +- .../workflow/controller/job/job_freestyle.go | 2 +- .../workflow/controller/job/job_plugin.go | 2 +- .../service/workflow/controller/workflow.go | 92 +++++++++++++++++++ .../core/workflow/service/workflow/types.go | 2 + .../service/workflow/workflow_task_v4.go | 33 +++++++ 7 files changed, 135 insertions(+), 6 deletions(-) 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..949ea32ba8 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,98 @@ func (w *Workflow) getWorkflowDefaultParams(taskID int64, creator, account, uid return resp, nil } +func sanitizeDynamicVariableKey(key string) string { + key = strings.ReplaceAll(key, "-", "_") + key = strings.ReplaceAll(key, ".", "_") + return key +} + +func (w *Workflow) GetWorkflowParamReferableVariables(taskID int64, creator, account, uid string) ([]*commonmodels.KeyVal, error) { + globalParams, err := w.getWorkflowDefaultParams(taskID, creator, account, uid) + 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) (map[string]string, error) { + globalParams, err := w.GetWorkflowParamReferableVariables(taskID, creator, account, uid) + 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 +} + +func (w *Workflow) RenderWorkflowDynamicParams(taskID int64, creator, account, uid string) error { + valueMap, err := w.getWorkflowParamDynamicValueMap(taskID, creator, account, uid) + 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) ([]string, error) { + valueMap, err := w.getWorkflowParamDynamicValueMap(taskID, creator, account, uid) + 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..484232852c 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 @@ -270,6 +270,11 @@ func GetWorkflowV4Preset(encryptedKey, workflowName, uid, username, ticketID str return nil, e.ErrPresetWorkflow.AddDesc(err.Error()) } + if err := workflowCtrl.RenderWorkflowDynamicParams(0, username, username, uid); 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 +286,22 @@ func GetAvailableWorkflowV4DynamicVariable(ctx *internalhandler.Context, workflo resp := make([]string, 0) workflowCtrl := workflowController.CreateWorkflowController(workflow) + + if jobName == "" { + variables, err := workflowCtrl.GetWorkflowParamReferableVariables(0, "", "", "") + 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 +326,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) + 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, From 42d6d4e3f5d28c1e965a9343738a9ba9dcbb88b4 Mon Sep 17 00:00:00 2001 From: YuTang Song <2313186065@qq.com> Date: Wed, 20 May 2026 15:24:54 +0800 Subject: [PATCH 3/4] refactor(workflow): add release plan argument Signed-off-by: YuTang Song <2313186065@qq.com> --- .../service/workflow/controller/workflow.go | 16 ++++++++-------- .../service/workflow/workflow_task_v4.go | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) 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 949ea32ba8..9a781ba9a7 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/workflow.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/workflow.go @@ -400,8 +400,8 @@ func sanitizeDynamicVariableKey(key string) string { return key } -func (w *Workflow) GetWorkflowParamReferableVariables(taskID int64, creator, account, uid string) ([]*commonmodels.KeyVal, error) { - globalParams, err := w.getWorkflowDefaultParams(taskID, creator, account, uid) +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) } @@ -423,8 +423,8 @@ func (w *Workflow) GetWorkflowParamReferableVariables(taskID int64, creator, acc return resp, nil } -func (w *Workflow) getWorkflowParamDynamicValueMap(taskID int64, creator, account, uid string) (map[string]string, error) { - globalParams, err := w.GetWorkflowParamReferableVariables(taskID, creator, account, uid) +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 } @@ -440,8 +440,8 @@ func (w *Workflow) getWorkflowParamDynamicValueMap(taskID int64, creator, accoun return resp, nil } -func (w *Workflow) RenderWorkflowDynamicParams(taskID int64, creator, account, uid string) error { - valueMap, err := w.getWorkflowParamDynamicValueMap(taskID, creator, account, uid) +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) } @@ -465,8 +465,8 @@ func (w *Workflow) RenderWorkflowDynamicParams(taskID int64, creator, account, u return nil } -func (w *Workflow) GetWorkflowParamDynamicValues(taskID int64, creator, account, uid string, key string) ([]string, error) { - valueMap, err := w.getWorkflowParamDynamicValueMap(taskID, creator, account, uid) +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) } 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 484232852c..a844800be0 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 @@ -270,7 +270,7 @@ func GetWorkflowV4Preset(encryptedKey, workflowName, uid, username, ticketID str return nil, e.ErrPresetWorkflow.AddDesc(err.Error()) } - if err := workflowCtrl.RenderWorkflowDynamicParams(0, username, username, uid); err != nil { + 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()) } @@ -288,7 +288,7 @@ func GetAvailableWorkflowV4DynamicVariable(ctx *internalhandler.Context, workflo workflowCtrl := workflowController.CreateWorkflowController(workflow) if jobName == "" { - variables, err := workflowCtrl.GetWorkflowParamReferableVariables(0, "", "", "") + 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) @@ -329,7 +329,7 @@ func GetWorkflowV4DynamicVariableValues(ctx *internalhandler.Context, workflow * // When jobName is empty, render dynamic options for workflow-level params instead of job inputs. if jobName == "" { - resp, err := workflowCtrl.GetWorkflowParamDynamicValues(0, "", "", "", key) + 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) From 957df41bc27f9ca41d5d5bb84f2169c4e8122027 Mon Sep 17 00:00:00 2001 From: YuTang Song <2313186065@qq.com> Date: Thu, 21 May 2026 17:52:39 +0800 Subject: [PATCH 4/4] pref: add comment Signed-off-by: YuTang Song <2313186065@qq.com> --- .../core/workflow/service/workflow/controller/workflow.go | 3 +++ .../aslan/core/workflow/service/workflow/workflow_task_v4.go | 2 ++ 2 files changed, 5 insertions(+) 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 9a781ba9a7..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,12 +394,14 @@ 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 { @@ -440,6 +442,7 @@ func (w *Workflow) getWorkflowParamDynamicValueMap(taskID int64, creator, accoun 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 { 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 a844800be0..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,7 @@ 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())