Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot use fine-grained PATs to add labels to pull requests #9166

Open
jamiesanson opened this issue Jun 4, 2024 · 13 comments
Open

Cannot use fine-grained PATs to add labels to pull requests #9166

jamiesanson opened this issue Jun 4, 2024 · 13 comments
Labels
bug Something isn't working needs-triage needs to be reviewed p2 Affects more than a few users but doesn't prevent core functions

Comments

@jamiesanson
Copy link

jamiesanson commented Jun 4, 2024

Describe the bug

Using certain gh pr commands never works with a fine-grained PAT as GH_TOKEN, regardless of scopes granted to it.

CLI version:

gh version 2.50.0 (2024-05-29)
https://github.com/cli/cli/releases/tag/v2.50.0

Steps to reproduce the behavior

  1. Create a fine-grained PAT with read-only access to every repository scope available, adding write access to the "pull request" scope.
  2. Use the PAT generated in the previous step as the token used with gh (GH_TOKEN)
  3. View an existing pull request: gh pr view 201
  4. Observe the failure.
See: Fine-grained PAT permissions

Expected vs actual behavior

Expected: PR can be viewed successfully.

Actual: Error: GraphQL: Resource not accessible by personal access token (repository.pullRequest.projectCards.nodes)

@jamiesanson jamiesanson added the bug Something isn't working label Jun 4, 2024
@cliAutomation cliAutomation added the needs-triage needs to be reviewed label Jun 4, 2024
@williammartin
Copy link
Member

Hey @jamiesanson, can you share the output of gh pr edit 201 --add-label Release with GH_DEBUG=api on? I suspect looking at the error that you need write permissions on something project related.

@jamiesanson
Copy link
Author

Sure! Here's the output using GH_DEBUG=api

➜  test-repo git:(main) ✗ gh pr edit 210 --add-label Release
[git remote -v]
[git config --get-regexp ^remote\..*\.gh-resolved$]
* Request at 2024-06-04 10:49:36.506297 +0100 BST m=+0.055295334
* Request to https://api.github.com/graphql
> POST /graphql HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ████████████████████
> Content-Length: 419
> Content-Type: application/json; charset=utf-8
> Graphql-Features: merge_queue
> Time-Zone: Europe/London
> User-Agent: GitHub CLI 2.50.0

GraphQL query:
fragment repo on Repository {
    id
    name
    owner { login }
    viewerPermission
    defaultBranchRef {
      name
    }
    isPrivate
  }
  query RepositoryNetwork {
    viewer { login }

    repo_000: repository(owner: "██████████", name: "██████████") {
      ...repo
      parent {
        ...repo
      }
    }

  }
GraphQL variables: null

< HTTP/2.0 200 OK
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Content-Security-Policy: default-src 'none'
< Content-Type: application/json; charset=utf-8
< Date: Tue, 04 Jun 2024 09:49:36 GMT
< Github-Authentication-Token-Expiration: 2024-06-11 10:49:09 +0100
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: GitHub.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept-Encoding, Accept, X-Requested-With
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Media-Type: github.v4; param=merge-info-preview.nebula-preview; format=json
< X-Github-Request-Id: D07C:1614D9:210EF8D:22A7647:665EE330
< X-Ratelimit-Limit: 5000
< X-Ratelimit-Remaining: 4997
< X-Ratelimit-Reset: 1717498164
< X-Ratelimit-Resource: graphql
< X-Ratelimit-Used: 3
< X-Xss-Protection: 0

{
  "data": {
    "viewer": {
      "login": "jamiesanson"
    },
    "repo_000": {
      "id": "R_kgDOLAaeWw",
      "name": "██████████",
      "owner": {
        "login": "██████████"
      },
      "viewerPermission": "ADMIN",
      "defaultBranchRef": {
        "name": "main"
      },
      "isPrivate": true,
      "parent": null
    }
  }
}

* Request took 356.815333ms
⣾* Request at 2024-06-04 10:49:36.863555 +0100 BST m=+0.412551459
* Request to https://api.github.com/graphql
> POST /graphql HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ████████████████████
> Content-Length: 680
> Content-Type: application/json; charset=utf-8
> Graphql-Features: merge_queue
> Time-Zone: Europe/London
> User-Agent: GitHub CLI 2.50.0

GraphQL query:
query PullRequestByNumber($owner: String!, $repo: String!, $pr_number: Int!) {
    repository(owner: $owner, name: $repo) {
      pullRequest(number: $pr_number) {id,url,title,body,baseRefName,reviewRequests(first: 100) {nodes {requestedReviewer {__typename,...on User{login},...on Team{organization{login}name,slug}}}},assignees(first:100){nodes{id,login,name},totalCount},labels(first:100){nodes{id,name,description,color},totalCount},projectCards(first:100){nodes{project{name}column{name}},totalCount},milestone{number,title,description,dueOn},number}
    }
  }
GraphQL variables: {"owner":"██████████","pr_number":210,"repo":"██████████"}

⣻< HTTP/2.0 200 OK
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Content-Security-Policy: default-src 'none'
< Content-Type: application/json; charset=utf-8
< Date: Tue, 04 Jun 2024 09:49:37 GMT
< Github-Authentication-Token-Expiration: 2024-06-11 10:49:09 +0100
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: GitHub.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept-Encoding, Accept, X-Requested-With
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Media-Type: github.v4; param=merge-info-preview.nebula-preview; format=json
< X-Github-Request-Id: D07C:1614D9:210EFC2:22A768F:665EE330
< X-Ratelimit-Limit: 5000
< X-Ratelimit-Remaining: 4996
< X-Ratelimit-Reset: 1717498164
< X-Ratelimit-Resource: graphql
< X-Ratelimit-Used: 4
< X-Xss-Protection: 0

{
  "data": {
    "repository": {
      "pullRequest": {
        "id": "PR_kwDOLAaeW85wsfdO",
        "url": "https://github.com/██████████/██████████/pull/210",
        "title": "Update to 1.3.1",
        "body": "This PR updates the version to 1.3.1.\nSince the repo is using proactive versioning, 1.3.1 is the next version to be released.\nThe most recent release can be found at https://github.com/██████████/██████████/releases/tag/v1.3.0\n",
        "baseRefName": "main",
        "reviewRequests": {
          "nodes": []
        },
        "assignees": {
          "nodes": [],
          "totalCount": 1
        },
        "labels": {
          "nodes": [],
          "totalCount": 0
        },
        "projectCards": {
          "nodes": null,
          "totalCount": 0
        },
        "milestone": null,
        "number": 210
      }
    }
  },
  "errors": [
    {
      "type": "FORBIDDEN",
      "path": [
        "repository",
        "pullRequest",
        "projectCards",
        "nodes"
      ],
      "extensions": {
        "saml_failure": false
      },
      "locations": [
        {
          "line": 4,
          "column": 335
        }
      ],
      "message": "Resource not accessible by personal access token"
    }
  ]
}

* Request took 298.212167ms
GraphQL: Resource not accessible by personal access token (repository.pullRequest.projectCards.nodes)

@jamiesanson
Copy link
Author

Note, I've also been able to reproduce the same issue with other gh pr commands - notably, gh pr view

@williammartin
Copy link
Member

Yeh so we can see here that when we're looking for the PR using the PullRequestByNumber query, we're also trying to load projectCards and the API is saying the token doesn't have permission. I would expect the same for most pr commands because they all use the same mechanism for fetching a PR.

Can you try giving Project read access on the resource? I think it's probably under Organization rather than Repository in the settings page.

@jamiesanson
Copy link
Author

I've given the fine-grained PAT write access to every scope available, and it still results in the same issue:
Screenshot 2024-06-04 at 11 04 30

@jamiesanson
Copy link
Author

jamiesanson commented Jun 4, 2024

Note: We've seen this working using a Classic Token with the Project read scope, so feels like an issue with fine-grained PATs and project access. The scopes we used were:

  • repo (for private repository access)
  • read:org (for users)
  • read:project (for project cards, presumably)

@jamiesanson
Copy link
Author

jamiesanson commented Jun 4, 2024

Another note: We've also had this working with github.token in CI. These permissions yield a token which works:

permissions:
   # write: gh pr edit
   pull-requests: write
   # gh pr edit (workaround for this issue)
   repository-projects: read

@williammartin
Copy link
Member

Hmmm looks like something similar was reported in March but wasn't triaged: #8784 and also on the community forums: https://github.com/orgs/community/discussions/111324

It may well be that fine-grained PATs don't support this. It wouldn't be the first time that things haven't been fully implemented. I'll see what I can find internally, thanks for your continued investigation.

@williammartin
Copy link
Member

williammartin commented Jun 4, 2024

In the meantime I wonder whether we could be smarter about what we try to fetch. Like, is it really necessary for us to fetch projectCards in all cases?

Also loosely related: #6274

@williammartin williammartin added p2 Affects more than a few users but doesn't prevent core functions and removed needs-user-input needs-triage needs to be reviewed labels Jun 4, 2024
@jamiesanson
Copy link
Author

In the meantime I wonder whether we could be smarter about what we try to fetch. Like, is it really necessary for us to fetch projectCards in all cases?

Yeah, we've had similar conversations internally. Based on what we've read in the code & documentation, --add-project and --remove-project are the only options which need it (aside from the view command, maybe), so could potentially only fetch projectCards in those cases.

@jayspadie
Copy link

Projects (classic) can be owned by the org or by the repository. Naively, when I see code like the above or in programmatic_access.yaml, it makes me wonder if we somehow missed adding project permissions to the Repository scope (repository_projects) in the Find-grained PAT UI, and we only offer the ability to create Organization scoped permissions (organization_projects).

https://github.com/github/github/blob/79f0df60f71c82e31b33361389b6e8819ee7b33d/config/access_control/programmatic_access.yaml#L9487-L9498

- namespace: Api::Projects
  endpoint: GET /projects/columns/:column_id/cards
  service: projects
  access_definition: show_project
  server_to_server:
    enabled: true
  user_to_server:
    enabled: true
  allows_public_read: true
  permission_sets:
  - repository_projects: read
  - organization_projects: read

@ajvpot
Copy link

ajvpot commented Jun 11, 2024

In the meantime I wonder whether we could be smarter about what we try to fetch. Like, is it really necessary for us to fetch projectCards in all cases?

Yeah, we've had similar conversations internally. Based on what we've read in the code & documentation, --add-project and --remove-project are the only options which need it (aside from the view command, maybe), so could potentially only fetch projectCards in those cases.

This would definitely be helpful for us, we do not use projects and it would be ideal to not have to grant an extraneous permission.

@dezren39
Copy link

also experiencing this with
gh pr checks --interval 30 --watch
or maybe
gh pr status --json statusCheckRollup --jq '[{ state: .currentBranch.statusCheckRollup[].state, conclusion: .currentBranch.statusCheckRollup[].conclusion }] | any(.state != null and .state != "SUCCESS") or any(.conclusion != null and .conclusion != "SUCCESS")'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working needs-triage needs to be reviewed p2 Affects more than a few users but doesn't prevent core functions
Projects
None yet
Development

No branches or pull requests

6 participants