@@ -22,74 +22,106 @@ import { throttling } from '@octokit/plugin-throttling';
2222import { createChildLogger } from '@aws-github-runner/aws-powertools-util' ;
2323import { getParameter } from '@aws-github-runner/aws-ssm-util' ;
2424import { EndpointDefaults } from '@octokit/types' ;
25+ import {
26+ getClient ,
27+ getAuthObject ,
28+ getAuthConfig ,
29+ createTokenCacheKey ,
30+ createAuthCacheKey ,
31+ createAuthConfigCacheKey
32+ } from './cache' ;
33+ import type { GithubAppConfig } from './types' ;
2534
2635const logger = createChildLogger ( 'gh-auth' ) ;
2736
2837export async function createOctokitClient ( token : string , ghesApiUrl = '' ) : Promise < Octokit > {
29- const CustomOctokit = Octokit . plugin ( throttling ) ;
30- const ocktokitOptions : OctokitOptions = {
31- auth : token ,
32- } ;
33- if ( ghesApiUrl ) {
34- ocktokitOptions . baseUrl = ghesApiUrl ;
35- ocktokitOptions . previews = [ 'antiope' ] ;
36- }
38+ const cacheKey = createTokenCacheKey ( token , ghesApiUrl ) ;
3739
38- return new CustomOctokit ( {
39- ...ocktokitOptions ,
40- userAgent : process . env . USER_AGENT || 'github-aws-runners' ,
41- throttle : {
42- onRateLimit : ( retryAfter : number , options : Required < EndpointDefaults > ) => {
43- logger . warn (
44- `GitHub rate limit: Request quota exhausted for request ${ options . method } ${ options . url } . Requested ` ,
45- ) ;
46- } ,
47- onSecondaryRateLimit : ( retryAfter : number , options : Required < EndpointDefaults > ) => {
48- logger . warn ( `GitHub rate limit: SecondaryRateLimit detected for request ${ options . method } ${ options . url } ` ) ;
40+ return getClient ( cacheKey , async ( ) => {
41+ const CustomOctokit = Octokit . plugin ( throttling ) ;
42+ const ocktokitOptions : OctokitOptions = {
43+ auth : token ,
44+ } ;
45+ if ( ghesApiUrl ) {
46+ ocktokitOptions . baseUrl = ghesApiUrl ;
47+ ocktokitOptions . previews = [ 'antiope' ] ;
48+ }
49+
50+ return new CustomOctokit ( {
51+ ...ocktokitOptions ,
52+ userAgent : process . env . USER_AGENT || 'github-aws-runners' ,
53+ throttle : {
54+ onRateLimit : ( retryAfter : number , options : Required < EndpointDefaults > ) => {
55+ logger . warn (
56+ `GitHub rate limit: Request quota exhausted for request ${ options . method } ${ options . url } . Requested ` ,
57+ ) ;
58+ } ,
59+ onSecondaryRateLimit : ( retryAfter : number , options : Required < EndpointDefaults > ) => {
60+ logger . warn ( `GitHub rate limit: SecondaryRateLimit detected for request ${ options . method } ${ options . url } ` ) ;
61+ } ,
4962 } ,
50- } ,
63+ } ) ;
5164 } ) ;
5265}
5366
5467export async function createGithubAppAuth (
5568 installationId : number | undefined ,
5669 ghesApiUrl = '' ,
5770) : Promise < AppAuthentication > {
58- const auth = await createAuth ( installationId , ghesApiUrl ) ;
59- const appAuthOptions : AppAuthOptions = { type : 'app' } ;
60- return auth ( appAuthOptions ) ;
71+ const cacheKey = createAuthCacheKey ( 'app' , installationId , ghesApiUrl ) ;
72+
73+ return getAuthObject < AppAuthentication > ( cacheKey , async ( ) => {
74+ const auth = await createAuth ( installationId , ghesApiUrl ) ;
75+ const appAuthOptions : AppAuthOptions = { type : 'app' } ;
76+ return auth ( appAuthOptions ) ;
77+ } ) ;
6178}
6279
6380export async function createGithubInstallationAuth (
6481 installationId : number | undefined ,
6582 ghesApiUrl = '' ,
6683) : Promise < InstallationAccessTokenAuthentication > {
67- const auth = await createAuth ( installationId , ghesApiUrl ) ;
68- const installationAuthOptions : InstallationAuthOptions = { type : 'installation' , installationId } ;
69- return auth ( installationAuthOptions ) ;
84+ const cacheKey = createAuthCacheKey ( 'installation' , installationId , ghesApiUrl ) ;
85+
86+ return getAuthObject < InstallationAccessTokenAuthentication > ( cacheKey , async ( ) => {
87+ const auth = await createAuth ( installationId , ghesApiUrl ) ;
88+ const installationAuthOptions : InstallationAuthOptions = { type : 'installation' , installationId } ;
89+ return auth ( installationAuthOptions ) ;
90+ } ) ;
7091}
7192
7293async function createAuth ( installationId : number | undefined , ghesApiUrl : string ) : Promise < AuthInterface > {
73- const appId = parseInt ( await getParameter ( process . env . PARAMETER_GITHUB_APP_ID_NAME ) ) ;
74- let authOptions : StrategyOptions = {
75- appId,
76- privateKey : Buffer . from (
94+ const configCacheKey = createAuthConfigCacheKey ( ghesApiUrl ) ;
95+
96+ const config = await getAuthConfig ( configCacheKey , async ( ) : Promise < GithubAppConfig > => {
97+ const appId = parseInt ( await getParameter ( process . env . PARAMETER_GITHUB_APP_ID_NAME ) ) ;
98+ const privateKey = Buffer . from (
7799 await getParameter ( process . env . PARAMETER_GITHUB_APP_KEY_BASE64_NAME ) ,
78100 'base64' ,
79101 // replace literal \n characters with new lines to allow the key to be stored as a
80102 // single line variable. This logic should match how the GitHub Terraform provider
81103 // processes private keys to retain compatibility between the projects
82104 )
83105 . toString ( )
84- . replace ( '/[\\n]/g' , String . fromCharCode ( 10 ) ) ,
106+ . replace ( '/[\\n]/g' , String . fromCharCode ( 10 ) ) ;
107+
108+ return {
109+ appId,
110+ privateKey,
111+ } ;
112+ } ) ;
113+
114+ let authOptions : StrategyOptions = {
115+ appId : config . appId ,
116+ privateKey : config . privateKey ,
85117 } ;
86118 if ( installationId ) authOptions = { ...authOptions , installationId } ;
87119
88120 logger . debug ( `GHES API URL: ${ ghesApiUrl } ` ) ;
89121 if ( ghesApiUrl ) {
90122 authOptions . request = request . defaults ( {
91123 baseUrl : ghesApiUrl ,
92- } ) ;
124+ } ) as RequestInterface ;
93125 }
94126 return createAppAuth ( authOptions ) ;
95127}
0 commit comments