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/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 dd2053f0e7..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
@@ -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,331 @@ 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 {
+ return util.KeyValsToMap(c.workflowCtx.WorkflowKeyVals)
+}
+
+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 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
+ }
+ 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()
@@ -223,7 +565,7 @@ func sendLarkMessage(client *lark.Client, productName, workflowName, workflowDis
productName,
workflowName,
taskID,
- workflowDisplayName,
+ url.QueryEscape(workflowDisplayName),
)
card.AddI18NElementsZhcnAction("点击查看更多信息", url)
@@ -240,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)
@@ -268,6 +600,34 @@ 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,
+ url.QueryEscape(workflowDisplayName),
+ )
+ card.AddI18NElementsZhcnAction("点击查看更多信息", detailURL)
+
+ imService := instantmessage.NewWeChatClient()
+ if err := imService.SendFeishuHookCard(uri, card); err != nil {
+ return err
+ }
+
+ if len(idList) == 0 && !isAtAll {
+ return nil
+ }
+
+ atMessage := buildLarkAtMessage(idList, isAtAll)
+ return imService.SendFeishuHookText(uri, atMessage)
+}
+
func sendDingDingMessage(productName, workflowName, workflowDisplayName string, taskID int64, uri, title, message string, idList []string, isAtAll bool) error {
processedMessage := generateDingDingNotificationMessage(title, message, idList)
@@ -438,7 +798,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..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
@@ -173,6 +173,10 @@ func (gruem *gerritChangeMergedEventMatcherForWorkflowV4) GetHookRepo(hookRepo *
RepoOwner: hookRepo.RepoOwner,
RepoNamespace: hookRepo.GetRepoNamespace(),
Branch: hookRepo.Branch,
+ TargetBranch: hookRepo.Branch,
+ CommitID: gruem.Event.NewRev,
+ CommitMessage: gruem.Event.Change.CommitMessage,
+ Committer: hookRepo.Committer,
Source: hookRepo.Source,
}
}
@@ -223,7 +227,11 @@ func (gpcem *gerritPatchsetCreatedEventMatcherForWorkflowV4) GetHookRepo(hookRep
RepoOwner: hookRepo.RepoOwner,
RepoNamespace: hookRepo.GetRepoNamespace(),
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,
}
}
@@ -361,6 +369,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..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,12 +81,20 @@ 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,
RepoNamespace: hookRepo.GetRepoNamespace(),
RepoOwner: hookRepo.RepoOwner,
Branch: hookRepo.Branch,
+ TargetBranch: hookRepo.Branch,
+ CommitID: gpem.event.After,
+ CommitMessage: commitMessage,
+ Committer: hookRepo.Committer,
Source: hookRepo.Source,
}
}
@@ -139,7 +147,11 @@ 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,
+ CommitID: gmem.event.PullRequest.Head.Sha,
+ CommitMessage: gmem.event.PullRequest.Title,
+ Committer: hookRepo.Committer,
Source: hookRepo.Source,
}
}
@@ -173,7 +185,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 +220,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 +294,7 @@ func TriggerWorkflowV4ByGiteeEvent(event interface{}, baseURI, requestID string,
MergeRequestID: mergeRequestID,
CommitID: commitID,
EventType: eventType,
+ RawPayload: rawPayload,
}
case *gitee.PushEvent:
eventType = EventTypePush
@@ -297,11 +312,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..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
@@ -108,7 +108,11 @@ 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,
+ CommitID: gmem.event.ObjectAttributes.LastCommit.ID,
+ CommitMessage: gmem.event.ObjectAttributes.Title,
+ Committer: hookRepo.Committer,
Source: hookRepo.Source,
}
}
@@ -225,12 +229,20 @@ 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,
RepoOwner: hookRepo.RepoOwner,
RepoNamespace: hookRepo.GetRepoNamespace(),
Branch: hookRepo.Branch,
+ TargetBranch: hookRepo.Branch,
+ CommitID: gpem.event.After,
+ CommitMessage: commitMessage,
+ Committer: hookRepo.Committer,
Source: hookRepo.Source,
}
}
@@ -268,12 +280,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 +384,7 @@ func TriggerWorkflowV4ByGitlabEvent(event interface{}, baseURI, requestID string
CommitID: commitID,
CodehostID: eventRepo.CodehostID,
EventType: eventType,
+ RawPayload: rawPayload,
}
case *gitlab.PushEvent:
eventType = EventTypePush
@@ -387,11 +402,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_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)
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..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
@@ -458,64 +462,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..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
@@ -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,42 @@ func updateNotifyCtls(notifyCtls []*commonmodels.NotifyCtl, notifyInputs []*Crea
return notifyCtls
}
+func buildWorkflowTaskRuntimeContext(task *commonmodels.WorkflowTask) map[string]string {
+ 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,
+ task.ProjectDisplayName,
+ task.TaskID,
+ task.TaskCreator,
+ task.TaskCreatorAccount,
+ task.TaskCreatorID,
+ time.Unix(task.StartTime, 0),
+ ))
+
+ 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 +970,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 +1031,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 +1108,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 +1190,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"`