diff --git a/gradle.properties b/gradle.properties index 59b9fcda..8ab5acc5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,8 @@ SONATYPE_HOST=DEFAULT RELEASE_SIGNING_ENABLED=true GROUP=co.touchlab.kmmbridge -VERSION_NAME=1.1.1 + +VERSION_NAME=1.2.0 VERSION_NAME_3x=0.3.7 POM_URL=https://github.com/touchlab/KMMBridge diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 058eb425..78cc8273 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,6 +6,7 @@ mavenPublish = "0.30.0" aws = { module = "software.amazon.awssdk:s3", version = "2.23.8" } okhttp = { module = "com.squareup.okhttp3:okhttp", version = "4.12.0" } gson = { module = "com.google.code.gson:gson", version = "2.10.1" } +kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin-api", version.ref = "kotlin" } [plugins] kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } diff --git a/kmmbridge-github/src/main/kotlin/co/touchlab/kmmbridge/github/GithubReleaseArtifactManager.kt b/kmmbridge-github/src/main/kotlin/co/touchlab/kmmbridge/github/GithubReleaseArtifactManager.kt index f879f2bb..2486deef 100644 --- a/kmmbridge-github/src/main/kotlin/co/touchlab/kmmbridge/github/GithubReleaseArtifactManager.kt +++ b/kmmbridge-github/src/main/kotlin/co/touchlab/kmmbridge/github/GithubReleaseArtifactManager.kt @@ -3,6 +3,8 @@ package co.touchlab.kmmbridge.github import co.touchlab.kmmbridge.KmmBridgeExtension import co.touchlab.kmmbridge.artifactmanager.ArtifactManager import co.touchlab.kmmbridge.github.internal.GithubCalls +import co.touchlab.kmmbridge.github.internal.githubArtifactIdentifierName +import co.touchlab.kmmbridge.github.internal.githubArtifactReleaseId import co.touchlab.kmmbridge.github.internal.githubPublishToken import co.touchlab.kmmbridge.github.internal.githubRepo import org.gradle.api.GradleException @@ -12,9 +14,14 @@ import org.gradle.api.tasks.Input import org.gradle.api.tasks.TaskProvider import org.gradle.kotlin.dsl.getByType import java.io.File - -internal class GithubReleaseArtifactManager( - private val repository: String?, private val releaseString: String?, private val useExistingRelease: Boolean +import kotlin.properties.Delegates + +open class GithubReleaseArtifactManager( + private val repository: String?, + private val releaseString: String?, + @Deprecated("Releases should be created externally. This parameter controls the flow for releases created " + + "by this class, which will eventually be unsupported.") + private val useExistingRelease: Boolean ) : ArtifactManager { @get:Input @@ -26,42 +33,53 @@ internal class GithubReleaseArtifactManager( @get:Input lateinit var frameworkName: String + @get:Input + lateinit var artifactIdentifierName: String + + @get:Input + var artifactReleaseId by Delegates.notNull() + // TODO: This value is stored in the config cache. It is encrypted, but this still feels insecure. Review alternatives. // https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:secrets lateinit var githubPublishToken: String override fun configure( - project: Project, - version: String, - uploadTask: TaskProvider, - kmmPublishTask: TaskProvider + project: Project, version: String, uploadTask: TaskProvider, kmmPublishTask: TaskProvider ) { this.releaseVersion = releaseString ?: project.version.toString() this.repoName = this.repository ?: project.githubRepo this.githubPublishToken = project.githubPublishToken this.frameworkName = project.kmmBridgeExtension.frameworkName.get() + artifactReleaseId = project.githubArtifactReleaseId?.toInt() ?: 0 + this.artifactIdentifierName = project.githubArtifactIdentifierName ?: "" } override fun deployArtifact(task: Task, zipFilePath: File, version: String): String { - val existingReleaseId = GithubCalls.findReleaseId( - githubPublishToken, repoName, releaseVersion - ) + val uploadReleaseId = if (artifactReleaseId == 0) { + val existingReleaseId = GithubCalls.findReleaseId( + githubPublishToken, repoName, releaseVersion + ) - task.logger.info("existingReleaseId: $existingReleaseId") + task.logger.info("existingReleaseId: $existingReleaseId") - if (existingReleaseId != null && !useExistingRelease) { - throw GradleException("Release for '$releaseVersion' exists. Set 'useExistingRelease = true' to update existing releases.") - } + if (existingReleaseId != null && !useExistingRelease) { + throw GradleException("Release for '$releaseVersion' exists. Set 'useExistingRelease = true' to update existing releases.") + } - val idReply: Int = existingReleaseId ?: GithubCalls.createRelease( - githubPublishToken, repoName, releaseVersion, null - ) + val idReply: Int = existingReleaseId ?: GithubCalls.createRelease( + githubPublishToken, repoName, releaseVersion, null + ) - task.logger.info("GitHub Release created with id: $idReply") + task.logger.info("GitHub Release created with id: $idReply") + + idReply + } else { + artifactReleaseId + } val fileName = artifactName(version, useExistingRelease) - val uploadUrl = GithubCalls.uploadZipFile(githubPublishToken, zipFilePath, repoName, idReply, fileName) + val uploadUrl = GithubCalls.uploadZipFile(githubPublishToken, zipFilePath, repoName, uploadReleaseId, fileName) return "${uploadUrl}.zip" } @@ -69,9 +87,15 @@ internal class GithubReleaseArtifactManager( return if (useExistingRelease) { "$frameworkName-${versionString}-${(System.currentTimeMillis() / 1000)}.xcframework.zip" } else { - "$frameworkName.xcframework.zip" + uploadZipFileName(versionString) } } + + open fun uploadZipFileName(versionString: String) = if (artifactIdentifierName.isNotEmpty()) { + "$frameworkName-${artifactIdentifierName}.xcframework.zip" + } else { + "$frameworkName.xcframework.zip" + } } internal val Project.kmmBridgeExtension get() = extensions.getByType() \ No newline at end of file diff --git a/kmmbridge-github/src/main/kotlin/co/touchlab/kmmbridge/github/internal/GithubApi.kt b/kmmbridge-github/src/main/kotlin/co/touchlab/kmmbridge/github/internal/GithubApi.kt index 537ce007..69582e5a 100644 --- a/kmmbridge-github/src/main/kotlin/co/touchlab/kmmbridge/github/internal/GithubApi.kt +++ b/kmmbridge-github/src/main/kotlin/co/touchlab/kmmbridge/github/internal/GithubApi.kt @@ -39,6 +39,12 @@ internal val Project.githubPublishToken get() = (project.property("GITHUB_PUBLISH_TOKEN") ?: throw IllegalArgumentException("KMMBridge Github operations need property GITHUB_PUBLISH_TOKEN")) as String +internal val Project.githubArtifactReleaseId + get() = project.findStringProperty("GITHUB_ARTIFACT_RELEASE_ID") + +internal val Project.githubArtifactIdentifierName + get() = project.findStringProperty("GITHUB_ARTIFACT_IDENTIFIER_NAME") + internal val Project.githubRepo: String get() = githubRepoOrNull ?: throw IllegalArgumentException("KMMBridge Github operations need a repo param or property GITHUB_REPO") diff --git a/kmmbridge/build.gradle.kts b/kmmbridge/build.gradle.kts index 70c32e11..6a5a304b 100644 --- a/kmmbridge/build.gradle.kts +++ b/kmmbridge/build.gradle.kts @@ -57,6 +57,7 @@ dependencies { implementation(libs.gson) testImplementation(kotlin("test")) + testImplementation(libs.kotlin.gradle.plugin) testImplementation(gradleTestKit()) testImplementation("commons-io:commons-io:2.18.0") } diff --git a/kmmbridge/src/test/kotlin/co/touchlab/kmmbridge/KmmBridgeExtensionTest.kt b/kmmbridge/src/test/kotlin/co/touchlab/kmmbridge/KmmBridgeExtensionTest.kt new file mode 100644 index 00000000..803e7780 --- /dev/null +++ b/kmmbridge/src/test/kotlin/co/touchlab/kmmbridge/KmmBridgeExtensionTest.kt @@ -0,0 +1,101 @@ +package co.touchlab.kmmbridge + +import co.touchlab.kmmbridge.artifactmanager.ArtifactManager +import co.touchlab.kmmbridge.artifactmanager.AwsS3PublicArtifactManager +import co.touchlab.kmmbridge.artifactmanager.MavenPublishArtifactManager +import co.touchlab.kmmbridge.dependencymanager.CocoapodsDependencyManager +import co.touchlab.kmmbridge.dependencymanager.DependencyManager +import co.touchlab.kmmbridge.dependencymanager.SpmDependencyManager +import org.gradle.api.Project +import org.gradle.api.provider.Property +import org.gradle.testfixtures.ProjectBuilder +import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType +import kotlin.test.BeforeTest +import kotlin.test.Ignore +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class KmmBridgeExtensionTest { + private lateinit var project: Project + private lateinit var extension: TestKmmBridgeExtension + + @BeforeTest + fun setup() { + project = ProjectBuilder.builder().build() + extension = TestKmmBridgeExtension(project) + } + + @Test + fun `test s3 artifact configuration`() { + extension.apply { + project.s3PublicArtifacts( + region = "us-east-1", + bucket = "test-bucket", + accessKeyId = "test-key", + secretAccessKey = "test-secret" + ) + } + + val artifactManager = extension.artifactManager.get() + assertTrue(artifactManager is AwsS3PublicArtifactManager) + } + + @Test + fun `test maven publish configuration`() { + extension.apply { + project.mavenPublishArtifacts( + repository = "test-repo", + publication = "test-pub", + isMavenCentral = true + ) + } + + val artifactManager = extension.artifactManager.get() + assertTrue(artifactManager is MavenPublishArtifactManager) + } + + @Test + fun `test spm configuration`() { + extension.apply { + project.spm( + spmDirectory = "test-dir", + useCustomPackageFile = true + ) + } + + val dependencyManager = extension.dependencyManagers.get().first() + assertTrue(dependencyManager is SpmDependencyManager) + } + + @Test + @Ignore("CocoaPods plugin not loaded in test environment. Trunk specifically isn't important.") + fun `test cocoapods trunk configuration`() { + extension.apply { + project.cocoapodsTrunk( + allowWarnings = true, + verboseErrors = true + ) + } + + val dependencyManager = extension.dependencyManagers.get().first() + assertTrue(dependencyManager is CocoapodsDependencyManager) + } + + @Test + fun `test property finalization`() { + val testValue = "test-framework" + extension.frameworkName.set(testValue) + extension.frameworkName.finalizeValue() + + assertEquals(testValue, extension.frameworkName.get()) + assertTrue(extension.frameworkName.isPresent) + } +} + +private class TestKmmBridgeExtension(private val project: Project) : KmmBridgeExtension { + override val frameworkName: Property = project.objects.property(String::class.java) + override val dependencyManagers = project.objects.listProperty(DependencyManager::class.java) + override val artifactManager = project.objects.property(ArtifactManager::class.java) + override val buildType: Property = project.objects.property(NativeBuildType::class.java) +}