66import os
77import pathlib
88import subprocess
9+ import sys
910import time
1011
1112import requests
1213from jwcrypto import jwk , jwt
1314
1415SECRET_NAME = "pipelines-as-code-secret"
1516NAMESPACE = "pipelines-as-code"
16- EXPIRE_MINUTES_AS_SECONDS = (
17- int (os .environ .get ("GITHUBAPP_TOKEN_EXPIRATION_MINUTES" , 10 )) * 60
18- )
17+ JWT_EXPIRE_MINUTES_AS_SECONDS = 10 * 60
18+
1919# TODO support github enteprise
2020GITHUB_API_URL = "https://api.github.com"
2121
22+ HELP_TEXT = """
23+ ghapp_token.py let you generate a token for a github app out of an application
24+ id and a private key
25+
26+ The way it works is that first we generate a jwt token out of the private key
27+ then use it to get a installation token. (see documentation here:
28+ https://is.gd/DsPw4z)
29+
30+ It will automatically detect the secret from the cluster in the
31+ pipelines-as-code namespace (unless you specify the -n flag for another
32+ namespace) and get the value from there.
33+
34+ If you are generating a token on GHE you probably want to pass the flag -a for
35+ another api endpoint.
36+
37+ Alternatively you can use a pass (https://passwordstore.org) profile to get
38+ the keys with the -P flag.
39+
40+ You can use a cache file to avoid generating a new token each time and reuse
41+ """
42+
2243
2344# pylint: disable=too-few-public-methods
2445class GitHub :
2546 token = None
2647
27- def __init__ (
28- self , private_key , app_id , expiration_time , github_api_url , installation_id = None
29- ):
48+ def __init__ (self , private_key , app_id , expiration_time , github_api_url ):
3049 if not isinstance (private_key , bytes ):
3150 private_key = private_key .encode ()
3251 self ._private_key = private_key
3352 self .app_id = app_id
3453 self .expiration_time = expiration_time
3554 self .github_api_url = github_api_url
3655 self .jwt_token = self ._get_jwt_token ()
37- self .token = self ._get_token (installation_id )
3856
3957 @classmethod
4058 def _load_private_key (cls , pem_key_bytes ):
@@ -56,7 +74,7 @@ def _get_jwt_token(self):
5674 token .make_signed_token (key )
5775 return token .serialize ()
5876
59- def _get_token (self , installation_id ):
77+ def get_token (self , installation_id ):
6078 req = self ._request (
6179 "POST" ,
6280 f"/app/installations/{ installation_id } /access_tokens" ,
@@ -127,7 +145,7 @@ def main(args):
127145 if datetime .datetime .fromtimestamp (
128146 mtime
129147 ) < datetime .datetime .now () - datetime .timedelta (
130- seconds = args .token_expiration_time
148+ seconds = args .jwt_token_expiration_time
131149 ):
132150 os .remove (args .cache_file )
133151 else :
@@ -140,14 +158,23 @@ def main(args):
140158 github_app = GitHub (
141159 private_key ,
142160 application_id ,
143- expiration_time = args .token_expiration_time ,
161+ expiration_time = args .jwt_token_expiration_time ,
144162 github_api_url = args .api_url ,
145- installation_id = args .installation_id ,
146163 )
164+
147165 if args .jwt_token :
148166 print (github_app .jwt_token )
149- else :
150- print (github_app .token )
167+ sys .exit (0 )
168+
169+ if not args .installation_id :
170+ print (
171+ "You need to provide an installation id or have the -j flag to only generate jwt token"
172+ )
173+ sys .exit (1 )
174+
175+ github_app .token = github_app .get_token (args .installation_id )
176+ print (github_app .token )
177+
151178 if args .cache_file :
152179 print (
153180 pathlib .Path (args .cache_file ).write_text (
@@ -157,15 +184,20 @@ def main(args):
157184
158185
159186def parse_args ():
160- parser = argparse .ArgumentParser (description = "Generate a user token" )
187+ parser = argparse .ArgumentParser (
188+ description = "Generate a installation token from github application pac secret"
189+ )
161190 parser .add_argument (
162- "--token-expiration-time" ,
191+ "--jwt- token-expiration-time" ,
163192 type = int ,
164193 help = "Token expiration time (seconds)" ,
165- default = EXPIRE_MINUTES_AS_SECONDS ,
194+ default = JWT_EXPIRE_MINUTES_AS_SECONDS ,
166195 )
167196 parser .add_argument (
168- "--installation-id" , "-i" , type = int , help = "Installation_ID" , required = True
197+ "--installation-id" ,
198+ "-i" ,
199+ type = int ,
200+ help = "Installation_ID" ,
169201 )
170202
171203 parser .add_argument (
@@ -191,11 +223,18 @@ def parse_args():
191223 "--cache-file" ,
192224 help = (
193225 f"Cache file will only regenerate after the expiration time, "
194- f"default: { EXPIRE_MINUTES_AS_SECONDS / 60 } minutes"
226+ f"default: { JWT_EXPIRE_MINUTES_AS_SECONDS / 60 } minutes"
195227 ),
196228 default = os .environ .get ("GITHUBAPP_RESULT_PATH" ),
197229 )
198- return parser .parse_args ()
230+ args = parser .parse_args ()
231+ if not args .installation_id or not args .jwt_token :
232+ parser .print_help ()
233+ print ("Description:" , end = "" )
234+ print ("\n " .join ([f" { x } " for x in HELP_TEXT .splitlines ()]))
235+ sys .exit (1 )
236+
237+ return args
199238
200239
201240if __name__ == "__main__" :
0 commit comments