3636 VERSION = "unknown"
3737
3838FNULL = open (os .devnull , "w" )
39+ FILE_URI_PREFIX = "file://"
3940logger = logging .getLogger (__name__ )
4041
4142
@@ -126,9 +127,15 @@ def parse_args(args=None):
126127 parser .add_argument (
127128 "-t" ,
128129 "--token" ,
129- dest = "token " ,
130+ dest = "token_classic " ,
130131 help = "personal access, OAuth, or JSON Web token, or path to token (file://...)" ,
131132 ) # noqa
133+ parser .add_argument (
134+ "-f" ,
135+ "--token-fine" ,
136+ dest = "token_fine" ,
137+ help = "fine-grained personal access token (github_pat_....), or path to token (file://...)" ,
138+ ) # noqa
132139 parser .add_argument (
133140 "--as-app" ,
134141 action = "store_true" ,
@@ -431,18 +438,27 @@ def get_auth(args, encode=True, for_git_cli=False):
431438 raise Exception (
432439 "You must specify both name and account fields for osx keychain password items"
433440 )
434- elif args .token :
435- _path_specifier = "file://"
436- if args .token .startswith (_path_specifier ):
437- path_specifier_len = len (_path_specifier )
438- args .token = open (args .token [path_specifier_len :], "rt" ).readline ().strip ()
441+ elif args .token_fine :
442+ if args .token_fine .startswith (FILE_URI_PREFIX ):
443+ args .token_fine = read_file_contents (args .token_fine )
444+
445+ if args .token_fine .startswith ("github_pat_" ):
446+ auth = args .token_fine
447+ else :
448+ raise Exception (
449+ "Fine-grained token supplied does not look like a GitHub PAT"
450+ )
451+ elif args .token_classic :
452+ if args .token_classic .startswith (FILE_URI_PREFIX ):
453+ args .token_classic = read_file_contents (args .token_classic )
454+
439455 if not args .as_app :
440- auth = args .token + ":" + "x-oauth-basic"
456+ auth = args .token_classic + ":" + "x-oauth-basic"
441457 else :
442458 if not for_git_cli :
443- auth = args .token
459+ auth = args .token_classic
444460 else :
445- auth = "x-access-token:" + args .token
461+ auth = "x-access-token:" + args .token_classic
446462 elif args .username :
447463 if not args .password :
448464 args .password = getpass .getpass ()
@@ -457,7 +473,7 @@ def get_auth(args, encode=True, for_git_cli=False):
457473 if not auth :
458474 return None
459475
460- if not encode :
476+ if not encode or args . token_fine is not None :
461477 return auth
462478
463479 return base64 .b64encode (auth .encode ("ascii" ))
@@ -481,6 +497,10 @@ def get_github_host(args):
481497 return host
482498
483499
500+ def read_file_contents (file_uri ):
501+ return open (file_uri [len (FILE_URI_PREFIX ) :], "rt" ).readline ().strip ()
502+
503+
484504def get_github_repo_url (args , repository ):
485505 if repository .get ("is_gist" ):
486506 if args .prefer_ssh :
@@ -503,7 +523,7 @@ def get_github_repo_url(args, repository):
503523 auth = get_auth (args , encode = False , for_git_cli = True )
504524 if auth :
505525 repo_url = "https://{0}@{1}/{2}/{3}.git" .format (
506- auth ,
526+ auth if args . token_fine is None else "oauth2:" + auth ,
507527 get_github_host (args ),
508528 repository ["owner" ]["login" ],
509529 repository ["name" ],
@@ -523,7 +543,13 @@ def retrieve_data_gen(args, template, query_args=None, single_request=False):
523543 while True :
524544 page = page + 1
525545 request = _construct_request (
526- per_page , page , query_args , template , auth , as_app = args .as_app
546+ per_page ,
547+ page ,
548+ query_args ,
549+ template ,
550+ auth ,
551+ as_app = args .as_app ,
552+ fine = True if args .token_fine is not None else False ,
527553 ) # noqa
528554 r , errors = _get_response (request , auth , template )
529555
@@ -559,7 +585,13 @@ def retrieve_data_gen(args, template, query_args=None, single_request=False):
559585 retries += 1
560586 time .sleep (5 )
561587 request = _construct_request (
562- per_page , page , query_args , template , auth , as_app = args .as_app
588+ per_page ,
589+ page ,
590+ query_args ,
591+ template ,
592+ auth ,
593+ as_app = args .as_app ,
594+ fine = True if args .token_fine is not None else False ,
563595 ) # noqa
564596 r , errors = _get_response (request , auth , template )
565597
@@ -643,17 +675,23 @@ def _get_response(request, auth, template):
643675 return r , errors
644676
645677
646- def _construct_request (per_page , page , query_args , template , auth , as_app = None ):
678+ def _construct_request (
679+ per_page , page , query_args , template , auth , as_app = None , fine = False
680+ ):
647681 querystring = urlencode (
648682 dict (
649- list ({"per_page" : per_page , "page" : page }.items ()) + list (query_args .items ())
683+ list ({"per_page" : per_page , "page" : page }.items ())
684+ + list (query_args .items ())
650685 )
651686 )
652687
653688 request = Request (template + "?" + querystring )
654689 if auth is not None :
655690 if not as_app :
656- request .add_header ("Authorization" , "Basic " .encode ("ascii" ) + auth )
691+ if fine :
692+ request .add_header ("Authorization" , "token " + auth )
693+ else :
694+ request .add_header ("Authorization" , "Basic " .encode ("ascii" ) + auth )
657695 else :
658696 auth = auth .encode ("ascii" )
659697 request .add_header ("Authorization" , "token " .encode ("ascii" ) + auth )
0 commit comments