magit / forge

Work with Git forges from the comfort of Magit
GNU General Public License v3.0
1.32k stars 117 forks source link

fails to gracefully handle pre-existing GitHub auth token #61

Closed aspiers closed 5 years ago

aspiers commented 5 years ago

I had some issues with initial auth token setup (I think probably due to an old ghub as per #5), which resulted in the token being created on GitHub but not correctly set up locally (I guess in .authinfo). When I tried to step through again, I think I found a bug in the code which tries to warn the user that there is already an auth token on GitHub of the same name. The problem seems to be in this part of ghub--confirm-create-token:

(if (string-equal (let-alist (nth 3 ghub--create-token-error)
                               (car .errors.code))
                             "already_exists")

I stepped through with edebug and saw this as ghub--create-token-error:

Result: (ghub-http-error 422 "Unprocessable Entity (Added by DAV)" "/authorizations" ((message . "Validation Failed") (errors ((resource . "OauthAccess") (code . "already_exists") (field . "description"))) (documentation_url . "https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization")))

So the first problem is that (nth 3 ghub--create-token-error) evaluates to "/authorizations" not the payload.

Secondly, .errors returns a list of errors, not a single error, so you can't use .code on it directly; instead you need another nested let-alist.

I'll submit a PR with a fix.

Here's the full stacktrace for reference:

Debugger entered--Lisp error: (wrong-type-argument listp "/authorizations")
  assq(errors "/authorizations")
  (cdr (assq 'errors alist))
  (let ((\.errors (cdr (assq 'errors alist)))) (let ((alist (car \.errors))) (let ((\.code (cdr (assq 'code alist)))) \.code)))
  (let ((alist (nth 3 ghub--create-token-error))) (let ((\.errors (cdr (assq 'errors alist)))) (let ((alist (car \.errors))) (let ((\.code (cdr (assq 'code alist)))) \.code))))
  (string-equal (let ((alist (nth 3 ghub--create-token-error))) (let ((\.errors (cdr (assq 'errors alist)))) (let ((alist (car \.errors))) (let ((\.code (cdr (assq 'code alist)))) \.code)))) "already_exists")
  (if (string-equal (let ((alist (nth 3 ghub--create-token-error))) (let ((\.errors (cdr (assq 'errors alist)))) (let ((alist (car \.errors))) (let ((\.code (cdr (assq 'code alist)))) \.code)))) "already_exists") (error "A token named %S already exists on Github. Please visit https://github.com/settings/tokens and delete it." ident))
  (condition-case ghub--create-token-error (ghub-create-token host username package scopes) (ghub-http-error (if (string-equal (let ((alist (nth 3 ghub--create-token-error))) (let ((\.errors (cdr (assq 'errors alist)))) (let ((alist (car \.errors))) (let ((\.code (cdr (assq 'code alist)))) \.code)))) "already_exists") (error "A token named %S already exists on Github. Please visit https://github.com/settings/tokens and delete it." ident))))
  (if (let ((message-log-max nil)) (yes-or-no-p (format "Such a Github API token is not available:\n\n  Host:    %s\n  User:    %s\n  Package: %s\n\n  Scopes requested in `%s-github-token-scopes':\n%s\n  Store on Github as:\n    %S\n  Store locally according to option `auth-sources':\n    %S\n%s\nIf in doubt, then abort and first view the section of\nthe Ghub documentation called \"Interactively Creating\nand Storing a Token\".\n\nOtherwise confirm and then provide your Github username and\npassword at the next two prompts.  Depending on the backend\nyou might have to provide a passphrase and confirm that you\nreally want to save the token.\n\nCreate and store such a token? " host username package package (mapconcat (function (lambda (scope) (format "    %s" scope))) scopes "\n") ident auth-sources (if (and (stringp (car auth-sources)) (not (string-suffix-p ".gpg" (car auth-sources)))) (format "\nWARNING: The token will be stored unencrypted in %S.\n         If you don't want that, you have to abort and customize\n         the `auth-sources' option.\n" (car auth-sources)) "")))) (condition-case ghub--create-token-error (ghub-create-token host username package scopes) (ghub-http-error (if (string-equal (let ((alist (nth 3 ghub--create-token-error))) (let ((\.errors (cdr (assq 'errors alist)))) (let ((alist (car \.errors))) (let ((\.code (cdr (assq 'code alist)))) \.code)))) "already_exists") (error "A token named %S already exists on Github. Please visit https://github.com/settings/tokens and delete it." ident)))) (user-error "Abort"))
  (let* ((ident (ghub--ident-github package)) (scopes (ghub--package-scopes package)) (max-mini-window-height 40)) (if (let ((message-log-max nil)) (yes-or-no-p (format "Such a Github API token is not available:\n\n  Host:    %s\n  User:    %s\n  Package: %s\n\n  Scopes requested in `%s-github-token-scopes':\n%s\n  Store on Github as:\n    %S\n  Store locally according to option `auth-sources':\n    %S\n%s\nIf in doubt, then abort and first view the section of\nthe Ghub documentation called \"Interactively Creating\nand Storing a Token\".\n\nOtherwise confirm and then provide your Github username and\npassword at the next two prompts.  Depending on the backend\nyou might have to provide a passphrase and confirm that you\nreally want to save the token.\n\nCreate and store such a token? " host username package package (mapconcat (function (lambda (scope) (format "    %s" scope))) scopes "\n") ident auth-sources (if (and (stringp (car auth-sources)) (not (string-suffix-p ".gpg" (car auth-sources)))) (format "\nWARNING: The token will be stored unencrypted in %S.\n         If you don't want that, you have to abort and customize\n         the `auth-sources' option.\n" (car auth-sources)) "")))) (condition-case ghub--create-token-error (ghub-create-token host username package scopes) (ghub-http-error (if (string-equal (let ((alist (nth 3 ghub--create-token-error))) (let ((\.errors (cdr (assq 'errors alist)))) (let ((alist (car \.errors))) (let ((\.code (cdr (assq 'code alist)))) \.code)))) "already_exists") (error "A token named %S already exists on Github. Please visit https://github.com/settings/tokens and delete it." ident)))) (user-error "Abort")))
  ghub--confirm-create-token("api.github.com" "aspiers" forge)
  (cond ((memql forge '(nil github)) (ghub--confirm-create-token host username package)) ((memql forge '(gitlab gitea gogs bitbucket)) (error "Required %s token (%S for %S) does not exist.\nSee https://magit.vc/manual/ghub/Support-for-Other-Forges.html for instructions." (capitalize (symbol-name forge)) user host)) ((error "cl-ecase failed: %s, %s" forge '(github nil bitbucket gogs gitea gitlab)) nil))
  (and (not nocreate) (cond ((memql forge '(nil github)) (ghub--confirm-create-token host username package)) ((memql forge '(gitlab gitea gogs bitbucket)) (error "Required %s token (%S for %S) does not exist.\nSee https://magit.vc/manual/ghub/Support-for-Other-Forges.html for instructions." (capitalize (symbol-name forge)) user host)) ((error "cl-ecase failed: %s, %s" forge '(github nil bitbucket gogs gitea gitlab)) nil)))
  (progn (auth-source-forget (list :host host :user user :max 1)) (and (not nocreate) (cond ((memql forge '(nil github)) (ghub--confirm-create-token host username package)) ((memql forge '(gitlab gitea gogs bitbucket)) (error "Required %s token (%S for %S) does not exist.\nSee https://magit.vc/manual/ghub/Support-for-Other-Forges.html for instructions." (capitalize (symbol-name forge)) user host)) ((error "cl-ecase failed: %s, %s" forge '(github nil bitbucket gogs gitea gitlab)) nil))))
  (or (car (ghub--auth-source-get (list :secret) :host host :user user)) (progn (auth-source-forget (list :host host :user user :max 1)) (and (not nocreate) (cond ((memql forge '(nil github)) (ghub--confirm-create-token host username package)) ((memql forge '(gitlab gitea gogs bitbucket)) (error "Required %s token (%S for %S) does not exist.\nSee https://magit.vc/manual/ghub/Support-for-Other-Forges.html for instructions." (capitalize (symbol-name forge)) user host)) ((error "cl-ecase failed: %s, %s" forge '(github nil bitbucket gogs gitea gitlab)) nil)))))
  (let* ((user (ghub--ident username package)) (token (or (car (ghub--auth-source-get (list :secret) :host host :user user)) (progn (auth-source-forget (list :host host :user user :max 1)) (and (not nocreate) (cond ((memql forge '(nil github)) (ghub--confirm-create-token host username package)) ((memql forge '(gitlab gitea gogs bitbucket)) (error "Required %s token (%S for %S) does not exist.\nSee https://magit.vc/manual/ghub/Support-for-Other-Forges.html for instructions." (capitalize (symbol-name forge)) user host)) ((error "cl-ecase failed: %s, %s" forge '(github nil bitbucket gogs gitea gitlab)) nil))))))) (if (functionp token) (funcall token) token))
  ghub--token("api.github.com" "aspiers" forge nil nil)
  (cond ((stringp auth) auth) ((null auth) (ghub--token host username 'ghub nil forge)) ((symbolp auth) (ghub--token host username auth nil forge)) ('t (signal 'wrong-type-argument (list '(or stringp symbolp) auth))))
  (encode-coding-string (cond ((stringp auth) auth) ((null auth) (ghub--token host username 'ghub nil forge)) ((symbolp auth) (ghub--token host username auth nil forge)) ('t (signal 'wrong-type-argument (list '(or stringp symbolp) auth)))) 'utf-8)
  (concat (and (not (eq forge 'gitlab)) "token ") (encode-coding-string (cond ((stringp auth) auth) ((null auth) (ghub--token host username 'ghub nil forge)) ((symbolp auth) (ghub--token host username auth nil forge)) ('t (signal 'wrong-type-argument (list '(or stringp symbolp) auth)))) 'utf-8))
  (cons (cond ((memql forge '(nil github gitea gogs bitbucket)) "Authorization") ((eql forge 'gitlab) "Private-Token") ((error "cl-ecase failed: %s, %s" forge '(bitbucket gogs gitea github nil gitlab)) nil)) (concat (and (not (eq forge 'gitlab)) "token ") (encode-coding-string (cond ((stringp auth) auth) ((null auth) (ghub--token host username 'ghub nil forge)) ((symbolp auth) (ghub--token host username auth nil forge)) ('t (signal 'wrong-type-argument (list '(or stringp symbolp) auth)))) 'utf-8)))
  (if (eq auth 'basic) (cond ((memql forge '(nil github gitea gogs bitbucket)) (cons "Authorization" (ghub--basic-auth host username))) ((eql forge 'gitlab) (error "Gitlab does not support basic authentication")) ((error "cl-ecase failed: %s, %s" forge '(bitbucket gogs gitea github nil gitlab)) nil)) (cons (cond ((memql forge '(nil github gitea gogs bitbucket)) "Authorization") ((eql forge 'gitlab) "Private-Token") ((error "cl-ecase failed: %s, %s" forge '(bitbucket gogs gitea github nil gitlab)) nil)) (concat (and (not (eq forge 'gitlab)) "token ") (encode-coding-string (cond ((stringp auth) auth) ((null auth) (ghub--token host username 'ghub nil forge)) ((symbolp auth) (ghub--token host username auth nil forge)) ('t (signal 'wrong-type-argument (list '(or stringp symbolp) auth)))) 'utf-8))))
  ghub--auth("api.github.com" forge "aspiers" nil)
  (cons (ghub--auth host auth username forge) headers)
  (if (eq auth 'basic) (cons (cons "Authorization" (ghub--basic-auth host username)) headers) (cons (ghub--auth host auth username forge) headers))
  (closure ((forge) (username . "aspiers") (auth . forge) (host . "api.github.com") (headers ("Content-Type" . "application/json")) cl-struct-ghub--req-tags url-http-response-status url-http-extra-headers url-http-end-of-headers url-callback-arguments t) nil (if (eq auth 'basic) (cons (cons "Authorization" (ghub--basic-auth host username)) headers) (cons (ghub--auth host auth username forge) headers)))()
  funcall((closure ((forge) (username . "aspiers") (auth . forge) (host . "api.github.com") (headers ("Content-Type" . "application/json")) cl-struct-ghub--req-tags url-http-response-status url-http-extra-headers url-http-end-of-headers url-callback-arguments t) nil (if (eq auth 'basic) (cons (cons "Authorization" (ghub--basic-auth host username)) headers) (cons (ghub--auth host auth username forge) headers))))
  (if (functionp headers) (funcall headers) headers)
  (let ((headers (progn (or (and (memq (type-of req) cl-struct-ghub--req-tags) t) (signal 'wrong-type-argument (list 'ghub--req req))) (aref req 5)))) (if (functionp headers) (funcall headers) headers))
  (let ((url-request-extra-headers (let ((headers (progn (or (and (memq (type-of req) cl-struct-ghub--req-tags) t) (signal 'wrong-type-argument (list 'ghub--req req))) (aref req 5)))) (if (functionp headers) (funcall headers) headers))) (url-request-method (progn (or (and (memq (type-of req) cl-struct-ghub--req-tags) t) (signal 'wrong-type-argument (list 'ghub--req req))) (aref req 4))) (url-request-data payload) (url-show-status nil) (url (progn (or (and (memq (type-of req) cl-struct-ghub--req-tags) t) (signal 'wrong-type-argument (list 'ghub--req req))) (aref req 1))) (handler (progn (or (and (memq (type-of req) cl-struct-ghub--req-tags) t) (signal 'wrong-type-argument (list 'ghub--req req))) (aref req 6))) (silent (progn (or (and (memq (type-of req) cl-struct-ghub--req-tags) t) (signal 'wrong-type-argument (list 'ghub--req req))) (aref req 3)))) (if (or (progn (or (and (memq (type-of req) cl-struct-ghub--req-tags) t) (signal 'wrong-type-argument (list 'ghub--req req))) (aref req 10)) (progn (or (and (memq (type-of req) cl-struct-ghub--req-tags) t) (signal 'wrong-type-argument (list 'ghub--req req))) (aref req 11))) (url-retrieve url handler (list req) silent) (url-do-setup) (save-current-buffer (set-buffer (let ((url-registered-auth-schemes '(("basic" ghub--basic-auth-errorback . 10)))) (url-retrieve-synchronously url silent))) (funcall handler (car url-callback-arguments) req))))
  ghub--retrieve("{\"query\":\"query (\\n$name:String!,\\n$owner:String!) {\\nrepository (\\nname:$name,\\nowner:$owner) {\\nname\\nid\\ncreatedAt\\nupdatedAt\\nnameWithOwner\\nparent {\\nnameWithOwner\\n}\\ndescription\\nhomepageUrl\\ndefaultBranchRef {\\nname\\n}\\nisArchived\\nisFork\\nisLocked\\nisMirror\\nisPrivate\\nhasIssuesEnabled\\nhasWikiEnabled\\nlicenseInfo {\\nname\\n}\\nstargazers {\\ntotalCount\\n}\\nwatchers {\\ntotalCount\\n}\\nassignableUsers (\\nfirst:100) {\\npageInfo {\\nendCursor\\nhasNextPage\\n}\\nedges {\\nnode {\\nid\\nlogin\\nname\\n}\\n}\\n}\\nissues (\\norderBy:{field:UPDATED_AT,direction:DESC},\\nfirst:100) {\\npageInfo {\\nendCursor\\nhasNextPage\\n}\\nedges {\\nnode {\\nnumber\\nstate\\nauthor {\\nlogin\\n}\\ntitle\\ncreatedAt\\nupdatedAt\\nclosedAt\\nlocked\\nmilestone {\\nid\\n}\\nbody\\nassignees (\\nfirst:100) {\\npageInfo {\\nendCursor\\nhasNextPage\\n}\\nedges {\\nnode {\\nid\\n}\\n}\\n}\\ncomments (\\nfirst:100) {\\npageInfo {\\nendCursor\\nhasNextPage\\n}\\nedges {\\nnode {\\ndatabaseId\\nauthor {\\nlogin\\n}\\ncreatedAt\\nupdatedAt\\nbody\\n}\\n}\\n}\\nlabels (\\nfirst:100) {\\npageInfo {\\nendCursor\\nhasNextPage\\n}\\nedges {\\nnode {\\nid\\n}\\n}\\n}\\n}\\n}\\n}\\nlabels (\\nfirst:100) {\\npageInfo {\\nendCursor\\nhasNextPage\\n}\\nedges {\\nnode {\\nid\\nname\\ncolor\\ndescription\\n}\\n}\\n}\\npullRequests (\\norderBy:{field:UPDATED_AT,direction:DESC},\\nfirst:100) {\\npageInfo {\\nendCursor\\nhasNextPage\\n}\\nedges {\\nnode {\\nnumber\\nstate\\nauthor {\\nlogin\\n}\\ntitle\\ncreatedAt\\nupdatedAt\\nclosedAt\\nmergedAt\\nlocked\\nmaintainerCanModify\\nisCrossRepository\\nmilestone {\\nid\\n}\\nbody\\nbaseRef {\\nname\\nrepository {\\nnameWithOwner\\n}\\n}\\nheadRef {\\nname\\nrepository {\\nowner {\\nlogin\\n}\\nnameWithOwner\\n}\\n}\\nassignees (\\nfirst:100) {\\npageInfo {\\nendCursor\\nhasNextPage\\n}\\nedges {\\nnode {\\nid\\n}\\n}\\n}\\ncomments (\\nfirst:100) {\\npageInfo {\\nendCursor\\nhasNextPage\\n}\\nedges {\\nnode {\\ndatabaseId\\nauthor {\\nlogin\\n}\\ncreatedAt\\nupdatedAt\\nbody\\n}\\n}\\n}\\nlabels (\\nfirst:100) {\\npageInfo {\\nendCursor\\nhasNextPage\\n}\\nedges {\\nnode {\\nid\\n}\\n}\\n}\\n}\\n}\\n}\\n}\\n}\",\"variables\":{\"owner\":\"aspiers\",\"name\":\"Fill-Column-Indicator\"}}" #s(ghub--graphql-req :url #s(url :type "https" :user nil :password nil :host "api.github.com" :portspec nil :filename "/graphql" :target nil :attributes nil :fullness t :silent nil :use-cookies t :asynchronous t) :forge nil :silent nil :method "POST" :headers (closure ((forge) (username . "aspiers") (auth . forge) (host . "api.github.com") (headers ("Content-Type" . "application/json")) cl-struct-ghub--req-tags url-http-response-status url-http-extra-headers url-http-end-of-headers url-callback-arguments t) nil (if (eq auth 'basic) (cons (cons "Authorization" (ghub--basic-auth host username)) headers) (cons (ghub--auth host auth username forge) headers))) :handler ghub--graphql-handle-response :unpaginate nil :noerror nil :reader nil :callback #f(compiled-function (data) #<bytecode 0x16fb05d>) :errorback nil :value nil :extra nil :query (query (repository [(owner $owner String!) (name $name String!)] name id createdAt updatedAt nameWithOwner (parent nameWithOwner) description homepageUrl (defaultBranchRef name) isArchived isFork isLocked isMirror isPrivate hasIssuesEnabled hasWikiEnabled (licenseInfo name) (stargazers totalCount) (watchers totalCount) (assignableUsers [(:edges t)] id login name) (issues [(:edges t) (:singular issue number) (orderBy ((field . UPDATED_AT) (direction . DESC)))] number state (author login) title createdAt updatedAt closedAt locked (milestone id) body (assignees [(:edges t)] id) (comments [(:edges t)] databaseId (author login) createdAt updatedAt body) (labels [(:edges t)] id)) (labels [(:edges t) (:singular label id)] id name color description) (pullRequests [(:edges t) (:singular pullRequest number) (orderBy ((field . UPDATED_AT) (direction . DESC)))] number state (author login) title createdAt updatedAt closedAt mergedAt locked maintainerCanModify isCrossRepository (milestone id) body (baseRef name (repository nameWithOwner)) (headRef name (repository (owner login) nameWithOwner)) (assignees [(:edges t)] id) (comments [(:edges t)] databaseId (author login) createdAt updatedAt body) (labels [(:edges t)] id)))) :variables ((owner . "aspiers") (name . "Fill-Column-Indicator")) :until ((issues-until) (pullRequests-until)) :buffer #<buffer magit: fill-column-indicator> :pages 1))
  ghub--graphql-retrieve(#s(ghub--graphql-req :url #s(url :type "https" :user nil :password nil :host "api.github.com" :portspec nil :filename "/graphql" :target nil :attributes nil :fullness t :silent nil :use-cookies t :asynchronous t) :forge nil :silent nil :method "POST" :headers (closure ((forge) (username . "aspiers") (auth . forge) (host . "api.github.com") (headers ("Content-Type" . "application/json")) cl-struct-ghub--req-tags url-http-response-status url-http-extra-headers url-http-end-of-headers url-callback-arguments t) nil (if (eq auth 'basic) (cons (cons "Authorization" (ghub--basic-auth host username)) headers) (cons (ghub--auth host auth username forge) headers))) :handler ghub--graphql-handle-response :unpaginate nil :noerror nil :reader nil :callback #f(compiled-function (data) #<bytecode 0x16fb05d>) :errorback nil :value nil :extra nil :query (query (repository [(owner $owner String!) (name $name String!)] name id createdAt updatedAt nameWithOwner (parent nameWithOwner) description homepageUrl (defaultBranchRef name) isArchived isFork isLocked isMirror isPrivate hasIssuesEnabled hasWikiEnabled (licenseInfo name) (stargazers totalCount) (watchers totalCount) (assignableUsers [(:edges t)] id login name) (issues [(:edges t) (:singular issue number) (orderBy ((field . UPDATED_AT) (direction . DESC)))] number state (author login) title createdAt updatedAt closedAt locked (milestone id) body (assignees [(:edges t)] id) (comments [(:edges t)] databaseId (author login) createdAt updatedAt body) (labels [(:edges t)] id)) (labels [(:edges t) (:singular label id)] id name color description) (pullRequests [(:edges t) (:singular pullRequest number) (orderBy ((field . UPDATED_AT) (direction . DESC)))] number state (author login) title createdAt updatedAt closedAt mergedAt locked maintainerCanModify isCrossRepository (milestone id) body (baseRef name (repository nameWithOwner)) (headRef name (repository (owner login) nameWithOwner)) (assignees [(:edges t)] id) (comments [(:edges t)] databaseId (author login) createdAt updatedAt body) (labels [(:edges t)] id)))) :variables ((owner . "aspiers") (name . "Fill-Column-Indicator")) :until ((issues-until) (pullRequests-until)) :buffer #<buffer magit: fill-column-indicator> :pages 1))
  ghub--graphql-vacuum((query (repository [(owner $owner String!) (name $name String!)] name id createdAt updatedAt nameWithOwner (parent nameWithOwner) description homepageUrl (defaultBranchRef name) isArchived isFork isLocked isMirror isPrivate hasIssuesEnabled hasWikiEnabled (licenseInfo name) (stargazers totalCount) (watchers totalCount) (assignableUsers [(:edges t)] id login name) (issues [(:edges t) (:singular issue number) (orderBy ((field . UPDATED_AT) (direction . DESC)))] number state (author login) title createdAt updatedAt closedAt locked (milestone id) body (assignees [(:edges t)] id) (comments [(:edges t)] databaseId (author login) createdAt updatedAt body) (labels [(:edges t)] id)) (labels [(:edges t) (:singular label id)] id name color description) (pullRequests [(:edges t) (:singular pullRequest number) (orderBy ((field . UPDATED_AT) (direction . DESC)))] number state (author login) title createdAt updatedAt closedAt mergedAt locked maintainerCanModify isCrossRepository (milestone id) body (baseRef name (repository nameWithOwner)) (headRef name (repository (owner login) nameWithOwner)) (assignees [(:edges t)] id) (comments [(:edges t)] databaseId (author login) createdAt updatedAt body) (labels [(:edges t)] id)))) ((owner . "aspiers") (name . "Fill-Column-Indicator")) #f(compiled-function (data) #<bytecode 0x16ff57d>) ((issues-until) (pullRequests-until)) :narrow (repository) :username nil :auth forge :host nil :forge nil)
  ghub-fetch-repository("aspiers" "Fill-Column-Indicator" #f(compiled-function (data) #<bytecode 0x16ff57d>) ((issues-until) (pullRequests-until)) :auth forge)
  #f(compiled-function (repo) #<bytecode 0x162a439>)(#<forge-github-repository forge-github-repository>)
  apply(#f(compiled-function (repo) #<bytecode 0x162a439>) #<forge-github-repository forge-github-repository> nil)
  forge--pull(#<forge-github-repository forge-github-repository>)
  forge-pull()
  funcall-interactively(forge-pull)
  #<subr call-interactively>(forge-pull)
  apply(#<subr call-interactively> forge-pull nil)
  call-interactively@ido-cr+-record-current-command(#<subr call-interactively> forge-pull)
  apply(call-interactively@ido-cr+-record-current-command #<subr call-interactively> forge-pull)
  call-interactively(forge-pull)
  magit-invoke-popup-action(121)
  funcall-interactively(magit-invoke-popup-action 121)
  #<subr call-interactively>(magit-invoke-popup-action nil nil)
  apply(#<subr call-interactively> magit-invoke-popup-action (nil nil))
  call-interactively@ido-cr+-record-current-command(#<subr call-interactively> magit-invoke-popup-action nil nil)
  apply(call-interactively@ido-cr+-record-current-command #<subr call-interactively> (magit-invoke-popup-action nil nil))
  call-interactively(magit-invoke-popup-action nil nil)
  command-execute(magit-invoke-popup-action)
aspiers commented 5 years ago

Hmm, with hindsight I should have submitted this to ghub. I'll resubmit. Sorry for the noise.

aspiers commented 5 years ago

Resubmitted as https://github.com/magit/ghub/pull/78