From 6910a1d1d9f5451f78c7fd3f0d33b27ec87bb87d Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Thu, 7 May 2026 14:37:15 +0800 Subject: [PATCH 1/6] feat(workflow): pass through webhook payload to runtime notifications Signed-off-by: huanghongbo-hhb --- .../repository/models/wokflow_task_v4.go | 1 + .../core/common/repository/models/workflow.go | 1 + .../common/repository/models/workflow_v4.go | 64 ++- .../jobcontroller/job_notification.go | 423 +++++++++++++++++- .../service/workflowcontroller/workflow.go | 12 +- .../core/common/util/workflow_variables.go | 249 +++++++++++ .../service/webhook/gerrit_workflowv4_task.go | 5 + .../core/workflow/service/webhook/gitee.go | 6 +- .../service/webhook/gitee_workflowv4_task.go | 13 +- .../core/workflow/service/webhook/github.go | 6 +- .../service/webhook/github_workflowv4_task.go | 15 +- .../core/workflow/service/webhook/gitlab.go | 6 +- .../service/webhook/gitlab_workflowv4_task.go | 13 +- .../controller/job/job_notification.go | 13 + .../service/workflow/controller/job/utils.go | 59 +-- .../service/workflow/controller/workflow.go | 59 +-- .../core/workflow/service/workflow/types.go | 36 +- .../service/workflow/workflow_task_v4.go | 102 ++++- pkg/types/repo.go | 10 +- 19 files changed, 915 insertions(+), 178 deletions(-) create mode 100644 pkg/microservice/aslan/core/common/util/workflow_variables.go diff --git a/pkg/microservice/aslan/core/common/repository/models/wokflow_task_v4.go b/pkg/microservice/aslan/core/common/repository/models/wokflow_task_v4.go index 6df9c5bfa8..9da2ad93e3 100644 --- a/pkg/microservice/aslan/core/common/repository/models/wokflow_task_v4.go +++ b/pkg/microservice/aslan/core/common/repository/models/wokflow_task_v4.go @@ -774,6 +774,7 @@ type LarkChat struct { type JobTaskNotificationSpec struct { WebHookType setting.NotifyWebHookType `bson:"webhook_type" yaml:"webhook_type" json:"webhook_type"` + LarkHookNotificationConfig *LarkHookNotificationConfig `bson:"lark_hook_notification_config,omitempty" yaml:"lark_hook_notification_config,omitempty" json:"lark_hook_notification_config,omitempty"` LarkGroupNotificationConfig *LarkGroupNotificationConfig `bson:"lark_group_notification_config,omitempty" yaml:"lark_group_notification_config,omitempty" json:"lark_group_notification_config,omitempty"` LarkPersonNotificationConfig *LarkPersonNotificationConfig `bson:"lark_person_notification_config,omitempty" yaml:"lark_person_notification_config,omitempty" json:"lark_person_notification_config,omitempty"` WechatNotificationConfig *WechatNotificationConfig `bson:"wechat_notification_config,omitempty" yaml:"wechat_notification_config,omitempty" json:"wechat_notification_config,omitempty"` diff --git a/pkg/microservice/aslan/core/common/repository/models/workflow.go b/pkg/microservice/aslan/core/common/repository/models/workflow.go index d9756ba13b..eb02584075 100644 --- a/pkg/microservice/aslan/core/common/repository/models/workflow.go +++ b/pkg/microservice/aslan/core/common/repository/models/workflow.go @@ -486,6 +486,7 @@ type HookPayload struct { DeliveryID string `bson:"delivery_id" json:"delivery_id,omitempty"` CodehostID int `bson:"codehost_id" json:"codehost_id"` EventType string `bson:"event_type" json:"event_type"` + RawPayload string `bson:"raw_payload" json:"raw_payload,omitempty"` } type TargetArgs struct { diff --git a/pkg/microservice/aslan/core/common/repository/models/workflow_v4.go b/pkg/microservice/aslan/core/common/repository/models/workflow_v4.go index 2538206144..a763af15f1 100644 --- a/pkg/microservice/aslan/core/common/repository/models/workflow_v4.go +++ b/pkg/microservice/aslan/core/common/repository/models/workflow_v4.go @@ -1158,12 +1158,12 @@ type NotificationJobSpec struct { LarkGroupNotificationConfig *LarkGroupNotificationConfig `bson:"lark_group_notification_config,omitempty" yaml:"lark_group_notification_config,omitempty" json:"lark_group_notification_config,omitempty"` LarkPersonNotificationConfig *LarkPersonNotificationConfig `bson:"lark_person_notification_config,omitempty" yaml:"lark_person_notification_config,omitempty" json:"lark_person_notification_config,omitempty"` - //LarkHookNotificationConfig *LarkHookNotificationConfig `bson:"lark_hook_notification_config,omitempty" yaml:"lark_hook_notification_config,omitempty" json:"lark_hook_notification_config,omitempty"` - WechatNotificationConfig *WechatNotificationConfig `bson:"wechat_notification_config,omitempty" yaml:"wechat_notification_config,omitempty" json:"wechat_notification_config,omitempty"` - DingDingNotificationConfig *DingDingNotificationConfig `bson:"dingding_notification_config,omitempty" yaml:"dingding_notification_config,omitempty" json:"dingding_notification_config,omitempty"` - MSTeamsNotificationConfig *MSTeamsNotificationConfig `bson:"msteams_notification_config,omitempty" yaml:"msteams_notification_config,omitempty" json:"msteams_notification_config,omitempty"` - MailNotificationConfig *MailNotificationConfig `bson:"mail_notification_config,omitempty" yaml:"mail_notification_config,omitempty" json:"mail_notification_config,omitempty"` - WebhookNotificationConfig *WebhookNotificationConfig `bson:"webhook_notification_config,omitempty" yaml:"webhook_notification_config,omitempty" json:"webhook_notification_config,omitempty"` + LarkHookNotificationConfig *LarkHookNotificationConfig `bson:"lark_hook_notification_config,omitempty" yaml:"lark_hook_notification_config,omitempty" json:"lark_hook_notification_config,omitempty"` + WechatNotificationConfig *WechatNotificationConfig `bson:"wechat_notification_config,omitempty" yaml:"wechat_notification_config,omitempty" json:"wechat_notification_config,omitempty"` + DingDingNotificationConfig *DingDingNotificationConfig `bson:"dingding_notification_config,omitempty" yaml:"dingding_notification_config,omitempty" json:"dingding_notification_config,omitempty"` + MSTeamsNotificationConfig *MSTeamsNotificationConfig `bson:"msteams_notification_config,omitempty" yaml:"msteams_notification_config,omitempty" json:"msteams_notification_config,omitempty"` + MailNotificationConfig *MailNotificationConfig `bson:"mail_notification_config,omitempty" yaml:"mail_notification_config,omitempty" json:"mail_notification_config,omitempty"` + WebhookNotificationConfig *WebhookNotificationConfig `bson:"webhook_notification_config,omitempty" yaml:"webhook_notification_config,omitempty" json:"webhook_notification_config,omitempty"` Content string `bson:"content" yaml:"content" json:"content"` Title string `bson:"title" yaml:"title" json:"title"` @@ -1247,6 +1247,10 @@ func (n *NotificationJobSpec) GenerateNewNotifyConfigWithOldData() error { if n.LarkPersonNotificationConfig == nil { return fmt.Errorf("lark_person_notification_config cannot be empty for type feishu_person notification") } + case setting.NotifyWebHookTypeFeishu: + if n.LarkHookNotificationConfig == nil { + return fmt.Errorf("lark_hook_notification_config cannot be empty for type feishu notification") + } default: // TODO: this code is commented because of chagee old data. uncomment it if possible //return fmt.Errorf("unsupported notification type: %s", n.WebHookType) @@ -1255,44 +1259,56 @@ func (n *NotificationJobSpec) GenerateNewNotifyConfigWithOldData() error { return nil } +type DynamicRecipient struct { + Value string `bson:"value" json:"value" yaml:"value"` + IdentityType string `bson:"identity_type" json:"identity_type" yaml:"identity_type"` +} + // TODO: why is_at_all? it could be done in backend type LarkGroupNotificationConfig struct { - AppID string `bson:"app_id" json:"app_id" yaml:"app_id"` - Chat *LarkChat `bson:"chat" json:"chat" yaml:"chat"` - AtUsers []*lark.UserInfo `bson:"at_users" json:"at_users" yaml:"at_users"` - IsAtAll bool `bson:"is_at_all" json:"is_at_all" yaml:"is_at_all"` + AppID string `bson:"app_id" json:"app_id" yaml:"app_id"` + Chat *LarkChat `bson:"chat" json:"chat" yaml:"chat"` + AtUsers []*lark.UserInfo `bson:"at_users" json:"at_users" yaml:"at_users"` + DynamicRecipients []*DynamicRecipient `bson:"dynamic_recipients" json:"dynamic_recipients" yaml:"dynamic_recipients"` + IsAtAll bool `bson:"is_at_all" json:"is_at_all" yaml:"is_at_all"` } type LarkPersonNotificationConfig struct { - AppID string `bson:"app_id" json:"app_id" yaml:"app_id"` - TargetUsers []*lark.UserInfo `bson:"target_users" json:"target_users" yaml:"target_users"` + AppID string `bson:"app_id" json:"app_id" yaml:"app_id"` + TargetUsers []*lark.UserInfo `bson:"target_users" json:"target_users" yaml:"target_users"` + DynamicRecipients []*DynamicRecipient `bson:"dynamic_recipients" json:"dynamic_recipients" yaml:"dynamic_recipients"` } type LarkHookNotificationConfig struct { - HookAddress string `bson:"hook_address" json:"hook_address" yaml:"hook_address"` - AtUsers []string `bson:"at_users" json:"at_users" yaml:"at_users"` - IsAtAll bool `bson:"is_at_all" json:"is_at_all" yaml:"is_at_all"` + HookAddress string `bson:"hook_address" json:"hook_address" yaml:"hook_address"` + AtUsers []string `bson:"at_users" json:"at_users" yaml:"at_users"` + DynamicRecipients []*DynamicRecipient `bson:"dynamic_recipients" json:"dynamic_recipients" yaml:"dynamic_recipients"` + IsAtAll bool `bson:"is_at_all" json:"is_at_all" yaml:"is_at_all"` } type WechatNotificationConfig struct { - HookAddress string `bson:"hook_address" json:"hook_address" yaml:"hook_address"` - AtUsers []string `bson:"at_users" json:"at_users" yaml:"at_users"` - IsAtAll bool `bson:"is_at_all" json:"is_at_all" yaml:"is_at_all"` + HookAddress string `bson:"hook_address" json:"hook_address" yaml:"hook_address"` + AtUsers []string `bson:"at_users" json:"at_users" yaml:"at_users"` + DynamicRecipients []*DynamicRecipient `bson:"dynamic_recipients" json:"dynamic_recipients" yaml:"dynamic_recipients"` + IsAtAll bool `bson:"is_at_all" json:"is_at_all" yaml:"is_at_all"` } type DingDingNotificationConfig struct { - HookAddress string `bson:"hook_address" json:"hook_address" yaml:"hook_address"` - AtMobiles []string `bson:"at_mobiles" json:"at_mobiles" yaml:"at_mobiles"` - IsAtAll bool `bson:"is_at_all" json:"is_at_all" yaml:"is_at_all"` + HookAddress string `bson:"hook_address" json:"hook_address" yaml:"hook_address"` + AtMobiles []string `bson:"at_mobiles" json:"at_mobiles" yaml:"at_mobiles"` + DynamicRecipients []*DynamicRecipient `bson:"dynamic_recipients" json:"dynamic_recipients" yaml:"dynamic_recipients"` + IsAtAll bool `bson:"is_at_all" json:"is_at_all" yaml:"is_at_all"` } type MSTeamsNotificationConfig struct { - HookAddress string `bson:"hook_address" json:"hook_address" yaml:"hook_address"` - AtEmails []string `bson:"at_emails" json:"at_emails" yaml:"at_emails"` + HookAddress string `bson:"hook_address" json:"hook_address" yaml:"hook_address"` + AtEmails []string `bson:"at_emails" json:"at_emails" yaml:"at_emails"` + DynamicRecipients []*DynamicRecipient `bson:"dynamic_recipients" json:"dynamic_recipients" yaml:"dynamic_recipients"` } type MailNotificationConfig struct { - TargetUsers []*User `bson:"target_users" json:"target_users" yaml:"target_users"` + TargetUsers []*User `bson:"target_users" json:"target_users" yaml:"target_users"` + DynamicRecipients []*DynamicRecipient `bson:"dynamic_recipients" json:"dynamic_recipients" yaml:"dynamic_recipients"` } type WebhookNotificationConfig struct { diff --git a/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_notification.go b/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_notification.go index dd2053f0e7..5c42464f8a 100644 --- a/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_notification.go +++ b/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_notification.go @@ -75,6 +75,14 @@ func (c *NotificationJobCtl) Run(ctx context.Context) { c.job.Status = config.StatusRunning c.ack() + if err := c.prepareRuntimeNotificationFields(); err != nil { + c.logger.Error(err) + c.job.Status = config.StatusFailed + c.job.Error = err.Error() + c.ack() + return + } + if c.jobTaskSpec.WebHookType == setting.NotifyWebhookTypeFeishuApp { larkAtUserIDs := make([]string, 0) @@ -109,6 +117,15 @@ func (c *NotificationJobCtl) Run(ctx context.Context) { c.ack() return } + } else if c.jobTaskSpec.WebHookType == setting.NotifyWebHookTypeFeishu { + err := sendLarkHookMessage(c.workflowCtx.ProjectName, c.workflowCtx.WorkflowName, c.workflowCtx.WorkflowDisplayName, c.workflowCtx.TaskID, c.jobTaskSpec.LarkHookNotificationConfig.HookAddress, c.jobTaskSpec.Title, c.jobTaskSpec.Content, c.jobTaskSpec.LarkHookNotificationConfig.AtUsers, c.jobTaskSpec.LarkHookNotificationConfig.IsAtAll) + if err != nil { + c.logger.Error(err) + c.job.Status = config.StatusFailed + c.job.Error = err.Error() + c.ack() + return + } } else if c.jobTaskSpec.WebHookType == setting.NotifyWebHookTypeMSTeam { err := sendMSTeamsMessage(c.workflowCtx.ProjectName, c.workflowCtx.WorkflowName, c.workflowCtx.WorkflowDisplayName, c.workflowCtx.TaskID, c.jobTaskSpec.MSTeamsNotificationConfig.HookAddress, c.jobTaskSpec.Title, c.jobTaskSpec.Content, c.jobTaskSpec.MSTeamsNotificationConfig.AtEmails) if err != nil { @@ -207,6 +224,330 @@ func (c *NotificationJobCtl) Run(ctx context.Context) { return } +func (c *NotificationJobCtl) prepareRuntimeNotificationFields() error { + keyMap := c.buildRuntimeNotificationKeyMap() + + c.jobTaskSpec.Title = renderNotificationString(c.jobTaskSpec.Title, keyMap) + c.jobTaskSpec.Content = renderNotificationString(c.jobTaskSpec.Content, keyMap) + + if cfg := c.jobTaskSpec.LarkHookNotificationConfig; cfg != nil { + cfg.AtUsers = renderNotificationStrings(cfg.AtUsers, keyMap) + } + if cfg := c.jobTaskSpec.DingDingNotificationConfig; cfg != nil { + cfg.AtMobiles = renderNotificationStrings(cfg.AtMobiles, keyMap) + } + if cfg := c.jobTaskSpec.WechatNotificationConfig; cfg != nil { + cfg.AtUsers = renderNotificationStrings(cfg.AtUsers, keyMap) + } + if cfg := c.jobTaskSpec.MSTeamsNotificationConfig; cfg != nil { + cfg.AtEmails = renderNotificationStrings(cfg.AtEmails, keyMap) + } + return c.resolveDynamicRecipients(keyMap) +} + +func (c *NotificationJobCtl) buildRuntimeNotificationKeyMap() map[string]string { + keyMap := make(map[string]string) + + insertKVs := func(kvs []*commonmodels.KeyVal) { + for _, kv := range kvs { + if kv == nil || kv.Key == "" || kv.GetValue() == "" { + continue + } + keyMap[kv.Key] = kv.GetValue() + } + } + + insertKVs(c.workflowCtx.WorkflowKeyVals) + return keyMap +} + +func renderNotificationStrings(inputs []string, keyMap map[string]string) []string { + if len(keyMap) == 0 { + return inputs + } + pairs := make([]string, 0, len(keyMap)*2) + for key, value := range keyMap { + pairs = append(pairs, "{{."+key+"}}", value) + } + replacer := strings.NewReplacer(pairs...) + + resp := make([]string, 0, len(inputs)) + for _, item := range inputs { + resp = append(resp, replacer.Replace(item)) + } + return resp +} + +func (c *NotificationJobCtl) resolveDynamicRecipients(keyMap map[string]string) error { + if cfg := c.jobTaskSpec.LarkHookNotificationConfig; cfg != nil { + users := c.resolveDynamicRecipientsToDirectValues(cfg.DynamicRecipients, keyMap, "open_id", "user_id", "id") + cfg.AtUsers = lo.Uniq(append(cfg.AtUsers, users...)) + } + if cfg := c.jobTaskSpec.LarkGroupNotificationConfig; cfg != nil { + users, err := c.resolveDynamicRecipientsToLarkUsers(cfg.DynamicRecipients, cfg.AppID, keyMap) + if err != nil { + return err + } + cfg.AtUsers = uniqLarkUsers(append(cfg.AtUsers, users...)) + } + if cfg := c.jobTaskSpec.LarkPersonNotificationConfig; cfg != nil { + users, err := c.resolveDynamicRecipientsToLarkUsers(cfg.DynamicRecipients, cfg.AppID, keyMap) + if err != nil { + return err + } + cfg.TargetUsers = uniqLarkUsers(append(cfg.TargetUsers, users...)) + } + if cfg := c.jobTaskSpec.MSTeamsNotificationConfig; cfg != nil { + emails, err := c.resolveDynamicRecipientsToEmails(cfg.DynamicRecipients, keyMap) + if err != nil { + return err + } + cfg.AtEmails = lo.Uniq(append(cfg.AtEmails, emails...)) + } + if cfg := c.jobTaskSpec.MailNotificationConfig; cfg != nil { + emails, err := c.resolveDynamicRecipientsToEmails(cfg.DynamicRecipients, keyMap) + if err != nil { + return err + } + cfg.TargetUsers = uniqMailUsers(append(cfg.TargetUsers, buildMailUsersFromEmails(emails)...)) + } + if cfg := c.jobTaskSpec.DingDingNotificationConfig; cfg != nil { + mobiles, err := c.resolveDynamicRecipientsToMobiles(cfg.DynamicRecipients, keyMap) + if err != nil { + return err + } + cfg.AtMobiles = lo.Uniq(append(cfg.AtMobiles, mobiles...)) + } + if cfg := c.jobTaskSpec.WechatNotificationConfig; cfg != nil { + users := c.resolveDynamicRecipientsToDirectValues(cfg.DynamicRecipients, keyMap, "user_id", "userid", "id") + cfg.AtUsers = lo.Uniq(append(cfg.AtUsers, users...)) + } + + return nil +} + +func (c *NotificationJobCtl) resolveDynamicRecipientsToLarkUsers(recipients []*commonmodels.DynamicRecipient, appID string, keyMap map[string]string) ([]*lark.UserInfo, error) { + if len(recipients) == 0 { + return nil, nil + } + + client, err := larkservice.GetLarkClientByIMAppID(appID) + if err != nil { + return nil, err + } + + resp := make([]*lark.UserInfo, 0) + for _, recipient := range recipients { + value := renderNotificationString(recipient.Value, keyMap) + if value == "" { + continue + } + + idType, id, err := resolveLarkRecipient(client, recipient.IdentityType, value) + if err != nil { + return nil, err + } + if id == "" { + continue + } + resp = append(resp, &lark.UserInfo{ID: id, IDType: idType}) + } + + return uniqLarkUsers(resp), nil +} + +func (c *NotificationJobCtl) resolveDynamicRecipientsToEmails(recipients []*commonmodels.DynamicRecipient, keyMap map[string]string) ([]string, error) { + resp := make([]string, 0) + for _, recipient := range recipients { + value := renderNotificationString(recipient.Value, keyMap) + if value == "" { + continue + } + + switch recipient.IdentityType { + case "", "email": + resp = append(resp, value) + case "account": + userInfo, err := searchUserByAccount(value) + if err != nil { + return nil, err + } + if userInfo != nil && userInfo.Email != "" { + resp = append(resp, userInfo.Email) + } + } + } + return lo.Uniq(resp), nil +} + +func (c *NotificationJobCtl) resolveDynamicRecipientsToMobiles(recipients []*commonmodels.DynamicRecipient, keyMap map[string]string) ([]string, error) { + resp := make([]string, 0) + for _, recipient := range recipients { + value := renderNotificationString(recipient.Value, keyMap) + if value == "" { + continue + } + + switch recipient.IdentityType { + case "mobile": + resp = append(resp, value) + case "account": + userInfo, err := searchUserByAccount(value) + if err != nil { + return nil, err + } + if userInfo != nil && userInfo.Phone != "" { + resp = append(resp, userInfo.Phone) + } + } + } + return lo.Uniq(resp), nil +} + +func (c *NotificationJobCtl) resolveDynamicRecipientsToDirectValues(recipients []*commonmodels.DynamicRecipient, keyMap map[string]string, supportedTypes ...string) []string { + if len(recipients) == 0 { + return nil + } + supported := make(map[string]struct{}, len(supportedTypes)) + for _, identityType := range supportedTypes { + supported[identityType] = struct{}{} + } + resp := make([]string, 0) + for _, recipient := range recipients { + if _, ok := supported[recipient.IdentityType]; !ok { + continue + } + value := renderNotificationString(recipient.Value, keyMap) + if value == "" { + continue + } + resp = append(resp, value) + } + return lo.Uniq(resp) +} + +func resolveLarkRecipient(client *lark.Client, identityType, value string) (string, string, error) { + switch identityType { + case "", "email": + userInfo, err := client.GetUserIDByEmailOrMobile(lark.QueryTypeEmail, value, setting.LarkUserID) + if err != nil { + return "", "", err + } + return setting.LarkUserID, util2.GetStringFromPointer(userInfo.UserId), nil + case "mobile": + userInfo, err := client.GetUserIDByEmailOrMobile(lark.QueryTypeMobile, value, setting.LarkUserID) + if err != nil { + return "", "", err + } + return setting.LarkUserID, util2.GetStringFromPointer(userInfo.UserId), nil + case "account": + userInfo, err := searchUserByAccount(value) + if err != nil { + return "", "", err + } + if userInfo == nil { + return "", "", nil + } + if userInfo.Email != "" { + larkUser, err := client.GetUserIDByEmailOrMobile(lark.QueryTypeEmail, userInfo.Email, setting.LarkUserID) + if err == nil { + return setting.LarkUserID, util2.GetStringFromPointer(larkUser.UserId), nil + } + } + if userInfo.Phone != "" { + larkUser, err := client.GetUserIDByEmailOrMobile(lark.QueryTypeMobile, userInfo.Phone, setting.LarkUserID) + if err == nil { + return setting.LarkUserID, util2.GetStringFromPointer(larkUser.UserId), nil + } + } + return "", "", nil + default: + return "", "", fmt.Errorf("unsupported lark dynamic recipient identity type: %s", identityType) + } +} + +func searchUserByAccount(account string) (*user.User, error) { + resp, err := user.New().SearchUser(&user.SearchUserArgs{ + Account: account, + Page: 1, + PerPage: 1, + }) + if err != nil { + return nil, err + } + if resp == nil || len(resp.Users) == 0 { + return nil, nil + } + return resp.Users[0], nil +} + +func uniqLarkUsers(users []*lark.UserInfo) []*lark.UserInfo { + seen := make(map[string]struct{}) + resp := make([]*lark.UserInfo, 0, len(users)) + for _, user := range users { + if user == nil || user.ID == "" { + continue + } + key := user.IDType + ":" + user.ID + if _, ok := seen[key]; ok { + continue + } + seen[key] = struct{}{} + resp = append(resp, user) + } + return resp +} + +func buildMailUsersFromEmails(emails []string) []*commonmodels.User { + resp := make([]*commonmodels.User, 0, len(emails)) + for _, email := range lo.Uniq(emails) { + if email == "" { + continue + } + resp = append(resp, &commonmodels.User{ + Type: "email", + UserName: email, + }) + } + return resp +} + +func uniqMailUsers(users []*commonmodels.User) []*commonmodels.User { + seen := make(map[string]struct{}) + resp := make([]*commonmodels.User, 0, len(users)) + for _, user := range users { + if user == nil { + continue + } + key := user.Type + ":" + switch user.Type { + case "email": + key += user.UserName + case setting.UserTypeGroup: + key += user.GroupID + default: + key += user.UserID + } + if _, ok := seen[key]; ok { + continue + } + seen[key] = struct{}{} + resp = append(resp, user) + } + return resp +} + +func renderNotificationString(input string, keyMap map[string]string) string { + if len(keyMap) == 0 || !strings.Contains(input, "{{.") { + return input + } + pairs := make([]string, 0, len(keyMap)*2) + for key, value := range keyMap { + pairs = append(pairs, "{{."+key+"}}", value) + } + return strings.NewReplacer(pairs...).Replace(input) +} + func sendLarkMessage(client *lark.Client, productName, workflowName, workflowDisplayName string, taskID int64, receiverType, receiverID, title, message string, idList []string, isAtAll bool) error { // first generate lark card card := instantmessage.NewLarkCard() @@ -268,6 +609,58 @@ func sendLarkMessage(client *lark.Client, productName, workflowName, workflowDis return nil } +func sendLarkHookMessage(productName, workflowName, workflowDisplayName string, taskID int64, uri, title, message string, idList []string, isAtAll bool) error { + card := instantmessage.NewLarkCard() + card.SetConfig(true) + card.SetHeader("blue", title, "plain_text") + card.AddI18NElementsZhcnFeild(message, true) + + detailURL := fmt.Sprintf("%s/v1/projects/detail/%s/pipelines/custom/%s/%d?display_name=%s", + configbase.SystemAddress(), + productName, + workflowName, + taskID, + workflowDisplayName, + ) + card.AddI18NElementsZhcnAction("点击查看更多信息", detailURL) + + messageReq := instantmessage.LarkCardReq{ + MsgType: "interactive", + Card: card, + } + if _, err := httpclient.New().Post(uri, httpclient.SetBody(messageReq)); err != nil { + return err + } + + if len(idList) == 0 && !isAtAll { + return nil + } + + atUserList := make([]string, 0, len(idList)) + idList = lo.Filter(idList, func(s string, _ int) bool { return s != "All" }) + for _, userID := range idList { + atUserList = append(atUserList, fmt.Sprintf("", userID)) + } + atMessage := strings.Join(atUserList, " ") + if isAtAll { + atMessage += "" + } + + if strings.Contains(uri, "bot/v2/hook") { + _, err := httpclient.New().Post(uri, httpclient.SetBody(&instantmessage.FeiShuMessageV2{ + MsgType: "text", + Content: instantmessage.FeiShuContentV2{Text: atMessage}, + })) + return err + } + + _, err := httpclient.New().Post(uri, httpclient.SetBody(&instantmessage.FeiShuMessage{ + Title: "", + Text: atMessage, + })) + return err +} + func sendDingDingMessage(productName, workflowName, workflowDisplayName string, taskID int64, uri, title, message string, idList []string, isAtAll bool) error { processedMessage := generateDingDingNotificationMessage(title, message, idList) @@ -438,7 +831,35 @@ func sendMailMessage(title, message string, users []*commonmodels.User, callerID return err } - users, userMap := util.GeneFlatUsersWithCaller(users, callerID) + directEmailUsers := make([]*commonmodels.User, 0) + lookupUsers := make([]*commonmodels.User, 0) + for _, u := range users { + if u != nil && u.Type == "email" { + directEmailUsers = append(directEmailUsers, u) + continue + } + lookupUsers = append(lookupUsers, u) + } + + users, userMap := util.GeneFlatUsersWithCaller(lookupUsers, callerID) + for _, u := range directEmailUsers { + log.Infof("Sending Mail to email: %s", u.UserName) + err = mail.SendEmail(&mail.EmailParams{ + From: emailSvc.Address, + To: u.UserName, + Subject: title, + Host: email.Name, + UserName: email.UserName, + Password: email.Password, + Port: email.Port, + TlsSkipVerify: email.TlsSkipVerify, + Body: message, + }) + if err != nil { + log.Errorf("sendMailMessage SendEmail error, error msg:%s", err) + } + } + for _, u := range users { log.Infof("Sending Mail to user: %s", u.UserName) info, ok := userMap[u.UserID] diff --git a/pkg/microservice/aslan/core/common/service/workflowcontroller/workflow.go b/pkg/microservice/aslan/core/common/service/workflowcontroller/workflow.go index 27bc169d66..2312025619 100644 --- a/pkg/microservice/aslan/core/common/service/workflowcontroller/workflow.go +++ b/pkg/microservice/aslan/core/common/service/workflowcontroller/workflow.go @@ -25,11 +25,6 @@ import ( "time" "github.com/google/uuid" - "github.com/koderover/zadig/v2/pkg/tool/clientmanager" - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/rand" config2 "github.com/koderover/zadig/v2/pkg/config" "github.com/koderover/zadig/v2/pkg/microservice/aslan/config" commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" @@ -39,13 +34,19 @@ import ( "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/scmnotify" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/workflowstat" + commonutil "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/util" "github.com/koderover/zadig/v2/pkg/setting" "github.com/koderover/zadig/v2/pkg/tool/cache" + "github.com/koderover/zadig/v2/pkg/tool/clientmanager" e "github.com/koderover/zadig/v2/pkg/tool/errors" "github.com/koderover/zadig/v2/pkg/tool/kube/getter" "github.com/koderover/zadig/v2/pkg/tool/kube/podexec" "github.com/koderover/zadig/v2/pkg/tool/kube/updater" "github.com/koderover/zadig/v2/pkg/tool/log" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/rand" ) const ( @@ -238,6 +239,7 @@ func (c *workflowCtl) Run(ctx context.Context, concurrency int) { WorkflowTaskCreatorUserID: c.workflowTask.TaskCreatorID, WorkflowTaskCreatorMobile: c.workflowTask.TaskCreatorPhone, WorkflowTaskCreatorEmail: c.workflowTask.TaskCreatorEmail, + WorkflowKeyVals: commonutil.BuildWorkflowRuntimeVariableKVs(c.workflowTask.WorkflowArgs, c.workflowTask.ProjectName, c.workflowTask.ProjectDisplayName, c.workflowTask.TaskID, c.workflowTask.TaskCreator, c.workflowTask.TaskCreatorAccount, c.workflowTask.TaskCreatorID, time.Unix(c.workflowTask.StartTime, 0)), Workspace: "/workspace", DistDir: fmt.Sprintf("%s/%s/dist/%d", config.S3StoragePath(), c.workflowTask.WorkflowName, c.workflowTask.TaskID), DockerMountDir: fmt.Sprintf("/tmp/%s/docker/%d", uuid.NewString(), time.Now().Unix()), diff --git a/pkg/microservice/aslan/core/common/util/workflow_variables.go b/pkg/microservice/aslan/core/common/util/workflow_variables.go new file mode 100644 index 0000000000..b66f703ac0 --- /dev/null +++ b/pkg/microservice/aslan/core/common/util/workflow_variables.go @@ -0,0 +1,249 @@ +package util + +import ( + "encoding/json" + "fmt" + "net/url" + "strconv" + "strings" + "time" + + configbase "github.com/koderover/zadig/v2/pkg/config" + commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" + "github.com/koderover/zadig/v2/pkg/types" +) + +func BuildPayloadVariables(rawPayload string) []*commonmodels.KeyVal { + if rawPayload == "" { + return nil + } + + var payload interface{} + if err := json.Unmarshal([]byte(rawPayload), &payload); err != nil { + return nil + } + + resp := make([]*commonmodels.KeyVal, 0) + flattenPayloadValue("payload", payload, &resp) + return resp +} + +func flattenPayloadValue(prefix string, value interface{}, resp *[]*commonmodels.KeyVal) { + switch val := value.(type) { + case map[string]interface{}: + for key, item := range val { + flattenPayloadValue(prefix+"."+key, item, resp) + } + case []interface{}: + for index, item := range val { + flattenPayloadValue(fmt.Sprintf("%s.%d", prefix, index), item, resp) + } + case string: + *resp = append(*resp, &commonmodels.KeyVal{Key: prefix, Value: val, IsCredential: false}) + case float64: + *resp = append(*resp, &commonmodels.KeyVal{Key: prefix, Value: strconv.FormatFloat(val, 'f', -1, 64), IsCredential: false}) + case bool: + *resp = append(*resp, &commonmodels.KeyVal{Key: prefix, Value: strconv.FormatBool(val), IsCredential: false}) + case nil: + return + default: + *resp = append(*resp, &commonmodels.KeyVal{Key: prefix, Value: fmt.Sprint(val), IsCredential: false}) + } +} + +func RepoVariableKVs(repos []*types.Repository) []*commonmodels.KeyVal { + ret := make([]*commonmodels.KeyVal, 0) + for index, repo := range repos { + repoNameIndex := fmt.Sprintf("REPONAME_%d", index) + ret = append(ret, &commonmodels.KeyVal{Key: repoNameIndex, Value: repo.RepoName, IsCredential: false}) + + repoIndex := fmt.Sprintf("REPO_%d", index) + repoName := RepoNameToRepoIndex(repo.RepoName) + ret = append(ret, &commonmodels.KeyVal{Key: repoIndex, Value: repoName, IsCredential: false}) + + if len(repo.Branch) > 0 { + ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_BRANCH", repoName), Value: repo.Branch, IsCredential: false}) + } + + if len(repo.Tag) > 0 { + ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_TAG", repoName), Value: repo.Tag, IsCredential: false}) + } + + if repo.PR > 0 { + ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_PR", repoName), Value: strconv.Itoa(repo.PR), IsCredential: false}) + } + + ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_PRE_MERGE_BRANCHES", repoName), Value: repo.GetPreMergeBranches(), IsCredential: false}) + ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_ORG", repoName), Value: repo.RepoOwner, IsCredential: false}) + + if len(repo.PRs) > 0 { + prStrs := []string{} + for _, pr := range repo.PRs { + prStrs = append(prStrs, strconv.Itoa(pr)) + } + ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_PR", repoName), Value: strings.Join(prStrs, ","), IsCredential: false}) + } + + if len(repo.CommitID) > 0 { + ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_COMMIT_ID", repoName), Value: repo.CommitID, IsCredential: false}) + } + if len(repo.AuthorName) > 0 { + ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_AUTHOR", repoName), Value: repo.AuthorName, IsCredential: false}) + } + if len(repo.Committer) > 0 { + ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_COMMITTER", repoName), Value: repo.Committer, IsCredential: false}) + } + if len(repo.CommitMessage) > 0 { + ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_COMMIT_MESSAGE", repoName), Value: repo.CommitMessage, IsCredential: false}) + } + if len(repo.TargetBranch) > 0 { + ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_TARGET_BRANCH", repoName), Value: repo.TargetBranch, IsCredential: false}) + } + } + return ret +} + +func RepoNameToRepoIndex(repoName string) string { + words := map[rune]string{ + '0': "A", '1': "B", '2': "C", '3': "D", '4': "E", + '5': "F", '6': "G", '7': "H", '8': "I", '9': "J", + } + result := "" + for i, digit := range repoName { + if word, ok := words[digit]; ok { + result += word + } else { + result += repoName[i:] + break + } + } + + result = strings.ReplaceAll(result, "-", "_") + result = strings.ReplaceAll(result, ".", "_") + return result +} + +func CollectWorkflowRepos(workflow *commonmodels.WorkflowV4) []*types.Repository { + if workflow == nil { + return nil + } + + resp := make([]*types.Repository, 0) + repoKeySet := make(map[string]struct{}) + appendRepo := func(repo *types.Repository) { + if repo == nil { + return + } + key := fmt.Sprintf("%d/%s/%s/%s/%s/%d", repo.CodehostID, repo.RepoOwner, repo.RepoNamespace, repo.RepoName, repo.Branch, repo.PR) + if _, ok := repoKeySet[key]; ok { + return + } + repoKeySet[key] = struct{}{} + resp = append(resp, repo) + } + + for _, stage := range workflow.Stages { + for _, jobInfo := range stage.Jobs { + switch spec := jobInfo.Spec.(type) { + case *commonmodels.ZadigBuildJobSpec: + for _, build := range spec.ServiceAndBuilds { + for _, repo := range build.Repos { + appendRepo(repo) + } + } + case *commonmodels.ZadigTestingJobSpec: + for _, testModule := range spec.TestModules { + for _, repo := range testModule.Repos { + appendRepo(repo) + } + } + for _, serviceTest := range spec.ServiceAndTests { + for _, repo := range serviceTest.Repos { + appendRepo(repo) + } + } + case *commonmodels.ZadigScanningJobSpec: + for _, scanning := range spec.Scannings { + for _, repo := range scanning.Repos { + appendRepo(repo) + } + } + for _, serviceScanning := range spec.ServiceAndScannings { + for _, repo := range serviceScanning.Repos { + appendRepo(repo) + } + } + } + } + } + + return resp +} + +func BuildWorkflowSystemVariableKVs(workflow *commonmodels.WorkflowV4, projectName, projectDisplayName string, taskID int64, creator, account, uid string, now time.Time) []*commonmodels.KeyVal { + if workflow == nil { + return nil + } + + resp := []*commonmodels.KeyVal{ + {Key: "project", Value: projectName, IsCredential: false}, + {Key: "project.id", Value: projectName, IsCredential: false}, + {Key: "project.name", Value: projectDisplayName, IsCredential: false}, + {Key: "workflow.id", Value: workflow.Name, IsCredential: false}, + {Key: "workflow.name", Value: workflow.DisplayName, IsCredential: false}, + {Key: "workflow.task.id", Value: fmt.Sprintf("%d", taskID), IsCredential: false}, + {Key: "workflow.task.creator", Value: creator, IsCredential: false}, + {Key: "workflow.task.creator.id", Value: account, IsCredential: false}, + {Key: "workflow.task.creator.userId", Value: uid, IsCredential: false}, + {Key: "workflow.task.timestamp", Value: fmt.Sprintf("%d", now.Unix()), IsCredential: false}, + {Key: "workflow.task.datetime", Value: now.Format(time.DateTime), IsCredential: false}, + { + Key: "workflow.task.url", + Value: fmt.Sprintf("%s/v1/projects/detail/%s/pipelines/custom/%s/%d?display_name=%s", configbase.SystemAddress(), projectName, workflow.Name, taskID, url.QueryEscape(workflow.DisplayName)), + IsCredential: false, + }, + } + + for _, param := range workflow.Params { + if param == nil { + continue + } + value := param.Value + if param.ParamsType == string(commonmodels.MultiSelectType) { + value = strings.Join(param.ChoiceValue, ",") + } else if param.ParamsType == string(commonmodels.FileType) { + continue + } + resp = append(resp, &commonmodels.KeyVal{ + Key: strings.Join([]string{"workflow", "params", param.Name}, "."), + Value: value, + IsCredential: false, + }) + } + if workflow.HookPayload != nil { + resp = append(resp, BuildPayloadVariables(workflow.HookPayload.RawPayload)...) + } + + return resp +} + +func BuildWorkflowRuntimeVariableKVs(workflow *commonmodels.WorkflowV4, projectName, projectDisplayName string, taskID int64, creator, account, uid string, now time.Time) []*commonmodels.KeyVal { + resp := BuildWorkflowSystemVariableKVs(workflow, projectName, projectDisplayName, taskID, creator, account, uid, now) + if workflow == nil { + return resp + } + resp = append(resp, RepoVariableKVs(CollectWorkflowRepos(workflow))...) + + return resp +} + +func KeyValsToMap(kvs []*commonmodels.KeyVal) map[string]string { + resp := make(map[string]string) + for _, kv := range kvs { + if kv == nil || kv.Key == "" || kv.GetValue() == "" { + continue + } + resp[kv.Key] = kv.GetValue() + } + return resp +} diff --git a/pkg/microservice/aslan/core/workflow/service/webhook/gerrit_workflowv4_task.go b/pkg/microservice/aslan/core/workflow/service/webhook/gerrit_workflowv4_task.go index ff7117c8c0..a649f964bb 100644 --- a/pkg/microservice/aslan/core/workflow/service/webhook/gerrit_workflowv4_task.go +++ b/pkg/microservice/aslan/core/workflow/service/webhook/gerrit_workflowv4_task.go @@ -173,6 +173,8 @@ func (gruem *gerritChangeMergedEventMatcherForWorkflowV4) GetHookRepo(hookRepo * RepoOwner: hookRepo.RepoOwner, RepoNamespace: hookRepo.GetRepoNamespace(), Branch: hookRepo.Branch, + TargetBranch: hookRepo.Branch, + Committer: hookRepo.Committer, Source: hookRepo.Source, } } @@ -223,7 +225,9 @@ func (gpcem *gerritPatchsetCreatedEventMatcherForWorkflowV4) GetHookRepo(hookRep RepoOwner: hookRepo.RepoOwner, RepoNamespace: hookRepo.GetRepoNamespace(), Branch: hookRepo.Branch, + TargetBranch: hookRepo.Branch, PR: gpcem.Event.Change.Number, + Committer: hookRepo.Committer, Source: hookRepo.Source, } } @@ -361,6 +365,7 @@ func TriggerWorkflowV4ByGerritEvent(event *gerritTypeEvent, body []byte, uri, ba CodehostID: item.MainRepo.CodehostID, MergeRequestID: mergeRequestID, CommitID: commitID, + RawPayload: string(body), } } workflowController := controller.CreateWorkflowController(item.WorkflowArg) diff --git a/pkg/microservice/aslan/core/workflow/service/webhook/gitee.go b/pkg/microservice/aslan/core/workflow/service/webhook/gitee.go index 418c817baa..44d445a5a6 100644 --- a/pkg/microservice/aslan/core/workflow/service/webhook/gitee.go +++ b/pkg/microservice/aslan/core/workflow/service/webhook/gitee.go @@ -116,7 +116,7 @@ func ProcessGiteeHook(payload []byte, req *http.Request, requestID string, log * wg.Add(1) go func() { defer wg.Done() - if err = TriggerWorkflowV4ByGiteeEvent(event, baseURI, requestID, log); err != nil { + if err = TriggerWorkflowV4ByGiteeEvent(event, string(payload), baseURI, requestID, log); err != nil { errorList = multierror.Append(errorList, err) } }() @@ -150,7 +150,7 @@ func ProcessGiteeHook(payload []byte, req *http.Request, requestID string, log * wg.Add(1) go func() { defer wg.Done() - if err = TriggerWorkflowV4ByGiteeEvent(event, baseURI, requestID, log); err != nil { + if err = TriggerWorkflowV4ByGiteeEvent(event, string(payload), baseURI, requestID, log); err != nil { errorList = multierror.Append(errorList, err) } }() @@ -176,7 +176,7 @@ func ProcessGiteeHook(payload []byte, req *http.Request, requestID string, log * wg.Add(1) go func() { defer wg.Done() - if err = TriggerWorkflowV4ByGiteeEvent(event, baseURI, requestID, log); err != nil { + if err = TriggerWorkflowV4ByGiteeEvent(event, string(payload), baseURI, requestID, log); err != nil { errorList = multierror.Append(errorList, err) } }() diff --git a/pkg/microservice/aslan/core/workflow/service/webhook/gitee_workflowv4_task.go b/pkg/microservice/aslan/core/workflow/service/webhook/gitee_workflowv4_task.go index c5bf677f35..0aed7a1df0 100644 --- a/pkg/microservice/aslan/core/workflow/service/webhook/gitee_workflowv4_task.go +++ b/pkg/microservice/aslan/core/workflow/service/webhook/gitee_workflowv4_task.go @@ -87,6 +87,8 @@ func (gpem *giteePushEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmode RepoNamespace: hookRepo.GetRepoNamespace(), RepoOwner: hookRepo.RepoOwner, Branch: hookRepo.Branch, + TargetBranch: hookRepo.Branch, + Committer: hookRepo.Committer, Source: hookRepo.Source, } } @@ -139,7 +141,9 @@ func (gmem *giteeMergeEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmod RepoOwner: hookRepo.RepoOwner, RepoNamespace: hookRepo.GetRepoNamespace(), Branch: hookRepo.Branch, + TargetBranch: gmem.event.PullRequest.Base.Ref, PR: gmem.event.PullRequest.Number, + Committer: hookRepo.Committer, Source: hookRepo.Source, } } @@ -173,7 +177,9 @@ func (gtem *giteeTagEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmodel RepoOwner: hookRepo.RepoOwner, RepoNamespace: hookRepo.GetRepoNamespace(), Branch: hookRepo.Branch, + TargetBranch: hookRepo.Branch, Tag: hookRepo.Tag, + Committer: hookRepo.Committer, Source: hookRepo.Source, } } @@ -206,7 +212,7 @@ func createGiteeEventMatcherForWorkflowV4( return nil } -func TriggerWorkflowV4ByGiteeEvent(event interface{}, baseURI, requestID string, log *zap.SugaredLogger) error { +func TriggerWorkflowV4ByGiteeEvent(event interface{}, rawPayload, baseURI, requestID string, log *zap.SugaredLogger) error { workflows, _, err := commonrepo.NewWorkflowV4Coll().List(&commonrepo.ListWorkflowV4Option{}, 0, 0) if err != nil { errMsg := fmt.Sprintf("list workflow v4 error: %v", err) @@ -280,6 +286,7 @@ func TriggerWorkflowV4ByGiteeEvent(event interface{}, baseURI, requestID string, MergeRequestID: mergeRequestID, CommitID: commitID, EventType: eventType, + RawPayload: rawPayload, } case *gitee.PushEvent: eventType = EventTypePush @@ -297,11 +304,13 @@ func TriggerWorkflowV4ByGiteeEvent(event interface{}, baseURI, requestID string, IsPr: false, CommitID: commitID, EventType: eventType, + RawPayload: rawPayload, } case *gitee.TagPushEvent: eventType = EventTypeTag hookPayload = &commonmodels.HookPayload{ - EventType: eventType, + EventType: eventType, + RawPayload: rawPayload, } } if autoCancelOpt.Type != "" { diff --git a/pkg/microservice/aslan/core/workflow/service/webhook/github.go b/pkg/microservice/aslan/core/workflow/service/webhook/github.go index 8a9ee2b9b1..360f452d43 100644 --- a/pkg/microservice/aslan/core/workflow/service/webhook/github.go +++ b/pkg/microservice/aslan/core/workflow/service/webhook/github.go @@ -429,19 +429,19 @@ func ProcessGithubWebHookForWorkflowV4(payload []byte, req *http.Request, reques if *et.Action != "opened" && *et.Action != "synchronize" { return nil } - err = TriggerWorkflowV4ByGithubEvent(et, baseURI, deliveryID, requestID, log) + err = TriggerWorkflowV4ByGithubEvent(et, string(payload), baseURI, deliveryID, requestID, log) if err != nil { log.Errorf("prEventToPipelineTasks error: %v", err) return e.ErrGithubWebHook.AddErr(err) } case *github.PushEvent: - err = TriggerWorkflowV4ByGithubEvent(et, baseURI, deliveryID, requestID, log) + err = TriggerWorkflowV4ByGithubEvent(et, string(payload), baseURI, deliveryID, requestID, log) if err != nil { log.Infof("pushEventToPipelineTasks error: %v", err) return e.ErrGithubWebHook.AddErr(err) } case *github.CreateEvent: - err = TriggerWorkflowV4ByGithubEvent(et, baseURI, deliveryID, requestID, log) + err = TriggerWorkflowV4ByGithubEvent(et, string(payload), baseURI, deliveryID, requestID, log) if err != nil { log.Errorf("tagEventToPipelineTasks error: %s", err) return e.ErrGithubWebHook.AddErr(err) diff --git a/pkg/microservice/aslan/core/workflow/service/webhook/github_workflowv4_task.go b/pkg/microservice/aslan/core/workflow/service/webhook/github_workflowv4_task.go index 699358b3a6..b33f29439c 100644 --- a/pkg/microservice/aslan/core/workflow/service/webhook/github_workflowv4_task.go +++ b/pkg/microservice/aslan/core/workflow/service/webhook/github_workflowv4_task.go @@ -25,7 +25,6 @@ import ( "github.com/hashicorp/go-multierror" "go.uber.org/zap" - internalhandler "github.com/koderover/zadig/v2/pkg/shared/handler" "github.com/koderover/zadig/v2/pkg/microservice/aslan/config" commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" commonrepo "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/mongodb" @@ -33,6 +32,7 @@ import ( workflowservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/workflow/service/workflow" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/workflow/service/workflow/controller" "github.com/koderover/zadig/v2/pkg/setting" + internalhandler "github.com/koderover/zadig/v2/pkg/shared/handler" "github.com/koderover/zadig/v2/pkg/types" ) @@ -86,8 +86,10 @@ func (gpem *githubPushEventMatcheForWorkflowV4) GetHookRepo(hookRepo *commonmode RepoOwner: hookRepo.RepoOwner, RepoNamespace: hookRepo.GetRepoNamespace(), Branch: hookRepo.Branch, + TargetBranch: hookRepo.Branch, CommitID: *gpem.event.HeadCommit.ID, CommitMessage: *gpem.event.HeadCommit.Message, + Committer: hookRepo.Committer, Source: hookRepo.Source, } } @@ -143,9 +145,11 @@ func (gmem *githubMergeEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmo RepoOwner: hookRepo.RepoOwner, RepoNamespace: hookRepo.GetRepoNamespace(), Branch: hookRepo.Branch, + TargetBranch: *gmem.event.PullRequest.Base.Ref, PR: *gmem.event.PullRequest.Number, CommitID: *gmem.event.PullRequest.Head.SHA, CommitMessage: *gmem.event.PullRequest.Title, + Committer: hookRepo.Committer, Source: hookRepo.Source, } } @@ -182,7 +186,9 @@ func (gtem *githubTagEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmode RepoOwner: hookRepo.RepoOwner, RepoNamespace: hookRepo.GetRepoNamespace(), Branch: hookRepo.Branch, + TargetBranch: hookRepo.Branch, Tag: hookRepo.Tag, + Committer: hookRepo.Committer, Source: hookRepo.Source, } } @@ -215,7 +221,7 @@ func createGithubEventMatcherForWorkflowV4( return nil } -func TriggerWorkflowV4ByGithubEvent(event interface{}, baseURI, deliveryID, requestID string, log *zap.SugaredLogger) error { +func TriggerWorkflowV4ByGithubEvent(event interface{}, rawPayload, baseURI, deliveryID, requestID string, log *zap.SugaredLogger) error { workflows, _, err := commonrepo.NewWorkflowV4Coll().List(&commonrepo.ListWorkflowV4Option{}, 0, 0) if err != nil { errMsg := fmt.Sprintf("list workflow v4 error: %v", err) @@ -284,6 +290,7 @@ func TriggerWorkflowV4ByGithubEvent(event interface{}, baseURI, deliveryID, requ MergeRequestID: mergeRequestID, CommitID: commitID, EventType: eventType, + RawPayload: rawPayload, } case *github.PushEvent: if ev.GetRef() != "" && ev.GetHeadCommit().GetID() != "" { @@ -302,12 +309,14 @@ func TriggerWorkflowV4ByGithubEvent(event interface{}, baseURI, deliveryID, requ DeliveryID: deliveryID, CommitID: commitID, EventType: eventType, + RawPayload: rawPayload, } } case *github.CreateEvent: eventType = EventTypeTag hookPayload = &commonmodels.HookPayload{ - EventType: eventType, + EventType: eventType, + RawPayload: rawPayload, } } if autoCancelOpt.Type != "" { diff --git a/pkg/microservice/aslan/core/workflow/service/webhook/gitlab.go b/pkg/microservice/aslan/core/workflow/service/webhook/gitlab.go index e733b47c72..b3963515d7 100644 --- a/pkg/microservice/aslan/core/workflow/service/webhook/gitlab.go +++ b/pkg/microservice/aslan/core/workflow/service/webhook/gitlab.go @@ -163,7 +163,7 @@ func ProcessGitlabHook(payload []byte, req *http.Request, requestID string, log go func() { defer wg.Done() triggerWorkflowV4Start := time.Now() - if err = TriggerWorkflowV4ByGitlabEvent(pushEvent, baseURI, requestID, log); err != nil { + if err = TriggerWorkflowV4ByGitlabEvent(pushEvent, string(payload), baseURI, requestID, log); err != nil { errorList = multierror.Append(errorList, err) } log.Infof("gitlab webhook TriggerWorkflowV4ByGitlabEvent push cost %s", time.Since(triggerWorkflowV4Start)) @@ -196,7 +196,7 @@ func ProcessGitlabHook(payload []byte, req *http.Request, requestID string, log go func() { defer wg.Done() triggerWorkflowV4Start := time.Now() - if err = TriggerWorkflowV4ByGitlabEvent(mergeEvent, baseURI, requestID, log); err != nil { + if err = TriggerWorkflowV4ByGitlabEvent(mergeEvent, string(payload), baseURI, requestID, log); err != nil { errorList = multierror.Append(errorList, err) } log.Infof("gitlab webhook TriggerWorkflowV4ByGitlabEvent merge cost %s", time.Since(triggerWorkflowV4Start)) @@ -229,7 +229,7 @@ func ProcessGitlabHook(payload []byte, req *http.Request, requestID string, log go func() { defer wg.Done() triggerWorkflowV4Start := time.Now() - if err = TriggerWorkflowV4ByGitlabEvent(tagEvent, baseURI, requestID, log); err != nil { + if err = TriggerWorkflowV4ByGitlabEvent(tagEvent, string(payload), baseURI, requestID, log); err != nil { errorList = multierror.Append(errorList, err) } log.Infof("gitlab webhook TriggerWorkflowV4ByGitlabEvent tag cost %s", time.Since(triggerWorkflowV4Start)) diff --git a/pkg/microservice/aslan/core/workflow/service/webhook/gitlab_workflowv4_task.go b/pkg/microservice/aslan/core/workflow/service/webhook/gitlab_workflowv4_task.go index 1c896aa053..520d728773 100644 --- a/pkg/microservice/aslan/core/workflow/service/webhook/gitlab_workflowv4_task.go +++ b/pkg/microservice/aslan/core/workflow/service/webhook/gitlab_workflowv4_task.go @@ -108,7 +108,9 @@ func (gmem *gitlabMergeEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmo RepoOwner: hookRepo.RepoOwner, RepoNamespace: hookRepo.GetRepoNamespace(), Branch: hookRepo.Branch, + TargetBranch: gmem.event.ObjectAttributes.TargetBranch, PR: gmem.event.ObjectAttributes.IID, + Committer: hookRepo.Committer, Source: hookRepo.Source, } } @@ -231,6 +233,8 @@ func (gpem *gitlabPushEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmod RepoOwner: hookRepo.RepoOwner, RepoNamespace: hookRepo.GetRepoNamespace(), Branch: hookRepo.Branch, + TargetBranch: hookRepo.Branch, + Committer: hookRepo.Committer, Source: hookRepo.Source, } } @@ -268,12 +272,14 @@ func (gpem *gitlabTagEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmode RepoOwner: hookRepo.RepoOwner, RepoNamespace: hookRepo.GetRepoNamespace(), Branch: hookRepo.Branch, + TargetBranch: hookRepo.Branch, Tag: hookRepo.Tag, + Committer: hookRepo.Committer, Source: hookRepo.Source, } } -func TriggerWorkflowV4ByGitlabEvent(event interface{}, baseURI, requestID string, log *zap.SugaredLogger) error { +func TriggerWorkflowV4ByGitlabEvent(event interface{}, rawPayload, baseURI, requestID string, log *zap.SugaredLogger) error { // TODO: cache workflow // 1. find configured workflow workflows, _, err := commonrepo.NewWorkflowV4Coll().List(&commonrepo.ListWorkflowV4Option{}, 0, 0) @@ -370,6 +376,7 @@ func TriggerWorkflowV4ByGitlabEvent(event interface{}, baseURI, requestID string CommitID: commitID, CodehostID: eventRepo.CodehostID, EventType: eventType, + RawPayload: rawPayload, } case *gitlab.PushEvent: eventType = EventTypePush @@ -387,11 +394,13 @@ func TriggerWorkflowV4ByGitlabEvent(event interface{}, baseURI, requestID string CommitID: commitID, CodehostID: eventRepo.CodehostID, EventType: eventType, + RawPayload: rawPayload, } case *gitlab.TagEvent: eventType = EventTypeTag hookPayload = &commonmodels.HookPayload{ - EventType: eventType, + EventType: eventType, + RawPayload: rawPayload, } } if autoCancelOpt.Type != "" { diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_notification.go b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_notification.go index 4f8550be86..10b208235d 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_notification.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_notification.go @@ -86,30 +86,42 @@ func (j NotificationJobController) Update(useUserInput bool, ticket *commonmodel j.jobSpec.Source = currJobSpec.Source if currJobSpec.Source == "runtime" { + if currJobSpec.LarkHookNotificationConfig != nil && j.jobSpec.LarkHookNotificationConfig != nil { + currJobSpec.LarkHookNotificationConfig.AtUsers = j.jobSpec.LarkHookNotificationConfig.AtUsers + currJobSpec.LarkHookNotificationConfig.DynamicRecipients = j.jobSpec.LarkHookNotificationConfig.DynamicRecipients + currJobSpec.LarkHookNotificationConfig.IsAtAll = j.jobSpec.LarkHookNotificationConfig.IsAtAll + } if currJobSpec.LarkGroupNotificationConfig != nil && j.jobSpec.LarkGroupNotificationConfig != nil { currJobSpec.LarkGroupNotificationConfig.AtUsers = j.jobSpec.LarkGroupNotificationConfig.AtUsers + currJobSpec.LarkGroupNotificationConfig.DynamicRecipients = j.jobSpec.LarkGroupNotificationConfig.DynamicRecipients currJobSpec.LarkGroupNotificationConfig.IsAtAll = j.jobSpec.LarkGroupNotificationConfig.IsAtAll } if currJobSpec.LarkPersonNotificationConfig != nil && j.jobSpec.LarkPersonNotificationConfig != nil { currJobSpec.LarkPersonNotificationConfig.TargetUsers = j.jobSpec.LarkPersonNotificationConfig.TargetUsers + currJobSpec.LarkPersonNotificationConfig.DynamicRecipients = j.jobSpec.LarkPersonNotificationConfig.DynamicRecipients } if currJobSpec.WechatNotificationConfig != nil && j.jobSpec.WechatNotificationConfig != nil { currJobSpec.WechatNotificationConfig.AtUsers = j.jobSpec.WechatNotificationConfig.AtUsers + currJobSpec.WechatNotificationConfig.DynamicRecipients = j.jobSpec.WechatNotificationConfig.DynamicRecipients currJobSpec.WechatNotificationConfig.IsAtAll = j.jobSpec.WechatNotificationConfig.IsAtAll } if currJobSpec.DingDingNotificationConfig != nil && j.jobSpec.DingDingNotificationConfig != nil { currJobSpec.DingDingNotificationConfig.AtMobiles = j.jobSpec.DingDingNotificationConfig.AtMobiles + currJobSpec.DingDingNotificationConfig.DynamicRecipients = j.jobSpec.DingDingNotificationConfig.DynamicRecipients currJobSpec.DingDingNotificationConfig.IsAtAll = j.jobSpec.DingDingNotificationConfig.IsAtAll } if currJobSpec.MSTeamsNotificationConfig != nil && j.jobSpec.MSTeamsNotificationConfig != nil { currJobSpec.MSTeamsNotificationConfig.AtEmails = j.jobSpec.MSTeamsNotificationConfig.AtEmails + currJobSpec.MSTeamsNotificationConfig.DynamicRecipients = j.jobSpec.MSTeamsNotificationConfig.DynamicRecipients } if currJobSpec.MailNotificationConfig != nil && j.jobSpec.MailNotificationConfig != nil { currJobSpec.MailNotificationConfig.TargetUsers = j.jobSpec.MailNotificationConfig.TargetUsers + currJobSpec.MailNotificationConfig.DynamicRecipients = j.jobSpec.MailNotificationConfig.DynamicRecipients } } // use the latest webhook settings, except for title and content + j.jobSpec.LarkHookNotificationConfig = currJobSpec.LarkHookNotificationConfig j.jobSpec.LarkGroupNotificationConfig = currJobSpec.LarkGroupNotificationConfig j.jobSpec.LarkPersonNotificationConfig = currJobSpec.LarkPersonNotificationConfig j.jobSpec.WechatNotificationConfig = currJobSpec.WechatNotificationConfig @@ -218,6 +230,7 @@ func generateNotificationJobSpec(spec *commonmodels.NotificationJobSpec) (*commo return nil, err } + resp.LarkHookNotificationConfig = spec.LarkHookNotificationConfig resp.MailNotificationConfig = spec.MailNotificationConfig resp.WechatNotificationConfig = spec.WechatNotificationConfig resp.LarkPersonNotificationConfig = spec.LarkPersonNotificationConfig diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/utils.go b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/utils.go index 8d0060c44f..4106b19ae6 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/utils.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/utils.go @@ -458,64 +458,9 @@ func generateKeyValsFromWorkflowParam(params []*commonmodels.Param) []*commonmod return resp } -func repoNameToRepoIndex(repoName string) string { - words := map[rune]string{ - '0': "A", '1': "B", '2': "C", '3': "D", '4': "E", - '5': "F", '6': "G", '7': "H", '8': "I", '9': "J", - } - result := "" - for i, digit := range repoName { - if word, ok := words[digit]; ok { - result += word - } else { - result += repoName[i:] - break - } - } - - result = strings.Replace(result, "-", "_", -1) - result = strings.Replace(result, ".", "_", -1) - - return result -} - func getReposVariables(repos []*types.Repository) []*commonmodels.KeyVal { - ret := make([]*commonmodels.KeyVal, 0) - for index, repo := range repos { - repoNameIndex := fmt.Sprintf("REPONAME_%d", index) - ret = append(ret, &commonmodels.KeyVal{Key: repoNameIndex, Value: repo.RepoName, IsCredential: false}) - - repoIndex := fmt.Sprintf("REPO_%d", index) - repoName := repoNameToRepoIndex(repo.RepoName) - ret = append(ret, &commonmodels.KeyVal{Key: repoIndex, Value: repoName, IsCredential: false}) - - if len(repo.Branch) > 0 { - ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_BRANCH", repoName), Value: repo.Branch, IsCredential: false}) - } - - if len(repo.Tag) > 0 { - ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_TAG", repoName), Value: repo.Tag, IsCredential: false}) - } - - if repo.PR > 0 { - ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_PR", repoName), Value: strconv.Itoa(repo.PR), IsCredential: false}) - } - - ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_PRE_MERGE_BRANCHES", repoName), Value: repo.GetPreMergeBranches(), IsCredential: false}) - - ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_ORG", repoName), Value: repo.RepoOwner, IsCredential: false}) - - if len(repo.PRs) > 0 { - prStrs := []string{} - for _, pr := range repo.PRs { - prStrs = append(prStrs, strconv.Itoa(pr)) - } - ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_PR", repoName), Value: strings.Join(prStrs, ","), IsCredential: false}) - } - - if len(repo.CommitID) > 0 { - ret = append(ret, &commonmodels.KeyVal{Key: fmt.Sprintf("%s_COMMIT_ID", repoName), Value: repo.CommitID, IsCredential: false}) - } + ret := commonutil.RepoVariableKVs(repos) + for _, repo := range repos { ret = append(ret, getEnvFromCommitMsg(repo.CommitMessage)...) } return ret 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..c450fbf5e5 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/workflow.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/workflow.go @@ -19,12 +19,10 @@ package controller import ( "encoding/json" "fmt" - "net/url" "regexp" "strings" "time" - configbase "github.com/koderover/zadig/v2/pkg/config" "github.com/koderover/zadig/v2/pkg/microservice/aslan/config" commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" commonrepo "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/mongodb" @@ -208,19 +206,16 @@ func (w *Workflow) ToJobTasks(taskID int64, creator, account, uid string) ([]*co } } + pairs := make([]string, 0, len(globalKeyMap)*2) + for k, v := range globalKeyMap { + escaped, _ := json.Marshal(v) + pairs = append(pairs, "{{."+k+"}}", strings.Trim(string(escaped), `"`)) + } + replacer := strings.NewReplacer(pairs...) + for _, task := range tasks { taskBytes, _ := json.Marshal(task) - taskString := string(taskBytes) - for k, v := range globalKeyMap { - // Use json.Marshal to properly escape the value as it would appear in JSON - escapedValueBytes, _ := json.Marshal(v) - escapedValue := string(escapedValueBytes) - // Remove the surrounding quotes since we're replacing within a JSON string - escapedValue = strings.Trim(escapedValue, `"`) - - taskString = strings.ReplaceAll(taskString, fmt.Sprintf("{{.%s}}", k), escapedValue) - log.Debugf("replacing key %s with value: %s", fmt.Sprintf("{{.%s}}", k), v) - } + taskString := replacer.Replace(string(taskBytes)) err := json.Unmarshal([]byte(taskString), &task) if err != nil { @@ -355,40 +350,18 @@ func (w *Workflow) RenderWorkflowDefaultParams(taskID int64, creator, account, u } func (w *Workflow) getWorkflowDefaultParams(taskID int64, creator, account, uid string) ([]*commonmodels.Param, error) { - resp := []*commonmodels.Param{} projectInfo, err := templaterepo.NewProductColl().Find(w.Project) if err != nil { return nil, fmt.Errorf("failed to find project info for project %s, error: %s", w.Project, err) } - resp = append(resp, &commonmodels.Param{Name: "project", Value: w.Project, ParamsType: "string", IsCredential: false}) - resp = append(resp, &commonmodels.Param{Name: "project.id", Value: w.Project, ParamsType: "string", IsCredential: false}) - resp = append(resp, &commonmodels.Param{Name: "project.name", Value: projectInfo.ProjectName, ParamsType: "string", IsCredential: false}) - resp = append(resp, &commonmodels.Param{Name: "workflow.id", Value: w.Name, ParamsType: "string", IsCredential: false}) - resp = append(resp, &commonmodels.Param{Name: "workflow.name", Value: w.DisplayName, ParamsType: "string", IsCredential: false}) - resp = append(resp, &commonmodels.Param{Name: "workflow.task.id", Value: fmt.Sprintf("%d", taskID), ParamsType: "string", IsCredential: false}) - resp = append(resp, &commonmodels.Param{Name: "workflow.task.creator", Value: creator, ParamsType: "string", IsCredential: false}) - resp = append(resp, &commonmodels.Param{Name: "workflow.task.creator.id", Value: account, ParamsType: "string", IsCredential: false}) - resp = append(resp, &commonmodels.Param{Name: "workflow.task.creator.userId", Value: uid, ParamsType: "string", IsCredential: false}) - resp = append(resp, &commonmodels.Param{Name: "workflow.task.timestamp", Value: fmt.Sprintf("%d", time.Now().Unix()), ParamsType: "string", IsCredential: false}) - resp = append(resp, &commonmodels.Param{Name: "workflow.task.datetime", Value: time.Now().Format(time.DateTime), ParamsType: "string", IsCredential: false}) - detailURL := fmt.Sprintf("%s/v1/projects/detail/%s/pipelines/custom/%s/%d?display_name=%s", - configbase.SystemAddress(), - w.Project, - w.Name, - taskID, - url.QueryEscape(w.DisplayName), - ) - resp = append(resp, &commonmodels.Param{Name: "workflow.task.url", Value: detailURL, ParamsType: "string", IsCredential: false}) - - for _, param := range w.Params { - paramsKey := strings.Join([]string{"workflow", "params", param.Name}, ".") - newParam := &commonmodels.Param{Name: paramsKey, Value: param.Value, ParamsType: "string", IsCredential: false} - if param.ParamsType == string(commonmodels.MultiSelectType) { - newParam.Value = strings.Join(param.ChoiceValue, ",") - } else if param.ParamsType == string(commonmodels.FileType) { - continue - } - resp = append(resp, newParam) + resp := make([]*commonmodels.Param, 0) + for _, kv := range commonutil.BuildWorkflowSystemVariableKVs(w.WorkflowV4, w.Project, projectInfo.ProjectName, taskID, creator, account, uid, time.Now()) { + resp = append(resp, &commonmodels.Param{ + Name: kv.Key, + Value: kv.Value, + ParamsType: "string", + IsCredential: kv.IsCredential, + }) } return resp, nil } diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/types.go b/pkg/microservice/aslan/core/workflow/service/workflow/types.go index ddc0b45bea..dfb8206e5a 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/types.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/types.go @@ -158,6 +158,11 @@ type CreateCustomTaskNotifyInput struct { MailNotificationConfig *CreateCustomTaskMailNotificationConfig `json:"mail_notification_config"` } +type CreateCustomTaskDynamicRecipient struct { + Value string `json:"value"` + IdentityType string `json:"identity_type"` +} + type CreateCustomTaskLarkUserInfo struct { ID string `json:"id"` // 支持 open_id、user_id @@ -169,36 +174,43 @@ type CreateCustomTaskLarkUserInfo struct { } type CreateCustomTaskLarkGroupNotificationConfig struct { - ChatID string `json:"chat_id"` - AtUsers []CreateCustomTaskLarkUserInfo `json:"at_users"` + ChatID string `json:"chat_id"` + AtUsers []CreateCustomTaskLarkUserInfo `json:"at_users"` + DynamicRecipients []CreateCustomTaskDynamicRecipient `json:"dynamic_recipients"` } type CreateCustomTaskLarkPersonNotificationConfig struct { - Users []CreateCustomTaskLarkUserInfo `json:"users"` + Users []CreateCustomTaskLarkUserInfo `json:"users"` + DynamicRecipients []CreateCustomTaskDynamicRecipient `json:"dynamic_recipients"` } type CreateCustomTaskLarkHookNotificationConfig struct { - AtUsers []string `json:"at_users"` - IsAtAll bool `json:"is_at_all"` + AtUsers []string `json:"at_users"` + DynamicRecipients []CreateCustomTaskDynamicRecipient `json:"dynamic_recipients"` + IsAtAll bool `json:"is_at_all"` } type CreateCustomTaskWechatNotificationConfig struct { - AtUsers []string `json:"at_users"` - IsAtAll bool `json:"is_at_all"` + AtUsers []string `json:"at_users"` + DynamicRecipients []CreateCustomTaskDynamicRecipient `json:"dynamic_recipients"` + IsAtAll bool `json:"is_at_all"` } type CreateCustomTaskDingDingNotificationConfig struct { - AtMobiles []string `json:"at_mobiles"` - IsAtAll bool `json:"is_at_all"` + AtMobiles []string `json:"at_mobiles"` + DynamicRecipients []CreateCustomTaskDynamicRecipient `json:"dynamic_recipients"` + IsAtAll bool `json:"is_at_all"` } type CreateCustomTaskMSTeamsNotificationConfig struct { - AtEmails []string `json:"at_emails"` + AtEmails []string `json:"at_emails"` + DynamicRecipients []CreateCustomTaskDynamicRecipient `json:"dynamic_recipients"` } type CreateCustomTaskMailNotificationConfig struct { - UserIDs []string `json:"user_ids"` - Users []*commonmodels.User `json:"users"` + UserIDs []string `json:"user_ids"` + Users []*commonmodels.User `json:"users"` + DynamicRecipients []CreateCustomTaskDynamicRecipient `json:"dynamic_recipients"` } type CreateCustomTaskParam struct { 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 05ae268365..94f5c925d8 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 @@ -664,6 +664,7 @@ func CreateWorkflowTaskV4(args *CreateWorkflowTaskV4Args, workflow *commonmodels log.Errorf("fill serviceModules to jobs error: %v", err) return resp, e.ErrCreateTask.AddDesc(err.Error()) } + workflowTask.GlobalContext = buildWorkflowTaskRuntimeContext(workflowTask) if err := instantmessage.NewWeChatClient().SendWorkflowTaskNotifications(workflowTask); err != nil { log.Errorf("send workflow task notification failed, error: %v", err) @@ -690,6 +691,20 @@ func updateNotifyCtls(notifyCtls []*commonmodels.NotifyCtl, notifyInputs []*Crea notifyInputsMap[notifyInput.ID] = notifyInput } + toDynamicRecipients := func(inputs []CreateCustomTaskDynamicRecipient) []*commonmodels.DynamicRecipient { + resp := make([]*commonmodels.DynamicRecipient, 0, len(inputs)) + for _, input := range inputs { + if input.Value == "" { + continue + } + resp = append(resp, &commonmodels.DynamicRecipient{ + Value: input.Value, + IdentityType: input.IdentityType, + }) + } + return resp + } + for i, notifyCtl := range notifyCtls { notifyInput, ok := notifyInputsMap[i] if ok && notifyCtl.WebHookType == notifyInput.Type { @@ -705,9 +720,10 @@ func updateNotifyCtls(notifyCtls []*commonmodels.NotifyCtl, notifyInputs []*Crea } config := &commonmodels.LarkHookNotificationConfig{ - HookAddress: notifyCtl.LarkHookNotificationConfig.HookAddress, - AtUsers: notifyInput.LarkHookNotificationConfig.AtUsers, - IsAtAll: notifyInput.LarkHookNotificationConfig.IsAtAll, + HookAddress: notifyCtl.LarkHookNotificationConfig.HookAddress, + AtUsers: notifyInput.LarkHookNotificationConfig.AtUsers, + DynamicRecipients: toDynamicRecipients(notifyInput.LarkHookNotificationConfig.DynamicRecipients), + IsAtAll: notifyInput.LarkHookNotificationConfig.IsAtAll, } notifyCtl.LarkHookNotificationConfig = config @@ -718,7 +734,8 @@ func updateNotifyCtls(notifyCtls []*commonmodels.NotifyCtl, notifyInputs []*Crea } config := &commonmodels.LarkPersonNotificationConfig{ - AppID: notifyCtl.LarkPersonNotificationConfig.AppID, + AppID: notifyCtl.LarkPersonNotificationConfig.AppID, + DynamicRecipients: toDynamicRecipients(notifyInput.LarkPersonNotificationConfig.DynamicRecipients), } targetUsers := make([]*larktool.UserInfo, 0) @@ -740,7 +757,8 @@ func updateNotifyCtls(notifyCtls []*commonmodels.NotifyCtl, notifyInputs []*Crea } config := &commonmodels.LarkGroupNotificationConfig{ - AppID: notifyCtl.LarkGroupNotificationConfig.AppID, + AppID: notifyCtl.LarkGroupNotificationConfig.AppID, + DynamicRecipients: toDynamicRecipients(notifyInput.LarkGroupNotificationConfig.DynamicRecipients), Chat: &commonmodels.LarkChat{ ChatID: notifyInput.LarkGroupNotificationConfig.ChatID, }, @@ -763,9 +781,10 @@ func updateNotifyCtls(notifyCtls []*commonmodels.NotifyCtl, notifyInputs []*Crea } config := &commonmodels.WechatNotificationConfig{ - HookAddress: notifyCtl.WechatNotificationConfig.HookAddress, - AtUsers: notifyInput.WechatNotificationConfig.AtUsers, - IsAtAll: notifyInput.WechatNotificationConfig.IsAtAll, + HookAddress: notifyCtl.WechatNotificationConfig.HookAddress, + AtUsers: notifyInput.WechatNotificationConfig.AtUsers, + DynamicRecipients: toDynamicRecipients(notifyInput.WechatNotificationConfig.DynamicRecipients), + IsAtAll: notifyInput.WechatNotificationConfig.IsAtAll, } notifyCtl.WechatNotificationConfig = config @@ -776,9 +795,10 @@ func updateNotifyCtls(notifyCtls []*commonmodels.NotifyCtl, notifyInputs []*Crea } config := &commonmodels.DingDingNotificationConfig{ - HookAddress: notifyCtl.DingDingNotificationConfig.HookAddress, - AtMobiles: notifyInput.DingDingNotificationConfig.AtMobiles, - IsAtAll: notifyInput.DingDingNotificationConfig.IsAtAll, + HookAddress: notifyCtl.DingDingNotificationConfig.HookAddress, + AtMobiles: notifyInput.DingDingNotificationConfig.AtMobiles, + DynamicRecipients: toDynamicRecipients(notifyInput.DingDingNotificationConfig.DynamicRecipients), + IsAtAll: notifyInput.DingDingNotificationConfig.IsAtAll, } notifyCtl.DingDingNotificationConfig = config @@ -789,8 +809,9 @@ func updateNotifyCtls(notifyCtls []*commonmodels.NotifyCtl, notifyInputs []*Crea } config := &commonmodels.MSTeamsNotificationConfig{ - HookAddress: notifyCtl.MSTeamsNotificationConfig.HookAddress, - AtEmails: notifyInput.MSTeamsNotificationConfig.AtEmails, + HookAddress: notifyCtl.MSTeamsNotificationConfig.HookAddress, + AtEmails: notifyInput.MSTeamsNotificationConfig.AtEmails, + DynamicRecipients: toDynamicRecipients(notifyInput.MSTeamsNotificationConfig.DynamicRecipients), } notifyCtl.MSTeamsNotificationConfig = config @@ -801,7 +822,8 @@ func updateNotifyCtls(notifyCtls []*commonmodels.NotifyCtl, notifyInputs []*Crea } config := &commonmodels.MailNotificationConfig{ - TargetUsers: make([]*commonmodels.User, 0), + TargetUsers: make([]*commonmodels.User, 0), + DynamicRecipients: toDynamicRecipients(notifyInput.MailNotificationConfig.DynamicRecipients), } if len(notifyInput.MailNotificationConfig.Users) > 0 { @@ -835,6 +857,34 @@ func updateNotifyCtls(notifyCtls []*commonmodels.NotifyCtl, notifyInputs []*Crea return notifyCtls } +func buildWorkflowTaskRuntimeContext(task *commonmodels.WorkflowTask) map[string]string { + if task == nil || task.WorkflowArgs == nil { + return nil + } + + keyMap := commonutil.KeyValsToMap(commonutil.BuildWorkflowRuntimeVariableKVs( + task.WorkflowArgs, + task.ProjectName, + task.ProjectDisplayName, + task.TaskID, + task.TaskCreator, + task.TaskCreatorAccount, + task.TaskCreatorID, + time.Unix(task.StartTime, 0), + )) + + resp := make(map[string]string, len(keyMap)) + for key, value := range keyMap { + // Payload variables are resolved at task creation time and stored in RawPayload; + // they don't need to be persisted in GlobalContext (which would duplicate them in MongoDB). + if strings.HasPrefix(key, "payload.") { + continue + } + resp[runtimeWorkflowController.GetContextKey(fmt.Sprintf("{{.%s}}", key))] = value + } + return resp +} + func GetManualExecWorkflowTaskV4Info(workflowName string, taskID int64, logger *zap.SugaredLogger) (*commonmodels.WorkflowV4, error) { originWorkflow, err := commonrepo.NewWorkflowV4Coll().Find(workflowName) if err != nil { @@ -912,7 +962,16 @@ func RetryWorkflowTaskV4(workflowName string, taskID int64, logger *zap.SugaredL task.RetryNum++ - globalKeyMap := make(map[string]string) + globalKeyMap := commonutil.KeyValsToMap(commonutil.BuildWorkflowRuntimeVariableKVs( + task.WorkflowArgs, + task.ProjectName, + task.ProjectDisplayName, + task.TaskID, + task.TaskCreator, + task.TaskCreatorAccount, + task.TaskCreatorID, + time.Unix(task.StartTime, 0), + )) jobTaskMap := make(map[string]*commonmodels.JobTask) for _, stage := range task.WorkflowArgs.Stages { for _, job := range stage.Jobs { @@ -964,6 +1023,7 @@ func RetryWorkflowTaskV4(workflowName string, taskID int64, logger *zap.SugaredL globalKeyMap[key] = item.Value } } + task.GlobalContext = buildWorkflowTaskRuntimeContext(task) for _, stage := range task.Stages { if stage.Status == config.StatusPassed || stage.Status == config.StatusSkipped { @@ -1040,7 +1100,16 @@ func ManualExecWorkflowTaskV4(workflowName string, taskID int64, stageName strin return e.ErrCreateTask.AddErr(fmt.Errorf("save original jobs error: %v", err)) } - globalKeyMap := make(map[string]string) + globalKeyMap := commonutil.KeyValsToMap(commonutil.BuildWorkflowRuntimeVariableKVs( + task.WorkflowArgs, + task.ProjectName, + task.ProjectDisplayName, + task.TaskID, + task.TaskCreator, + task.TaskCreatorAccount, + task.TaskCreatorID, + time.Unix(task.StartTime, 0), + )) for _, stage := range task.WorkflowArgs.Stages { if stage.Name == stageName { @@ -1113,6 +1182,7 @@ func ManualExecWorkflowTaskV4(workflowName string, taskID int64, stageName strin globalKeyMap[key] = item.Value } } + task.GlobalContext = buildWorkflowTaskRuntimeContext(task) for _, stage := range task.OriginWorkflowArgs.Stages { if stage.Name == stageName { diff --git a/pkg/types/repo.go b/pkg/types/repo.go index 794451353c..8d27a32efe 100644 --- a/pkg/types/repo.go +++ b/pkg/types/repo.go @@ -49,10 +49,12 @@ type Repository struct { IsPrimary bool `bson:"is_primary" json:"is_primary" yaml:"is_primary"` CodehostID int `bson:"codehost_id" json:"codehost_id" yaml:"codehost_id"` // add - OauthToken string `bson:"oauth_token" json:"oauth_token" yaml:"oauth_token"` - Address string `bson:"address" json:"address" yaml:"address"` - AuthorName string `bson:"author_name,omitempty" json:"author_name,omitempty" yaml:"author_name,omitempty"` - CheckoutRef string `bson:"checkout_ref,omitempty" json:"checkout_ref,omitempty" yaml:"checkout_ref,omitempty"` + OauthToken string `bson:"oauth_token" json:"oauth_token" yaml:"oauth_token"` + Address string `bson:"address" json:"address" yaml:"address"` + AuthorName string `bson:"author_name,omitempty" json:"author_name,omitempty" yaml:"author_name,omitempty"` + Committer string `bson:"committer,omitempty" json:"committer,omitempty" yaml:"committer,omitempty"` + TargetBranch string `bson:"target_branch,omitempty" json:"target_branch,omitempty" yaml:"target_branch,omitempty"` + CheckoutRef string `bson:"checkout_ref,omitempty" json:"checkout_ref,omitempty" yaml:"checkout_ref,omitempty"` // username/password authorization for git/perforce Username string `bson:"username,omitempty" json:"username,omitempty" yaml:"username,omitempty"` Password string `bson:"password,omitempty" json:"password,omitempty" yaml:"password,omitempty"` From 5dcb0dfce314c18badebef3632340328c0174d00 Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Mon, 11 May 2026 09:55:52 +0800 Subject: [PATCH 2/6] fix(workflow): preserve runtime context job outputs --- .../workflow/service/workflow/workflow_task_v4.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) 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 94f5c925d8..aee15093f7 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 @@ -858,10 +858,19 @@ func updateNotifyCtls(notifyCtls []*commonmodels.NotifyCtl, notifyInputs []*Crea } func buildWorkflowTaskRuntimeContext(task *commonmodels.WorkflowTask) map[string]string { - if task == nil || task.WorkflowArgs == nil { + if task == nil { return nil } + resp := make(map[string]string) + for key, value := range task.GlobalContext { + resp[key] = value + } + + if task.WorkflowArgs == nil { + return resp + } + keyMap := commonutil.KeyValsToMap(commonutil.BuildWorkflowRuntimeVariableKVs( task.WorkflowArgs, task.ProjectName, @@ -873,7 +882,6 @@ func buildWorkflowTaskRuntimeContext(task *commonmodels.WorkflowTask) map[string time.Unix(task.StartTime, 0), )) - resp := make(map[string]string, len(keyMap)) for key, value := range keyMap { // Payload variables are resolved at task creation time and stored in RawPayload; // they don't need to be persisted in GlobalContext (which would duplicate them in MongoDB). From c95ebdc446a9ccf678533ea660ef1742c5ca5e28 Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Mon, 11 May 2026 15:21:51 +0800 Subject: [PATCH 3/6] fix(workflow): preserve webhook repo context fields --- .../core/workflow/service/workflow/controller/job/utils.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/utils.go b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/utils.go index 4106b19ae6..414a5dc0dc 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/utils.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/utils.go @@ -196,6 +196,10 @@ func applyRepos(base, input []*types.Repository) []*types.Repository { item.FilterRegexp = cv.FilterRegexp item.CommitID = cv.CommitID item.CommitMessage = cv.CommitMessage + item.AuthorName = cv.AuthorName + item.Committer = cv.Committer + item.TargetBranch = cv.TargetBranch + item.CheckoutRef = cv.CheckoutRef item.SSHKey = cv.SSHKey item.PrivateAccessToken = cv.PrivateAccessToken From a6f1390be4e7b0cc65636fa619a37957e355d196 Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Mon, 11 May 2026 17:47:20 +0800 Subject: [PATCH 4/6] refactor(notification): reuse key map and lark at builder --- .../jobcontroller/job_notification.go | 49 ++++++------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_notification.go b/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_notification.go index 5c42464f8a..f24e7bd455 100644 --- a/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_notification.go +++ b/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_notification.go @@ -246,19 +246,7 @@ func (c *NotificationJobCtl) prepareRuntimeNotificationFields() error { } func (c *NotificationJobCtl) buildRuntimeNotificationKeyMap() map[string]string { - keyMap := make(map[string]string) - - insertKVs := func(kvs []*commonmodels.KeyVal) { - for _, kv := range kvs { - if kv == nil || kv.Key == "" || kv.GetValue() == "" { - continue - } - keyMap[kv.Key] = kv.GetValue() - } - } - - insertKVs(c.workflowCtx.WorkflowKeyVals) - return keyMap + return util.KeyValsToMap(c.workflowCtx.WorkflowKeyVals) } func renderNotificationStrings(inputs []string, keyMap map[string]string) []string { @@ -537,6 +525,19 @@ func uniqMailUsers(users []*commonmodels.User) []*commonmodels.User { return resp } +func buildLarkAtMessage(idList []string, isAtAll bool) string { + idList = lo.Filter(idList, func(s string, _ int) bool { return s != "All" }) + atUserList := make([]string, 0, len(idList)) + for _, userID := range idList { + atUserList = append(atUserList, fmt.Sprintf("", userID)) + } + atMessage := strings.Join(atUserList, " ") + if isAtAll { + atMessage += "" + } + return atMessage +} + func renderNotificationString(input string, keyMap map[string]string) string { if len(keyMap) == 0 || !strings.Contains(input, "{{.") { return input @@ -581,18 +582,8 @@ func sendLarkMessage(client *lark.Client, productName, workflowName, workflowDis // then send @ message if len(idList) > 0 || isAtAll { - atUserList := []string{} - idList = lo.Filter(idList, func(s string, _ int) bool { return s != "All" }) - for _, userID := range idList { - atUserList = append(atUserList, fmt.Sprintf("", userID)) - } - atMessage := strings.Join(atUserList, " ") - if isAtAll { - atMessage += "" - } - larkAtMessage := &instantmessage.FeiShuMessage{ - Text: atMessage, + Text: buildLarkAtMessage(idList, isAtAll), } atMessageContent, err := json.Marshal(larkAtMessage) @@ -636,15 +627,7 @@ func sendLarkHookMessage(productName, workflowName, workflowDisplayName string, return nil } - atUserList := make([]string, 0, len(idList)) - idList = lo.Filter(idList, func(s string, _ int) bool { return s != "All" }) - for _, userID := range idList { - atUserList = append(atUserList, fmt.Sprintf("", userID)) - } - atMessage := strings.Join(atUserList, " ") - if isAtAll { - atMessage += "" - } + atMessage := buildLarkAtMessage(idList, isAtAll) if strings.Contains(uri, "bot/v2/hook") { _, err := httpclient.New().Post(uri, httpclient.SetBody(&instantmessage.FeiShuMessageV2{ From 86d4e68dad2fbbdc6d6873952855797a20eca5cc Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Mon, 11 May 2026 18:25:57 +0800 Subject: [PATCH 5/6] refactor(notification): reuse feishu sender and escape urls --- .../common/service/instantmessage/lark.go | 8 ++++++ .../jobcontroller/job_notification.go | 26 ++++--------------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/pkg/microservice/aslan/core/common/service/instantmessage/lark.go b/pkg/microservice/aslan/core/common/service/instantmessage/lark.go index cf90e9592f..81c0ccf3c4 100644 --- a/pkg/microservice/aslan/core/common/service/instantmessage/lark.go +++ b/pkg/microservice/aslan/core/common/service/instantmessage/lark.go @@ -197,6 +197,10 @@ func (w *Service) sendFeishuMessage(uri string, lcMsg *LarkCard) error { return err } +func (w *Service) SendFeishuHookCard(uri string, lcMsg *LarkCard) error { + return w.sendFeishuMessage(uri, lcMsg) +} + func (w *Service) sendFeishuMessageFromClient(client *lark.Client, receiverType, receiverID, messageType, messageBody string) error { err := client.SendMessage(receiverType, messageType, receiverID, messageBody) @@ -228,6 +232,10 @@ func (w *Service) sendFeishuMessageOfSingleType(title, uri, content string) erro return err } +func (w *Service) SendFeishuHookText(uri, content string) error { + return w.sendFeishuMessageOfSingleType("", uri, content) +} + func getColorTemplateWithStatus(status config.Status) string { if status == config.StatusPassed || status == config.StatusCreated { return feishuHeaderTemplateGreen diff --git a/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_notification.go b/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_notification.go index f24e7bd455..144db954ec 100644 --- a/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_notification.go +++ b/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_notification.go @@ -565,7 +565,7 @@ func sendLarkMessage(client *lark.Client, productName, workflowName, workflowDis productName, workflowName, taskID, - workflowDisplayName, + url.QueryEscape(workflowDisplayName), ) card.AddI18NElementsZhcnAction("点击查看更多信息", url) @@ -611,15 +611,12 @@ func sendLarkHookMessage(productName, workflowName, workflowDisplayName string, productName, workflowName, taskID, - workflowDisplayName, + url.QueryEscape(workflowDisplayName), ) card.AddI18NElementsZhcnAction("点击查看更多信息", detailURL) - messageReq := instantmessage.LarkCardReq{ - MsgType: "interactive", - Card: card, - } - if _, err := httpclient.New().Post(uri, httpclient.SetBody(messageReq)); err != nil { + imService := instantmessage.NewWeChatClient() + if err := imService.SendFeishuHookCard(uri, card); err != nil { return err } @@ -628,20 +625,7 @@ func sendLarkHookMessage(productName, workflowName, workflowDisplayName string, } atMessage := buildLarkAtMessage(idList, isAtAll) - - if strings.Contains(uri, "bot/v2/hook") { - _, err := httpclient.New().Post(uri, httpclient.SetBody(&instantmessage.FeiShuMessageV2{ - MsgType: "text", - Content: instantmessage.FeiShuContentV2{Text: atMessage}, - })) - return err - } - - _, err := httpclient.New().Post(uri, httpclient.SetBody(&instantmessage.FeiShuMessage{ - Title: "", - Text: atMessage, - })) - return err + return imService.SendFeishuHookText(uri, atMessage) } func sendDingDingMessage(productName, workflowName, workflowDisplayName string, taskID int64, uri, title, message string, idList []string, isAtAll bool) error { From 98026ad4aa47c78f45a49cff1365cde737523bea Mon Sep 17 00:00:00 2001 From: huanghongbo-hhb Date: Tue, 12 May 2026 11:40:30 +0800 Subject: [PATCH 6/6] fix(workflow): preserve webhook repo fields across scm providers --- .../workflow/service/webhook/gerrit_workflowv4_task.go | 4 ++++ .../workflow/service/webhook/gitee_workflowv4_task.go | 8 ++++++++ .../workflow/service/webhook/gitlab_workflowv4_task.go | 8 ++++++++ .../workflow/service/workflow/controller/job/job_build.go | 4 ++++ 4 files changed, 24 insertions(+) diff --git a/pkg/microservice/aslan/core/workflow/service/webhook/gerrit_workflowv4_task.go b/pkg/microservice/aslan/core/workflow/service/webhook/gerrit_workflowv4_task.go index a649f964bb..bc6eaedab9 100644 --- a/pkg/microservice/aslan/core/workflow/service/webhook/gerrit_workflowv4_task.go +++ b/pkg/microservice/aslan/core/workflow/service/webhook/gerrit_workflowv4_task.go @@ -174,6 +174,8 @@ func (gruem *gerritChangeMergedEventMatcherForWorkflowV4) GetHookRepo(hookRepo * RepoNamespace: hookRepo.GetRepoNamespace(), Branch: hookRepo.Branch, TargetBranch: hookRepo.Branch, + CommitID: gruem.Event.NewRev, + CommitMessage: gruem.Event.Change.CommitMessage, Committer: hookRepo.Committer, Source: hookRepo.Source, } @@ -227,6 +229,8 @@ func (gpcem *gerritPatchsetCreatedEventMatcherForWorkflowV4) GetHookRepo(hookRep Branch: hookRepo.Branch, TargetBranch: hookRepo.Branch, PR: gpcem.Event.Change.Number, + CommitID: gpcem.Event.PatchSet.Revision, + CommitMessage: gpcem.Event.Change.CommitMessage, Committer: hookRepo.Committer, Source: hookRepo.Source, } diff --git a/pkg/microservice/aslan/core/workflow/service/webhook/gitee_workflowv4_task.go b/pkg/microservice/aslan/core/workflow/service/webhook/gitee_workflowv4_task.go index 0aed7a1df0..5bac23d81f 100644 --- a/pkg/microservice/aslan/core/workflow/service/webhook/gitee_workflowv4_task.go +++ b/pkg/microservice/aslan/core/workflow/service/webhook/gitee_workflowv4_task.go @@ -81,6 +81,10 @@ func (gpem *giteePushEventMatcherForWorkflowV4) Match(hookRepo *commonmodels.Mai } func (gpem *giteePushEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmodels.MainHookRepo) *types.Repository { + commitMessage := "" + if len(gpem.event.Commits) > 0 { + commitMessage = gpem.event.Commits[len(gpem.event.Commits)-1].Message + } return &types.Repository{ CodehostID: hookRepo.CodehostID, RepoName: hookRepo.RepoName, @@ -88,6 +92,8 @@ func (gpem *giteePushEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmode RepoOwner: hookRepo.RepoOwner, Branch: hookRepo.Branch, TargetBranch: hookRepo.Branch, + CommitID: gpem.event.After, + CommitMessage: commitMessage, Committer: hookRepo.Committer, Source: hookRepo.Source, } @@ -143,6 +149,8 @@ func (gmem *giteeMergeEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmod Branch: hookRepo.Branch, TargetBranch: gmem.event.PullRequest.Base.Ref, PR: gmem.event.PullRequest.Number, + CommitID: gmem.event.PullRequest.Head.Sha, + CommitMessage: gmem.event.PullRequest.Title, Committer: hookRepo.Committer, Source: hookRepo.Source, } diff --git a/pkg/microservice/aslan/core/workflow/service/webhook/gitlab_workflowv4_task.go b/pkg/microservice/aslan/core/workflow/service/webhook/gitlab_workflowv4_task.go index 520d728773..be3779715f 100644 --- a/pkg/microservice/aslan/core/workflow/service/webhook/gitlab_workflowv4_task.go +++ b/pkg/microservice/aslan/core/workflow/service/webhook/gitlab_workflowv4_task.go @@ -110,6 +110,8 @@ func (gmem *gitlabMergeEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmo Branch: hookRepo.Branch, TargetBranch: gmem.event.ObjectAttributes.TargetBranch, PR: gmem.event.ObjectAttributes.IID, + CommitID: gmem.event.ObjectAttributes.LastCommit.ID, + CommitMessage: gmem.event.ObjectAttributes.Title, Committer: hookRepo.Committer, Source: hookRepo.Source, } @@ -227,6 +229,10 @@ func (gpem *gitlabPushEventMatcherForWorkflowV4) Match(hookRepo *commonmodels.Ma } func (gpem *gitlabPushEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmodels.MainHookRepo) *types.Repository { + commitMessage := "" + if len(gpem.event.Commits) > 0 { + commitMessage = gpem.event.Commits[len(gpem.event.Commits)-1].Message + } return &types.Repository{ CodehostID: hookRepo.CodehostID, RepoName: hookRepo.RepoName, @@ -234,6 +240,8 @@ func (gpem *gitlabPushEventMatcherForWorkflowV4) GetHookRepo(hookRepo *commonmod RepoNamespace: hookRepo.GetRepoNamespace(), Branch: hookRepo.Branch, TargetBranch: hookRepo.Branch, + CommitID: gpem.event.After, + CommitMessage: commitMessage, Committer: hookRepo.Committer, Source: hookRepo.Source, } 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 6813040a7e..1bbd194de3 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 @@ -1122,6 +1122,10 @@ func mergeRepos(targetRepos, sourceRepos []*types.Repository) []*types.Repositor targetRepo.PRs = sourceRepo.PRs targetRepo.CommitID = sourceRepo.CommitID targetRepo.CommitMessage = sourceRepo.CommitMessage + targetRepo.AuthorName = sourceRepo.AuthorName + targetRepo.Committer = sourceRepo.Committer + targetRepo.TargetBranch = sourceRepo.TargetBranch + targetRepo.CheckoutRef = sourceRepo.CheckoutRef } else { // Add new repo from source repos targetRepos = append(targetRepos, sourceRepo)