Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8952282
feat(workflow): pass through webhook payload to runtime notifications
huanghongbo-hhb May 7, 2026
ec35e66
fix(workflow): preserve runtime context job outputs
huanghongbo-hhb May 11, 2026
ab09ea3
fix(workflow): preserve webhook repo context fields
huanghongbo-hhb May 11, 2026
2596a28
refactor(notification): reuse key map and lark at builder
huanghongbo-hhb May 11, 2026
9501566
refactor(notification): reuse feishu sender and escape urls
huanghongbo-hhb May 11, 2026
91454da
fix(workflow): preserve webhook repo fields across scm providers
huanghongbo-hhb May 12, 2026
394b634
fix(workflow): keep only payload runtime variables
huanghongbo-hhb Jun 5, 2026
d8a006f
fix dynamic notification recipients
huanghongbo-hhb Jun 16, 2026
1e275bd
support workflow trigger runtime variables
huanghongbo-hhb Jun 16, 2026
c9da469
feat(workflow): add commit sha runtime variable
huanghongbo-hhb Jun 16, 2026
714a28b
fix trigger vars and lark hook recipients
huanghongbo-hhb Jun 17, 2026
68566c3
fix payload scope for workflow runtime
huanghongbo-hhb Jun 17, 2026
acf362e
fix: allow payload variables in notification recipients
huanghongbo-hhb Jun 17, 2026
83903fe
fix: preserve notification dynamic recipients runtime rendering
huanghongbo-hhb Jun 18, 2026
b391772
chore: drop notification runtime tests from pr
huanghongbo-hhb Jun 18, 2026
5c20ad8
fix: preserve notification payload templates at runtime
huanghongbo-hhb Jun 18, 2026
571de67
fix: send mail to dynamic email recipients
huanghongbo-hhb Jun 23, 2026
ae3d561
fix: resolve workflow notification dynamic recipients
huanghongbo-hhb Jun 23, 2026
713db51
fix: make dingding notifications mention recipients
huanghongbo-hhb Jun 24, 2026
7031482
fix: restrict dynamic notification recipients
huanghongbo-hhb Jun 26, 2026
e51cea0
chore: remove notification runtime tests from pr
huanghongbo-hhb Jun 26, 2026
6d6f28b
fix: clarify notification runtime variables
huanghongbo-hhb Jun 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions pkg/cli/upgradeassistant/cmd/migrate/430.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,21 @@ func V421ToV430() error {
updateMigrationError(migrationInfo.ID, err)
}()

// 这次迁移分三段
// 这次迁移分四段
// 1. MySQL: user 表新增 api_token_enabled
// 2. MySQL: permission action + role/template 绑定
// 3. Mongo: collaboration mode / instance verbs
// 2. MySQL: user 表新增 email/phone 索引
// 3. MySQL: permission action + role/template 绑定
// 4. Mongo: collaboration mode / instance verbs
err = migrateUserAPITokenEnabledColumn(ctx, migrationInfo)
if err != nil {
return err
}

err = migrateUserContactIndexes(migrationInfo)
if err != nil {
return err
}

err = migrateGlobalReadOnlyRole(ctx, migrationInfo)
if err != nil {
return err
Expand All @@ -144,6 +150,29 @@ func V421ToV430() error {
return nil
}

// migrateUserContactIndexes adds indexes for dynamic notification recipient lookups.
func migrateUserContactIndexes(migrationInfo *internalmodels.Migration) error {
if !migrationInfo.Migration430UserContactIndexes {
if !repository.DB.Migrator().HasIndex(&usermodels.User{}, "idx_email") {
if err := repository.DB.Migrator().CreateIndex(&usermodels.User{}, "idx_email"); err != nil {
return fmt.Errorf("failed to add idx_email index for user table, err: %s", err)
}
}

if !repository.DB.Migrator().HasIndex(&usermodels.User{}, "idx_phone") {
if err := repository.DB.Migrator().CreateIndex(&usermodels.User{}, "idx_phone"); err != nil {
return fmt.Errorf("failed to add idx_phone index for user table, err: %s", err)
}
}
}

_ = internalmongodb.NewMigrationColl().UpdateMigrationStatus(migrationInfo.ID, map[string]interface{}{
getMigrationFieldBsonTag(migrationInfo, &migrationInfo.Migration430UserContactIndexes): true,
})

return nil
}

// migrateUserAPITokenEnabledColumn adds api_token_enabled column for user table.
func migrateUserAPITokenEnabledColumn(_ *internalhandler.Context, migrationInfo *internalmodels.Migration) error {
if !migrationInfo.Migration430UserAPITokenEnabled {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type Migration struct {
Migration421CollaborationRollbackPermission bool `bson:"migration_421_collaboration_rollback_permission"`
Migration421WorkflowDeploySpec bool `bson:"migration_421_workflow_deploy_spec"`
Migration430UserAPITokenEnabled bool `bson:"migration_430_user_api_token_enabled"`
Migration430UserContactIndexes bool `bson:"migration_430_user_contact_indexes"`
Migration430GlobalReadOnlyRole bool `bson:"migration_430_global_read_only_role"`
Migration430ScalePermission bool `bson:"migration_430_scale_permission"`
Migration430CollaborationScalePermission bool `bson:"migration_430_collaboration_scale_permission"`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package models

import (
"encoding/json"
"fmt"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsontype"
)

// DynamicRecipients keeps compatibility with the temporary PR schema
// [{"value":"{{.payload.user.email}}","identity_type":"email"}].
type DynamicRecipients []string

type legacyDynamicRecipient struct {
Value string `bson:"value" json:"value"`
IdentityType string `bson:"identity_type" json:"identity_type"`
}

func (r *DynamicRecipients) UnmarshalJSON(data []byte) error {
var recipients []string
if err := json.Unmarshal(data, &recipients); err == nil {
*r = recipients
return nil
}

var legacyRecipients []legacyDynamicRecipient
if err := json.Unmarshal(data, &legacyRecipients); err != nil {
return fmt.Errorf("failed to unmarshal dynamic recipients: %w", err)
}

*r = legacyDynamicRecipientsToStrings(legacyRecipients)
return nil
}

func (r *DynamicRecipients) UnmarshalBSONValue(t bsontype.Type, data []byte) error {
if t == bsontype.Null || t == bsontype.Undefined {
*r = nil
return nil
}

var recipients []string
if err := (bson.RawValue{Type: t, Value: data}).Unmarshal(&recipients); err == nil {
*r = recipients
return nil
}

var legacyRecipients []legacyDynamicRecipient
if err := (bson.RawValue{Type: t, Value: data}).Unmarshal(&legacyRecipients); err != nil {
return fmt.Errorf("failed to unmarshal dynamic recipients: %w", err)
}

*r = legacyDynamicRecipientsToStrings(legacyRecipients)
return nil
}

func legacyDynamicRecipientsToStrings(recipients []legacyDynamicRecipient) []string {
resp := make([]string, 0, len(recipients))
for _, recipient := range recipients {
resp = append(resp, recipient.Value)
}
return resp
}
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,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"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -485,14 +485,19 @@ type HookPayload struct {
Owner string `bson:"owner" json:"owner,omitempty"`
Repo string `bson:"repo" json:"repo,omitempty"`
Branch string `bson:"branch" json:"branch,omitempty"`
TargetBranch string `bson:"target_branch" json:"target_branch,omitempty"`
Ref string `bson:"ref" json:"ref,omitempty"`
IsPr bool `bson:"is_pr" json:"is_pr,omitempty"`
CheckRunID int64 `bson:"check_run_id" json:"check_run_id,omitempty"`
MergeRequestID string `bson:"merge_request_id" json:"merge_request_id,omitempty"`
CommitID string `bson:"commit_id" json:"commit_id,omitempty"`
CommitSHA string `bson:"commit_sha" json:"commit_sha,omitempty"`
CommitMessage string `bson:"commit_message" json:"commit_message,omitempty"`
Committer string `bson:"committer" json:"committer,omitempty"`
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 {
Expand Down
60 changes: 36 additions & 24 deletions pkg/microservice/aslan/core/common/repository/models/workflow_v4.go
Original file line number Diff line number Diff line change
Expand Up @@ -1173,12 +1173,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"`
Expand Down Expand Up @@ -1262,6 +1262,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)
Expand All @@ -1272,42 +1276,50 @@ func (n *NotificationJobSpec) GenerateNewNotifyConfigWithOldData() error {

// 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 DynamicRecipients `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 DynamicRecipients `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"`
AppID string `bson:"app_id" json:"app_id" yaml:"app_id"`
HookAddress string `bson:"hook_address" json:"hook_address" yaml:"hook_address"`
AtUsers []string `bson:"at_users" json:"at_users" yaml:"at_users"`
DynamicRecipients DynamicRecipients `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 DynamicRecipients `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 DynamicRecipients `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 DynamicRecipients `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 DynamicRecipients `bson:"dynamic_recipients" json:"dynamic_recipients" yaml:"dynamic_recipients"`
}

type WebhookNotificationConfig struct {
Expand Down
Loading
Loading