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]

Reply via email to