import { Octokit } from '@octokit/rest' ; import { ProbotOctokit } from 'probot' ; import fs from 'fs' ; import { request } from 'gaxios' ; import { sign } from 'jsonwebtoken' ; const { readFile } = fs . promises ; export type OctokitType = InstanceType < typeof Octokit > | InstanceType < typeof ProbotOctokit > ; export interface OctokitParams { 'pem-path' ? : string ; privateKey ? : string ; 'app-id' : number ; installation : number ; } interface AuthArgs { privateKey : string ; appId : number ; installation : number ; } interface Token { token : string ; expires_at : string ; permissions : object ; repository_selection : string ; } /**
 * Creates an authenticated token for octokit.
 */ export async function octokitTokenFrom ( argv ) { let privateKey = '' ; if ( argv [ 'pem-path' ] ) { privateKey = await readFile ( argv [ 'pem-path' ] , 'utf8' ) ; } else if ( argv . privateKey ) { privateKey = argv . privateKey ; } const token = await getGitHubShortLivedAccessToken ( privateKey , argv [ 'app-id' ] , argv . installation ) ; return token . token ; } /**
 * Creates an authenticated instance of octokit.
 */ export async function octokitFrom ( argv ) { const token = await octokitTokenFrom ( argv ) ; return await getAuthenticatedOctokit ( token , false ) ; } /**
 * Interface lets us easily replace in tests.
 */ export interface OctokitFactory { getGitHubShortLivedAccessToken ( ) : Promise < string > ; getShortLivedOctokit ( token ? ) : Promise < OctokitType > ; } /**
 * Creates an octokit factory from the common params.
 * The factory will return a new octokit with a new token every 5 minutes.
 */ export function octokitFactoryFrom ( params ) { let lastOctokitTimestamp = 0 ; let lastOctokit = null ; return { getGitHubShortLivedAccessToken ( ) { return octokitTokenFrom ( params ) ; } , async getShortLivedOctokit ( token ? ) { if ( token ) { return getAuthenticatedOctokit ( token , false ) ; } const now = new Date ( ) . getTime ( ) ; const elapsedMilliseconds = now - lastOctokitTimestamp ; if ( ! lastOctokit || elapsedMilliseconds > 300000 ) { lastOctokitTimestamp = now ; const atoken = await octokitTokenFrom ( params ) ; lastOctokit = await getAuthenticatedOctokit ( atoken , false ) ; } return lastOctokit ; } , } ; } /**
 * Creates an octokit factory a short lived token.
 */ export function octokitFactoryFromToken ( token ) { return { getGitHubShortLivedAccessToken ( ) { return Promise . resolve ( token ) ; } , async getShortLivedOctokit ( atoken ? ) { return await getAuthenticatedOctokit ( atoken ?? token , false ) ; } , } ; } export async function getGitHubShortLivedAccessToken ( privateKey , appId , installation ) { const payload = { iat : parseInt ( '' + Date . now ( ) / 1000 ) , exp : parseInt ( '' + Date . now ( ) / 1000 + 10 * 60 ) , iss : appId , } ; const jwt = sign ( payload , privateKey , { algorithm : 'RS256' } ) ; const resp = await request < Token > ( { url : getAccessTokenURL ( installation ) , method : 'POST' , headers : { Authorization : `Bearer ${ jwt }` , Accept : 'application/vnd.github.v3+json' , } , } ) ; if ( resp . status !== 201 ) { throw Error ( `unexpected response http = ${ resp . status }` ) ; } else { return resp . data ; } } export function getAccessTokenURL ( installation ) { return `https://api.github.com/app/installations/${ installation }/access_tokens` ; } let cachedOctokit ; export async function getAuthenticatedOctokit ( auth , cache = true ) { if ( cache && cachedOctokit ) return cachedOctokit ; let tokenString ; if ( auth instanceof Object ) { const token = await getGitHubShortLivedAccessToken ( auth . privateKey , auth . appId , auth . installation ) ; tokenString = token . token ; } else { tokenString = auth ; } const octokit = new Octokit ( { auth : tokenString , } ) ; if ( cache ) cachedOctokit = octokit ; return octokit ; }