zyv4yk opened a new issue, #9085:
URL: https://github.com/apache/devlake/issues/9085
### Search before asking
- [x] I had searched in the issues and found no similar issues.
### What happened
When the GitHub connection has `Use GraphQL APIs` enabled, every pull
request, issue and review opened by an actor of GraphQL type `Bot` (GitHub
Apps, Dependabot, Renovate, GitHub Actions, any AI code-review or automation
app) is stored with an **empty author**: `author_name = ''` and `author_id = 0`
in `_tool_github_pull_requests`, and no `pull_requests.author_id` in the domain
layer.
The data is already empty in the raw layer, so it cannot be recovered by
re-running the extractor:
```json
"Author": {"Login": "", "Id": 0, "Name": "", "Company": "", "Email": "", ...}
```
Root cause is in
`backend/plugins/github_graphql/tasks/account_graphql_pre_extractor.go`:
```go
type GraphqlInlineAccountQuery struct {
GithubAccountEdge `graphql:"... on User"`
}
func extractGraphqlPreAccount(result *[]interface{}, res
*GraphqlInlineAccountQuery, repoId int, connId uint64) {
if res == nil || res.Id == 0 {
return
}
```
`author`, `mergedBy`, `assignees` and review authors are all typed as
`Actor` in the GitHub schema. `Actor` is implemented by `User`, **`Bot`**,
`Organization`, `Mannequin` and `EnterpriseUserAccount`. The query spreads only
`... on User`, so for any non-`User` actor GitHub returns an empty selection
set, and the `res.Id == 0` guard then drops the account entirely.
The REST collector is not affected — it resolves bot authors correctly —
which is why a connection using GraphQL shows almost no bot-authored PRs while
a REST one does.
### What do you expect to happen
PRs, issues and reviews opened by GitHub Apps / bots should carry their
author, with the same identity the REST collector produces (`dependabot[bot]`,
id `49699333`), so that `accounts` rows, `pull_requests.author_id` and any
bot-aware metric work the same regardless of which collector is used.
### How to reproduce
The behaviour can be shown against the GitHub API directly, no DevLake
instance needed. This query mirrors what the plugin sends today
(`userFragmentOnly`) next to what it should send (`withBotFragment`):
```bash
gh api graphql -f query='
{
search(query: "repo:grafana/grafana is:pr author:app/dependabot", type:
ISSUE, first: 2) {
nodes {
... on PullRequest {
number
userFragmentOnly: author { ... on User { login databaseId } }
withBotFragment: author { __typename ... on User { login databaseId
} ... on Bot { login databaseId } }
}
}
}
}'
```
Result:
```json
{"number":131544,
"userFragmentOnly":{},
"withBotFragment":{"__typename":"Bot","login":"dependabot","databaseId":49699333}}
```
End to end:
1. Create a GitHub connection with `Use GraphQL APIs` enabled, scoped to a
repo that receives Dependabot/Renovate/GitHub App PRs.
2. Run the blueprint.
3. `SELECT number, author_name, author_id FROM _tool_github_pull_requests
WHERE author_name = '';` — every bot-authored PR is listed.
4. Repeat with `Use GraphQL APIs` disabled: the same PRs come back with
`dependabot[bot]` / `49699333`.
### Anything else
Happens every time, for every bot-authored PR, on every repo. In our
deployment roughly 1,000 PRs opened by our own GitHub App over the last eight
months are unattributed, and all 18 bot accounts in `_tool_github_accounts`
(collected from comments and reviews) have zero PRs linked to them.
Two details worth knowing before writing the fix:
**1. `Bot` has a different field set than `User`.** It exposes `login`,
`databaseId`, `avatarUrl`, `url`, `createdAt`, `updatedAt` — but no `name`,
`company` or `email`. `GithubAccountEdge` cannot be reused as-is; a separate
struct plus a small normalisation step is needed:
```go
type GithubBotEdge struct {
Login string
Id int `graphql:"databaseId"`
AvatarUrl string
HtmlUrl string `graphql:"url"`
}
type GraphqlInlineAccountQuery struct {
GithubAccountEdge `graphql:"... on User"`
Bot GithubBotEdge `graphql:"... on Bot"`
}
```
The named-field-with-inline-fragment-tag pattern is already used in this
codebase for `RequestedReviewer` (#7716), so the client handles it.
**2. `Bot.login` omits the `[bot]` suffix.** GraphQL returns `dependabot`,
REST returns `dependabot[bot]`; the `databaseId` is the same `49699333` in
both, so there is no risk of duplicate accounts, but the logins would diverge
between collectors.
This matters for the `is_bot` flag added in #9000: the fallback is
`strings.HasSuffix(githubUser.Login, "[bot]")`, and
`_tool_github_accounts.type` is never populated on the GraphQL path
(`GithubAccountEdge` has no `Type` field, and `extractGraphqlPreAccount` only
writes `GithubRepoAccount`). So a naive fix would produce bot accounts that
`is_bot` cannot detect. The normalisation should append `[bot]` to match REST,
and ideally set `Type: "Bot"` on the `GithubAccount` row.
It also interacts with #8886 / #8894: `pr_convertor.go` now guards `if
pr.AuthorId != 0` before generating the account id, so as long as the author
stays empty these PRs will keep having no `author_id` by design — the fix has
to happen at collection time.
Existing data cannot be repaired by re-running extract or convert; a **Full
Refresh** re-collect is required, since the author is already empty in
`_raw_github_graphql_prs`.
Same fix applies to `mergedBy`, which is currently empty for merges
performed by a bot.
### Version
Verified on `main` @ `79ef9f4f`; the code is unchanged since #8583
(2025-09-24) and is present in `v1.0.3-beta16`.
### Are you willing to submit PR?
- [x] Yes I am willing to submit a PR!
### Code of Conduct
- [x] I agree to follow this project's [Code of
Conduct](https://www.apache.org/foundation/policies/conduct)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]