@@ -150,9 +150,13 @@ def parse_args(args=None):
150150 'If a username is given but not a password, the '
151151 'password will be prompted for.' )
152152 parser .add_argument ('-t' ,
153- '--token' ,
154- dest = 'token ' ,
153+ '--token-classic ' ,
154+ dest = 'token_classic ' ,
155155 help = 'personal access, OAuth, or JSON Web token, or path to token (file://...)' ) # noqa
156+ parser .add_argument ('-f' ,
157+ '--token-fine' ,
158+ dest = 'token_fine' ,
159+ help = 'fine-grained personal access token (github_pat_....)' ) # noqa
156160 parser .add_argument ('--as-app' ,
157161 action = 'store_true' ,
158162 dest = 'as_app' ,
@@ -357,18 +361,23 @@ def get_auth(args, encode=True, for_git_cli=False):
357361 raise Exception ('No password item matching the provided name and account could be found in the osx keychain.' )
358362 elif args .osx_keychain_item_account :
359363 raise Exception ('You must specify both name and account fields for osx keychain password items' )
360- elif args .token :
364+ elif args .token_fine :
365+ if args .token_fine .startswith ("github_pat_" ):
366+ auth = args .token_fine
367+ else :
368+ raise Exception ("Fine-grained token supplied does not look like a GitHub PAT" )
369+ elif args .token_classic :
361370 _path_specifier = 'file://'
362- if args .token .startswith (_path_specifier ):
363- args .token = open (args .token [len (_path_specifier ):],
364- 'rt' ).readline ().strip ()
371+ if args .token_classic .startswith (_path_specifier ):
372+ args .token_classic = open (args .token_classic [len (_path_specifier ):],
373+ 'rt' ).readline ().strip ()
365374 if not args .as_app :
366- auth = args .token + ':' + 'x-oauth-basic'
375+ auth = args .token_classic + ':' + 'x-oauth-basic'
367376 else :
368377 if not for_git_cli :
369- auth = args .token
378+ auth = args .token_classic
370379 else :
371- auth = 'x-access-token:' + args .token
380+ auth = 'x-access-token:' + args .token_classic
372381 elif args .username :
373382 if not args .password :
374383 args .password = getpass .getpass ()
@@ -383,7 +392,7 @@ def get_auth(args, encode=True, for_git_cli=False):
383392 if not auth :
384393 return None
385394
386- if not encode :
395+ if not encode or args . token_fine is not None :
387396 return auth
388397
389398 return base64 .b64encode (auth .encode ('ascii' ))
@@ -421,12 +430,19 @@ def get_github_repo_url(args, repository):
421430 return repository ['ssh_url' ]
422431
423432 auth = get_auth (args , encode = False , for_git_cli = True )
424- if auth :
425- repo_url = 'https://{0}@{1}/{2}/{3}.git' .format (
426- auth ,
427- get_github_host (args ),
428- repository ['owner' ]['login' ],
429- repository ['name' ])
433+ if auth :
434+ if args .token_fine is None :
435+ repo_url = 'https://{0}@{1}/{2}/{3}.git' .format (
436+ auth ,
437+ get_github_host (args ),
438+ repository ['owner' ]['login' ],
439+ repository ['name' ])
440+ else :
441+ repo_url = 'https://{0}@{1}/{2}/{3}.git' .format (
442+ "oauth2:" + auth ,
443+ get_github_host (args ),
444+ repository ['owner' ]['login' ],
445+ repository ['name' ])
430446 else :
431447 repo_url = repository ['clone_url' ]
432448
@@ -441,7 +457,7 @@ def retrieve_data_gen(args, template, query_args=None, single_request=False):
441457
442458 while True :
443459 page = page + 1
444- request = _construct_request (per_page , page , query_args , template , auth , as_app = args .as_app ) # noqa
460+ request = _construct_request (per_page , page , query_args , template , auth , as_app = args .as_app , fine = True if args . token_fine is not None else False ) # noqa
445461 r , errors = _get_response (request , auth , template )
446462
447463 status_code = int (r .getcode ())
@@ -474,7 +490,7 @@ def retrieve_data_gen(args, template, query_args=None, single_request=False):
474490 log_warning ('API request failed. Retrying in 5 seconds' )
475491 retries += 1
476492 time .sleep (5 )
477- request = _construct_request (per_page , page , query_args , template , auth , as_app = args .as_app ) # noqa
493+ request = _construct_request (per_page , page , query_args , template , auth , as_app = args .as_app , fine = True if args . token_fine is not None else False ) # noqa
478494 r , errors = _get_response (request , auth , template )
479495
480496 status_code = int (r .getcode ())
@@ -557,7 +573,7 @@ def _get_response(request, auth, template):
557573 return r , errors
558574
559575
560- def _construct_request (per_page , page , query_args , template , auth , as_app = None ):
576+ def _construct_request (per_page , page , query_args , template , auth , as_app = None , fine = False ):
561577 querystring = urlencode (dict (list ({
562578 'per_page' : per_page ,
563579 'page' : page
@@ -566,7 +582,10 @@ def _construct_request(per_page, page, query_args, template, auth, as_app=None):
566582 request = Request (template + '?' + querystring )
567583 if auth is not None :
568584 if not as_app :
569- request .add_header ('Authorization' , 'Basic ' .encode ('ascii' ) + auth )
585+ if fine :
586+ request .add_header ('Authorization' , 'token ' + auth )
587+ else :
588+ request .add_header ('Authorization' , 'Basic ' .encode ('ascii' ) + auth )
570589 else :
571590 auth = auth .encode ('ascii' )
572591 request .add_header ('Authorization' , 'token ' .encode ('ascii' ) + auth )
0 commit comments