@@ -3,80 +3,38 @@ package io.github.typesafegithub.workflows.shared.internal
33import com.auth0.jwt.JWT
44import com.auth0.jwt.algorithms.Algorithm
55import io.github.oshai.kotlinlogging.KotlinLogging.logger
6- import io.ktor.client.HttpClient
7- import io.ktor.client.call.body
8- import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
9- import io.ktor.client.plugins.logging.LogLevel.ALL
10- import io.ktor.client.plugins.logging.Logger
11- import io.ktor.client.plugins.logging.Logging
12- import io.ktor.client.request.header
13- import io.ktor.client.request.post
14- import io.ktor.serialization.kotlinx.json.json
15- import kotlinx.serialization.Serializable
16- import kotlinx.serialization.Transient
17- import kotlinx.serialization.json.Json
6+ import org.kohsuke.github.GHAppInstallationToken
7+ import org.kohsuke.github.GitHubBuilder
188import java.security.KeyFactory
199import java.security.interfaces.RSAPrivateKey
2010import java.security.spec.PKCS8EncodedKeySpec
2111import java.time.Instant
22- import java.time.ZonedDateTime
2312import java.util.Base64
2413
2514private val logger = logger { }
2615
16+ private var cachedAccessToken: GHAppInstallationToken ? = null
17+
2718/* *
2819 * Returns an installation access token for the GitHub app, usable with API call to GitHub.
2920 * If `null` is returned, it means that the environment wasn't configured to generate the token.
3021 */
3122@Suppress(" ReturnCount" )
32- suspend fun getInstallationAccessToken (): String? {
23+ fun getInstallationAccessToken (): String? {
3324 if (cachedAccessToken?.isExpired() == false ) return cachedAccessToken!! .token
3425 val jwtToken = generateJWTToken() ? : return null
35- val appInstallationId = System .getenv(" APP_INSTALLATION_ID" ) ? : return null
36- cachedAccessToken =
37- httpClient
38- .post(" https://api.github.com/app/installations/$appInstallationId /access_tokens" ) {
39- header(" Accept" , " application/vnd.github+json" )
40- header(" Authorization" , " Bearer $jwtToken " )
41- header(" X-GitHub-Api-Version" , " 2022-11-28" )
42- }.body()
43- return cachedAccessToken!! .token
44- }
45-
46- private var cachedAccessToken: Token ? = null
26+ val installationId = System .getenv(" APP_INSTALLATION_ID" ) ? : return null
4727
48- @Suppress(" ConstructorParameterNaming" )
49- @Serializable
50- private data class Token (
51- val token : String ,
52- val expires_at : String ,
53- val permissions : Map <String , String >,
54- val repository_selection : String ,
55- ) {
56- @Transient
57- private val expiresAtDateTime = ZonedDateTime .parse(expires_at)
28+ val gitHubApp = GitHubBuilder ().withJwtToken(jwtToken).build().app
5829
59- fun isExpired () = ZonedDateTime .now().isAfter(expiresAtDateTime)
30+ cachedAccessToken =
31+ gitHubApp.getInstallationById(installationId.toLong()).createToken().create().also {
32+ logger.info { " Fetched new GitHub App installation access token for repository ${it.repositorySelection} " }
33+ }
34+ return cachedAccessToken!! .token
6035}
6136
62- private val httpClient =
63- HttpClient {
64- val klogger = logger
65- install(Logging ) {
66- logger =
67- object : Logger {
68- override fun log (message : String ) {
69- klogger.trace { message }
70- }
71- }
72- level = ALL
73- }
74- install(ContentNegotiation ) {
75- json(
76- Json { ignoreUnknownKeys = false },
77- )
78- }
79- }
37+ private fun GHAppInstallationToken.isExpired () = Instant .now() >= expiresAt.toInstant()
8038
8139@Suppress(" ReturnCount" , " MagicNumber" )
8240private fun generateJWTToken (): String? {
0 commit comments