Skip to content

Commit b575ba5

Browse files
author
Jan Gaebel
committed
Merge branch 'main' of github.com:Liftric/code-artifact-repository-plugin
2 parents 2fbdb00 + 2aa8e93 commit b575ba5

File tree

4 files changed

+190
-81
lines changed

4 files changed

+190
-81
lines changed

README.md

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,48 @@ plugins {
1111

1212
codeArtifactRepository {
1313
region.set(Region.EU_CENTRAL_1)
14+
// use profile credentials provider, otherwise the default credentials chain of aws will be used
1415
profile.set("liftric")
15-
domain.set("liftric-frankfurt")
1616
// Determines how long the generated authentication token is valid in seconds
1717
tokenExpiresIn.set(1_800)
18-
// Set to true if you want to resolve the credentials via the environment,
19-
// e.g. when using the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY env variables
20-
shouldResolveCredentialsByEnvironment.set(System.getenv("CI") != null)
2118
}
2219

2320
dependencyResolutionManagement {
2421
repositories {
25-
codeArtifact("my_repository")
22+
codeArtifact("my_domain", "my_repository")
2623
codeArtifact("my_other_domain", "my_other_repository")
2724
}
2825
}
2926
```
27+
28+
You can also use multiple CodeArtifact endpoints:
29+
```kotlin
30+
plugins {
31+
id("com.liftric.code-artifact-repository-plugin") version "<latest>"
32+
}
33+
34+
codeArtifactRepository {
35+
region.set(Region.EU_CENTRAL_1)
36+
profile.set("liftric")
37+
additional("customer1") {
38+
profile.set("customer1")
39+
// reuses properties of the default extension if not explicitly specified
40+
}
41+
}
42+
43+
dependencyResolutionManagement {
44+
repositories {
45+
// uses the default extension (liftric profile)
46+
codeArtifact(domain = "my_domain", repository = "my_repository")
47+
// uses the customer1 extension (customer1 profile)
48+
codeArtifact(additionalName = "customer1", domain = "my_other_domain", repository = "my_other_repository")
49+
}
50+
}
51+
```
52+
53+
You can also just get the token and endpoint, if you wan't to configure something different, like https://npm-publish.petuska.dev/:
54+
55+
```kotlin
56+
val token = codeArtifactToken(domain = "my_domain")
57+
val uri = codeArtifactUri(domain = "my_domain", repository = "my_repository")
58+
```
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.liftric.code.artifact.repository
2+
3+
import org.gradle.api.provider.Property
4+
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider
5+
import software.amazon.awssdk.regions.Region
6+
import software.amazon.awssdk.services.codeartifact.CodeartifactClient
7+
import software.amazon.awssdk.services.codeartifact.model.GetAuthorizationTokenResponse
8+
import software.amazon.awssdk.services.codeartifact.model.GetRepositoryEndpointResponse
9+
import software.amazon.awssdk.services.sts.StsClient
10+
11+
/**
12+
* internal class handling all aws client config and calls
13+
*/
14+
abstract class CodeArtifact {
15+
abstract val region: Property<Region>
16+
abstract val profile: Property<String>
17+
abstract val tokenExpiresIn: Property<Long>
18+
19+
private val stsClient by lazy {
20+
StsClient.builder().apply {
21+
region.orNull?.let {
22+
region(it)
23+
}
24+
profile.orNull?.let {
25+
credentialsProvider {
26+
ProfileCredentialsProvider.create(profile.get()).resolveCredentials()
27+
}
28+
}
29+
}.build()
30+
}
31+
32+
private val client by lazy {
33+
CodeartifactClient.builder().apply {
34+
region.orNull?.let {
35+
region(it)
36+
}
37+
profile.orNull?.let {
38+
credentialsProvider {
39+
ProfileCredentialsProvider.create(profile.get()).resolveCredentials()
40+
}
41+
}
42+
}.build()
43+
}
44+
45+
private val accountId by lazy {
46+
stsClient.getCallerIdentity {}.account()
47+
}
48+
49+
internal fun authorizationTokenResponse(domain: String): GetAuthorizationTokenResponse {
50+
return client.getAuthorizationToken {
51+
it.domain(domain)
52+
it.domainOwner(accountId)
53+
it.durationSeconds(tokenExpiresIn.getOrElse(1_800))
54+
}
55+
}
56+
57+
internal fun repositoryEndpointResponse(
58+
domain: String,
59+
repository: String,
60+
format: String = "maven"
61+
): GetRepositoryEndpointResponse {
62+
return client.getRepositoryEndpoint {
63+
it.domain(domain)
64+
it.domainOwner(accountId)
65+
it.repository(repository)
66+
it.format(format)
67+
}
68+
}
69+
}
Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
package com.liftric.code.artifact.repository
22

3-
import org.gradle.api.provider.Property
4-
import software.amazon.awssdk.regions.Region
3+
import org.gradle.api.plugins.ExtensionContainer
54

6-
interface CodeArtifactRepositoryExtension {
7-
val region: Property<Region>
8-
val profile: Property<String>
9-
val domain: Property<String>
10-
val tokenExpiresIn: Property<Long>
11-
val shouldResolveCredentialsByEnvironment: Property<Boolean>
5+
abstract class CodeArtifactRepositoryExtension(private val extensionContainer: ExtensionContainer) : CodeArtifact() {
6+
fun additional(name: String, block: CodeArtifact.() -> Unit) {
7+
if (name.isEmpty()) error("empty domain not supported!")
8+
additional[name] = extensionContainer.create(
9+
"${name}${CodeArtifactRepositoryPlugin.extensionName}",
10+
CodeArtifactRepositoryExtension::class.java,
11+
extensionContainer
12+
)
13+
.apply(block)
14+
.apply {
15+
region.convention(this@CodeArtifactRepositoryExtension.region)
16+
profile.convention(this@CodeArtifactRepositoryExtension.profile)
17+
tokenExpiresIn.convention(this@CodeArtifactRepositoryExtension.tokenExpiresIn)
18+
}
19+
}
20+
21+
companion object {
22+
internal val additional = mutableMapOf<String, CodeArtifact>()
23+
}
1224
}
Lines changed: 67 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@file:Suppress("unused")
2+
13
package com.liftric.code.artifact.repository
24

35
import org.gradle.api.GradleException
@@ -7,96 +9,93 @@ import org.gradle.api.artifacts.dsl.RepositoryHandler
79
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
810
import org.gradle.api.initialization.Settings
911
import org.gradle.configurationcache.extensions.capitalized
10-
import org.gradle.kotlin.dsl.create
11-
import org.gradle.kotlin.dsl.getByType
12-
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider
13-
import software.amazon.awssdk.services.codeartifact.CodeartifactClient
14-
import software.amazon.awssdk.services.codeartifact.model.GetAuthorizationTokenResponse
15-
import software.amazon.awssdk.services.codeartifact.model.GetRepositoryEndpointResponse
16-
import software.amazon.awssdk.services.sts.StsClient
17-
18-
private const val extensionName = "CodeArtifactRepository"
19-
20-
private lateinit var codeArtifact: CodeArtifact
21-
private lateinit var extension: CodeArtifactRepositoryExtension
12+
import org.gradle.kotlin.dsl.getByName
13+
import java.net.URI
2214

2315
abstract class CodeArtifactRepositoryPlugin : Plugin<Any> {
2416
override fun apply(scope: Any) {
2517
when (scope) {
2618
is Settings -> {
27-
extension = scope.extensions.create(extensionName)
28-
codeArtifact = CodeArtifact(extension)
19+
scope.extensions.create(extensionName, CodeArtifactRepositoryExtension::class.java, scope.extensions)
2920
}
21+
3022
is Project -> {
31-
extension = scope.extensions.create(extensionName)
32-
codeArtifact = CodeArtifact(extension)
23+
scope.extensions.create(extensionName, CodeArtifactRepositoryExtension::class.java, scope.extensions)
3324
}
25+
3426
else -> {
3527
throw GradleException("Should only get applied on Settings or Project")
3628
}
3729
}
3830
}
39-
}
40-
41-
class CodeArtifact(private val extension: CodeArtifactRepositoryExtension) {
42-
private val account: String
43-
get() = stsClient.getCallerIdentity {}.account()
4431

45-
private val stsClient by lazy {
46-
StsClient.builder().apply {
47-
region(extension.region.get())
48-
if (!extension.shouldResolveCredentialsByEnvironment.getOrElse(true)) {
49-
credentialsProvider {
50-
ProfileCredentialsProvider.create(extension.profile.get()).resolveCredentials()
51-
}
52-
}
53-
}.build()
54-
}
55-
56-
private val codeArtifactClient by lazy {
57-
CodeartifactClient.builder().apply {
58-
region(extension.region.get())
59-
if (!extension.shouldResolveCredentialsByEnvironment.getOrElse(true)) {
60-
credentialsProvider {
61-
ProfileCredentialsProvider.create(extension.profile.get()).resolveCredentials()
62-
}
63-
}
64-
}.build()
65-
}
66-
67-
fun authorizationTokenRepsponse(): GetAuthorizationTokenResponse {
68-
return codeArtifactClient.getAuthorizationToken {
69-
it.domain(extension.domain.get())
70-
it.domainOwner(account)
71-
it.durationSeconds(extension.tokenExpiresIn.getOrElse(1_800))
72-
}
73-
}
74-
75-
fun repositoryEndpointResponse(repository: String, format: String = "maven"): GetRepositoryEndpointResponse {
76-
return codeArtifactClient.getRepositoryEndpoint {
77-
it.domain(extension.domain.get())
78-
it.domainOwner(account)
79-
it.repository(repository)
80-
it.format(format)
81-
}
32+
companion object {
33+
const val extensionName = "CodeArtifactRepository"
8234
}
8335
}
8436

8537
inline fun Settings.codeArtifactRepository(configure: CodeArtifactRepositoryExtension.() -> Unit) {
86-
extensions.getByType<CodeArtifactRepositoryExtension>().configure()
38+
extensions.getByName<CodeArtifactRepositoryExtension>(CodeArtifactRepositoryPlugin.extensionName).configure()
8739
}
8840

8941
inline fun Project.codeArtifactRepository(configure: CodeArtifactRepositoryExtension.() -> Unit) {
90-
extensions.getByType<CodeArtifactRepositoryExtension>().configure()
42+
extensions.getByName<CodeArtifactRepositoryExtension>(CodeArtifactRepositoryPlugin.extensionName).configure()
9143
}
9244

93-
fun RepositoryHandler.codeArtifact(repository: String): MavenArtifactRepository = codeArtifact(extension.domain.get(), repository)
45+
/**
46+
* Use the default CodeArtifact config (and therefore extension)
47+
*/
48+
fun RepositoryHandler.codeArtifact(domain: String, repository: String): MavenArtifactRepository =
49+
codeArtifact("", domain, repository)
9450

95-
fun RepositoryHandler.codeArtifact(domain: String, repository: String): MavenArtifactRepository = maven {
96-
setName(listOf("CodeArtifact", domain, repository).joinToString("") { it.capitalized() })
97-
setUrl(codeArtifact.repositoryEndpointResponse(repository).repositoryEndpoint())
98-
credentials {
99-
username = "aws"
100-
password = codeArtifact.authorizationTokenRepsponse().authorizationToken()
101-
}
51+
/**
52+
* Use CodeArtifact by additional name
53+
*/
54+
fun RepositoryHandler.codeArtifact(additionalName: String, domain: String, repository: String) = maven {
55+
val extensionName = "$additionalName${CodeArtifactRepositoryPlugin.extensionName}"
56+
CodeArtifactRepositoryExtension.additional[extensionName]?.let {
57+
name = listOf(extensionName, domain, repository).joinToString("") { it.capitalized() }
58+
url = URI.create(it.repositoryEndpointResponse(domain, repository).repositoryEndpoint())
59+
credentials {
60+
username = "aws"
61+
password = it.authorizationTokenResponse(domain).authorizationToken()
62+
}
63+
} ?: throw GradleException("didn't find CodeArtifactRepositoryExtension with the name: $")
64+
}
65+
66+
/**
67+
* If you need the plain token
68+
*/
69+
fun codeArtifactToken(domain: String): String = codeArtifactToken("", domain)
70+
71+
/**
72+
* If you need the plain endpoint uri
73+
*/
74+
fun codeArtifactUri(domain: String, repository: String, format: String): URI =
75+
codeArtifactUri("", domain, repository, format)
76+
77+
/**
78+
* If you need the plain token
79+
*
80+
* @param additionalName this is the name (prefix) of the codeArtifactRepository configuration. Use an empty string to use
81+
* the default extension
82+
*/
83+
fun codeArtifactToken(additionalName: String, domain: String): String {
84+
val extensionName = "$additionalName${CodeArtifactRepositoryPlugin.extensionName}"
85+
val settings = CodeArtifactRepositoryExtension.additional[extensionName]
86+
?: throw GradleException("didn't find CodeArtifactRepositoryExtension with the name: $")
87+
return settings.authorizationTokenResponse(domain).authorizationToken()
88+
}
89+
90+
/**
91+
* If you need the plain endpoint uri
92+
*
93+
* @param additionalName this is the name (prefix) of the codeArtifactRepository configuration. Use an empty string to use
94+
* the default extension
95+
*/
96+
fun codeArtifactUri(additionalName: String, domain: String, repository: String, format: String): URI {
97+
val extensionName = "$additionalName${CodeArtifactRepositoryPlugin.extensionName}"
98+
val settings = CodeArtifactRepositoryExtension.additional[extensionName]
99+
?: throw GradleException("didn't find CodeArtifactRepositoryExtension with the name: $")
100+
return settings.repositoryEndpointResponse(domain, repository, format).repositoryEndpoint().let { URI.create(it) }
102101
}

0 commit comments

Comments
 (0)