diff --git a/backend/core/models/domainlayer/crossdomain/account.go b/backend/core/models/domainlayer/crossdomain/account.go index eba65fd6303..e8ae63a1ebd 100644 --- a/backend/core/models/domainlayer/crossdomain/account.go +++ b/backend/core/models/domainlayer/crossdomain/account.go @@ -18,8 +18,9 @@ limitations under the License. package crossdomain import ( - "github.com/apache/incubator-devlake/core/models/domainlayer" "time" + + "github.com/apache/incubator-devlake/core/models/domainlayer" ) type Account struct { @@ -31,6 +32,7 @@ type Account struct { Organization string `gorm:"type:varchar(255)"` CreatedDate *time.Time Status int + IsBot bool `gorm:"default:false"` } func (Account) TableName() string { diff --git a/backend/core/models/migrationscripts/20260720_add_is_bot_to_accounts.go b/backend/core/models/migrationscripts/20260720_add_is_bot_to_accounts.go new file mode 100644 index 00000000000..47b53660c36 --- /dev/null +++ b/backend/core/models/migrationscripts/20260720_add_is_bot_to_accounts.go @@ -0,0 +1,35 @@ +package migrationscripts + +import ( + "github.com/apache/incubator-devlake/core/context" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/plugin" +) + +var _ plugin.MigrationScript = (*addIsBotToAccounts)(nil) + +type account20260720 struct { + IsBot bool `gorm:"default:false"` +} + +func (account20260720) TableName() string { + return "accounts" +} + +type addIsBotToAccounts struct{} + +func (*addIsBotToAccounts) Up(basicRes context.BasicRes) errors.Error { + db := basicRes.GetDal() + if err := db.AutoMigrate(&account20260720{}); err != nil { + return err + } + return nil +} + +func (*addIsBotToAccounts) Version() uint64 { + return 20260720120000 +} + +func (*addIsBotToAccounts) Name() string { + return "add is_bot to accounts so bot/automation activity can be excluded from metrics, according to #8974" +} diff --git a/backend/core/models/migrationscripts/register.go b/backend/core/models/migrationscripts/register.go index e25363a1b40..9099b32a235 100644 --- a/backend/core/models/migrationscripts/register.go +++ b/backend/core/models/migrationscripts/register.go @@ -148,5 +148,6 @@ func All() []plugin.MigrationScript { new(changeIssueComponentToText), new(changeCqIssueCodeBlocksComponentToText), new(addCqProjectMetricsHistory), + new(addIsBotToAccounts), } } diff --git a/backend/plugins/dora/tasks/change_lead_time_calculator.go b/backend/plugins/dora/tasks/change_lead_time_calculator.go index 9861a5c7a9e..57e5b730968 100644 --- a/backend/plugins/dora/tasks/change_lead_time_calculator.go +++ b/backend/plugins/dora/tasks/change_lead_time_calculator.go @@ -248,7 +248,9 @@ func batchFetchFirstReviews(projectName string, db dal.Dal) (map[string]*code.Pu SELECT prc2.pull_request_id, MIN(prc2.created_date) as min_date FROM pull_request_comments prc2 INNER JOIN pull_requests pr2 ON pr2.id = prc2.pull_request_id + LEFT JOIN accounts acc2 ON acc2.id = prc2.account_id WHERE (pr2.author_id IS NULL OR pr2.author_id = '' OR prc2.account_id != pr2.author_id) + AND COALESCE(acc2.is_bot, false) = false GROUP BY prc2.pull_request_id ) first_reviews ON prc.pull_request_id = first_reviews.pull_request_id AND prc.created_date = first_reviews.min_date`), diff --git a/backend/plugins/github/tasks/account_convertor.go b/backend/plugins/github/tasks/account_convertor.go index 3d4db97d155..310780fc7f6 100644 --- a/backend/plugins/github/tasks/account_convertor.go +++ b/backend/plugins/github/tasks/account_convertor.go @@ -41,6 +41,7 @@ type repoAccountForConvert struct { Name string Email string AvatarUrl string + Type string common.NoPKModel } @@ -96,6 +97,7 @@ func ConvertAccounts(taskCtx plugin.SubTaskContext) errors.Error { COALESCE(ga.name, '') AS name, COALESCE(ga.email, '') AS email, COALESCE(ga.avatar_url, '') AS avatar_url, + COALESCE(ga.type, '') AS type, COALESCE(ga._raw_data_params, _tool_github_repo_accounts._raw_data_params) AS _raw_data_params, COALESCE(ga._raw_data_table, _tool_github_repo_accounts._raw_data_table) AS _raw_data_table, COALESCE(ga._raw_data_id, _tool_github_repo_accounts._raw_data_id) AS _raw_data_id, @@ -145,6 +147,7 @@ func ConvertAccounts(taskCtx plugin.SubTaskContext) errors.Error { orgStr = orgStr[:255] } } + isBot := githubUser.Type == "Bot" || strings.HasSuffix(githubUser.Login, "[bot]") domainUser := &crossdomain.Account{ DomainEntity: domainlayer.DomainEntity{Id: accountIdGen.Generate(data.Options.ConnectionId, githubUser.Id)}, @@ -153,6 +156,7 @@ func ConvertAccounts(taskCtx plugin.SubTaskContext) errors.Error { UserName: githubUser.Login, AvatarUrl: githubUser.AvatarUrl, Organization: orgStr, + IsBot: isBot, } return []interface{}{ domainUser,