diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index ced7e7833930c279185d35b383c889f94ae47836..2ac9014046ad8595f467e1c0973a3b6f20d0fff4 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -9,4 +9,10 @@ # message = "Fix typos in module documentation for generated crates" # references = ["smithy-rs#920"] # meta = { "breaking" = false, "tada" = false, "bug" = false, "sdk" = "client | server | all"} -# author = "rcoh" \ No newline at end of file +# author = "rcoh" + +[[smithy-rs]] +message = "Add codegen version to generated package metadata" +references = ["smithy-rs#1612"] +meta = { "breaking" = false, "tada" = false, "bug" = false } +author = "unexge" diff --git a/codegen/build.gradle.kts b/codegen/build.gradle.kts index e73271af856f3b10fc5e021d5ae60a6e6f40564a..b19e7fa0b824a3ac0b91679e924c562f41043227 100644 --- a/codegen/build.gradle.kts +++ b/codegen/build.gradle.kts @@ -4,6 +4,7 @@ */ import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import java.io.ByteArrayOutputStream plugins { kotlin("jvm") @@ -39,6 +40,18 @@ tasks.compileTestKotlin { kotlinOptions.jvmTarget = "1.8" } +fun gitCommitHash() = + try { + val output = ByteArrayOutputStream() + exec { + commandLine = listOf("git", "rev-parse", "HEAD") + standardOutput = output + } + output.toString().trim() + } catch (ex: Exception) { + "unknown" + } + val generateSmithyRuntimeCrateVersion by tasks.registering { // generate the version of the runtime to use as a resource. // this keeps us from having to manually change version numbers in multiple places @@ -47,9 +60,11 @@ val generateSmithyRuntimeCrateVersion by tasks.registering { outputs.file(versionFile) val crateVersion = project.properties["smithy.rs.runtime.crate.version"].toString() inputs.property("crateVersion", crateVersion) + // version format must be in sync with `software.amazon.smithy.rust.codegen.smithy.Version` + val version = "$crateVersion\n${gitCommitHash()}" sourceSets.main.get().output.dir(resourcesDir) doLast { - versionFile.writeText(crateVersion) + versionFile.writeText(version) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt index 5870b9cef540d1c3e3c7fa37dd13a67e0a1673a0..15685e626e91398ce39202c59fd56401f5bdafd1 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt @@ -46,12 +46,10 @@ fun RuntimeCrateLocation.crateLocation(): DependencyLocation = when (this.path) } fun defaultRuntimeCrateVersion(): String { - // generated as part of the build, see codegen/build.gradle.kts try { - return object {}.javaClass.getResource("runtime-crate-version.txt")?.readText() - ?: throw CodegenException("sdk-version.txt does not exist") + return Version.crateVersion() } catch (ex: Exception) { - throw CodegenException("failed to load sdk-version.txt which sets the default client-runtime version", ex) + throw CodegenException("failed to get crate version which sets the default client-runtime version", ex) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Version.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Version.kt new file mode 100644 index 0000000000000000000000000000000000000000..322b933f9ce44334f4dbc079ea54a0c2bceefb5f --- /dev/null +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Version.kt @@ -0,0 +1,37 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.smithy + +import software.amazon.smithy.codegen.core.CodegenException + +// generated as part of the build, see codegen/build.gradle.kts +private const val VERSION_FILENAME = "runtime-crate-version.txt" + +data class Version(val fullVersion: String, val crateVersion: String) { + companion object { + // Version must be in the "{smithy_rs_version}\n{git_commit_hash}" format + fun parse(content: String): Version { + val lines = content.lines() + if (lines.size != 2) { + throw IllegalArgumentException("Invalid version format, it should contain `2` lines but contains `${lines.size}` line(s)") + } + return Version(lines.joinToString("-"), lines.first()) + } + + // Returns full version in the "{smithy_rs_version}-{git_commit_hash}" format + fun fullVersion(): String = + fromDefaultResource().fullVersion + + fun crateVersion(): String = + fromDefaultResource().crateVersion + + private fun fromDefaultResource(): Version = + parse( + object {}.javaClass.getResource(VERSION_FILENAME)?.readText() + ?: throw CodegenException("$VERSION_FILENAME does not exist"), + ) + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGenerator.kt index dfbcd7e73fc2808bac545088cc4dce990c816ec1..b932cac262de1a2e660c12c20b174c3fb77e7551 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGenerator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.rust.codegen.rustlang.DependencyScope import software.amazon.smithy.rust.codegen.rustlang.Feature import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.smithy.CoreRustSettings +import software.amazon.smithy.rust.codegen.smithy.Version import software.amazon.smithy.rust.codegen.util.deepMergeWith /** @@ -63,6 +64,11 @@ class CargoTomlGenerator( "edition" to "2021", "license" to settings.license, "repository" to settings.moduleRepository, + "metadata" to listOfNotNull( + "smithy" to listOfNotNull( + "codegen-version" to Version.fullVersion(), + ).toMap(), + ).toMap(), ).toMap(), "dependencies" to dependencies.filter { it.scope == DependencyScope.Compile } .associate { it.name to it.toMap() }, diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/VersionTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/VersionTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..56c254c2d6e51257cec373782341bd35c8456ae5 --- /dev/null +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/VersionTest.kt @@ -0,0 +1,68 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.smithy + +import io.kotest.assertions.throwables.shouldThrowAny +import io.kotest.matchers.shouldBe +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +class VersionTest { + @ParameterizedTest() + @MethodSource("versionProvider") + fun `parses version`( + content: String, + fullVersion: String, + crateVersion: String, + ) { + val version = Version.parse(content) + version.fullVersion shouldBe fullVersion + version.crateVersion shouldBe crateVersion + } + + @ParameterizedTest() + @MethodSource("invalidVersionProvider") + fun `fails to parse version`( + content: String, + ) { + shouldThrowAny { Version.parse(content) } + } + + companion object { + @JvmStatic + fun versionProvider() = listOf( + Arguments.of( + "0.47.0\n0198d26096eb1af510ce24766c921ffc5e4c191e", + "0.47.0-0198d26096eb1af510ce24766c921ffc5e4c191e", + "0.47.0", + ), + Arguments.of( + "release-2022-08-04\ndb48039065bec890ef387385773b37154b555b14", + "release-2022-08-04-db48039065bec890ef387385773b37154b555b14", + "release-2022-08-04", + ), + Arguments.of( + "0.30.0-alpha\na1dbbe2947de3c8bbbef9446eb442e298f83f200", + "0.30.0-alpha-a1dbbe2947de3c8bbbef9446eb442e298f83f200", + "0.30.0-alpha", + ), + Arguments.of( + "0.6-rc1.cargo\nc281800a185b34600b05f8b501a0322074184123", + "0.6-rc1.cargo-c281800a185b34600b05f8b501a0322074184123", + "0.6-rc1.cargo", + ), + Arguments.of( + "0.27.0-alpha.1\n643f2ee", + "0.27.0-alpha.1-643f2ee", + "0.27.0-alpha.1", + ), + ) + + @JvmStatic + fun invalidVersionProvider() = listOf("0.0.0", "") + } +} diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGeneratorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..ecc20a7f029e032b134f3d12bd508cabc3d34934 --- /dev/null +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGeneratorTest.kt @@ -0,0 +1,45 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.smithy.generators + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.rustlang.CratesIo +import software.amazon.smithy.rust.codegen.smithy.Version +import software.amazon.smithy.rust.codegen.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.testutil.unitTest + +class CargoTomlGeneratorTest { + private val CargoMetadata: CargoDependency = CargoDependency("cargo_metadata", CratesIo("0.15.0")) + + @Test + fun `adds codegen version to package metadata`() { + val project = TestWorkspace.testProject() + project.lib { writer -> + writer.addDependency(CargoMetadata) + writer.unitTest( + "smithy_codegen_version_in_package_metadata", + """ + let metadata = cargo_metadata::MetadataCommand::new() + .exec() + .expect("could not run `cargo metadata`"); + + let pgk_metadata = &metadata.root_package().expect("missing root package").metadata; + + let codegen_version = pgk_metadata + .get("smithy") + .and_then(|s| s.get("codegen-version")) + .expect("missing `smithy.codegen-version` field") + .as_str() + .expect("`smithy.codegen-version` is not str"); + assert_eq!(codegen_version, "${Version.fullVersion()}"); + """, + ) + } + project.compileAndTest() + } +}