@@ -176,17 +176,14 @@ func (gh *GitHub) validateAppCredentials(ctx context.Context) (annotations.Annot
176176}
177177
178178// newGitHubClient returns a new GitHub API client authenticated with an access token via oauth2.
179- func newGitHubClient (ctx context.Context , instanceURL string , accessToken string ) (* github.Client , error ) {
179+ func newGitHubClient (ctx context.Context , instanceURL string , ts oauth2. TokenSource ) (* github.Client , error ) {
180180 httpClient , err := uhttp .NewClient (ctx , uhttp .WithLogger (true , ctxzap .Extract (ctx )))
181181 if err != nil {
182182 return nil , err
183183 }
184184
185185 ctx = context .WithValue (ctx , oauth2 .HTTPClient , httpClient )
186186
187- ts := oauth2 .StaticTokenSource (
188- & oauth2.Token {AccessToken : accessToken },
189- )
190187 tc := oauth2 .NewClient (ctx , ts )
191188 gc := github .NewClient (tc )
192189
@@ -205,13 +202,24 @@ func New(ctx context.Context, ghc *cfg.Github, appKey string) (*GitHub, error) {
205202 return nil , err
206203 }
207204
208- var appClient * github.Client
205+ var (
206+ client * github.Client
207+ appClient * github.Client
208+ ts = oauth2 .StaticTokenSource (
209+ & oauth2.Token {AccessToken : patToken },
210+ )
211+ )
209212 if jwttoken != "" {
210213 if len (ghc .Orgs ) != 1 {
211214 return nil , fmt .Errorf ("github-connector: only one org should be specified" )
212215 }
213216
214- appClient , err = newGitHubClient (ctx , ghc .InstanceUrl , jwttoken )
217+ appClient , err = newGitHubClient (ctx ,
218+ ghc .InstanceUrl ,
219+ oauth2 .StaticTokenSource (
220+ & oauth2.Token {AccessToken : jwttoken },
221+ ),
222+ )
215223 if err != nil {
216224 return nil , err
217225 }
@@ -224,14 +232,26 @@ func New(ctx context.Context, ghc *cfg.Github, appKey string) (*GitHub, error) {
224232 if err != nil {
225233 return nil , err
226234 }
227- patToken = token
235+
236+ ts = oauth2 .ReuseTokenSource (
237+ & oauth2.Token {
238+ AccessToken : token .GetToken (),
239+ Expiry : token .GetExpiresAt ().Time ,
240+ },
241+ & appTokenRefresher {
242+ ctx : ctx ,
243+ instanceURL : ghc .InstanceUrl ,
244+ installationID : installation .GetID (),
245+ jwttoken : jwttoken ,
246+ },
247+ )
228248 }
229249
230- client , err : = newGitHubClient (ctx , ghc .InstanceUrl , patToken )
250+ client , err = newGitHubClient (ctx , ghc .InstanceUrl , ts )
231251 if err != nil {
232252 return nil , err
233253 }
234- graphqlClient , err := newGitHubGraphqlClient (ctx , ghc .InstanceUrl , patToken )
254+ graphqlClient , err := newGitHubGraphqlClient (ctx , ghc .InstanceUrl , ts )
235255 if err != nil {
236256 return nil , err
237257 }
@@ -248,17 +268,14 @@ func New(ctx context.Context, ghc *cfg.Github, appKey string) (*GitHub, error) {
248268 return gh , nil
249269}
250270
251- func newGitHubGraphqlClient (ctx context.Context , instanceURL string , accessToken string ) (* githubv4.Client , error ) {
271+ func newGitHubGraphqlClient (ctx context.Context , instanceURL string , ts oauth2. TokenSource ) (* githubv4.Client , error ) {
252272 httpClient , err := uhttp .NewClient (ctx , uhttp .WithLogger (true , ctxzap .Extract (ctx )))
253273 if err != nil {
254274 return nil , err
255275 }
256276
257277 ctx = context .WithValue (ctx , oauth2 .HTTPClient , httpClient )
258278
259- ts := oauth2 .StaticTokenSource (
260- & oauth2.Token {AccessToken : accessToken },
261- )
262279 tc := oauth2 .NewClient (ctx , ts )
263280
264281 instanceURL = strings .TrimSuffix (instanceURL , "/" )
@@ -331,16 +348,44 @@ func findInstallation(ctx context.Context, c *github.Client, orgName string) (*g
331348 return installation , resp , nil
332349}
333350
334- func getInstallationToken (ctx context.Context , c * github.Client , id int64 ) (string , error ) {
351+ func getInstallationToken (ctx context.Context , c * github.Client , id int64 ) (* github. InstallationToken , error ) {
335352 token , resp , err := c .Apps .CreateInstallationToken (ctx , id , & github.InstallationTokenOptions {})
336353 if err != nil {
337- return "" , err
354+ return nil , err
338355 }
339356
340357 if resp .StatusCode != http .StatusCreated {
341358 body , _ := io .ReadAll (resp .Body )
342- return "" , fmt .Errorf ("GitHub API error: %s" , body )
359+ return nil , fmt .Errorf ("GitHub API error: %s" , body )
343360 }
344361
345- return token .GetToken (), nil
362+ return token , nil
363+ }
364+
365+ type appTokenRefresher struct {
366+ ctx context.Context
367+ jwttoken string
368+ instanceURL string
369+ installationID int64
370+ }
371+
372+ func (r * appTokenRefresher ) Token () (* oauth2.Token , error ) {
373+ appClient , err := newGitHubClient (r .ctx ,
374+ r .instanceURL ,
375+ oauth2 .StaticTokenSource (
376+ & oauth2.Token {AccessToken : r .jwttoken },
377+ ),
378+ )
379+ if err != nil {
380+ return nil , err
381+ }
382+
383+ token , err := getInstallationToken (r .ctx , appClient , r .installationID )
384+ if err != nil {
385+ return nil , err
386+ }
387+ return & oauth2.Token {
388+ AccessToken : token .GetToken (),
389+ Expiry : token .GetExpiresAt ().Time ,
390+ }, nil
346391}
0 commit comments