From 511853cdc3f6a5d8a1000e054d3a9c3751baa423 Mon Sep 17 00:00:00 2001 From: Tyler Crawford <91682066+tcrawford-figure@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:57:42 -0500 Subject: [PATCH] fix(calculator): strip non-semver text (#164) --- gradle/libs.versions.toml | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../semver/internal/calculator/Matchers.kt | 22 +++++++++ .../semver/internal/calculator/Patterns.kt | 47 +++++++++++++++++++ .../gradle/semver/internal/command/TagList.kt | 5 +- .../internal/calculator/MatchersKtTest.kt | 38 +++++++++++++++ 6 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/com/figure/gradle/semver/internal/calculator/Matchers.kt create mode 100644 src/main/kotlin/com/figure/gradle/semver/internal/calculator/Patterns.kt create mode 100644 src/test/kotlin/com/figure/gradle/semver/internal/calculator/MatchersKtTest.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index eecb3d6..19b87a3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] -kotlin = "1.9.24" # Goal is to track whatever the latest stable version that is supported by Gradle +kotlin = "1.9.24" jgit = "7.0.0.202409031743-r" kotest = "5.9.1" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 79eb9d0..c1d5e01 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/kotlin/com/figure/gradle/semver/internal/calculator/Matchers.kt b/src/main/kotlin/com/figure/gradle/semver/internal/calculator/Matchers.kt new file mode 100644 index 0000000..a6c0b03 --- /dev/null +++ b/src/main/kotlin/com/figure/gradle/semver/internal/calculator/Matchers.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 Figure Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.figure.gradle.semver.internal.calculator + +fun String.stripNonSemverText(): String { + val versionPattern = Regex(Patterns.VERSION_REGEX) + val matchResult = versionPattern.find(this) + return matchResult?.value ?: this +} diff --git a/src/main/kotlin/com/figure/gradle/semver/internal/calculator/Patterns.kt b/src/main/kotlin/com/figure/gradle/semver/internal/calculator/Patterns.kt new file mode 100644 index 0000000..f793dcd --- /dev/null +++ b/src/main/kotlin/com/figure/gradle/semver/internal/calculator/Patterns.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 Figure Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.figure.gradle.semver.internal.calculator + +internal object Patterns { + // Numeric identifier pattern. (used for parsing major, minor, and patch) + private const val NUMERIC = "0|[1-9]\\d*" + + // Alphanumeric or hyphen pattern. + private const val ALPHANUMERIC_OR_HYPHEN = "[0-9a-zA-Z-]" + + // Letter or hyphen pattern. + private const val LETTER_OR_HYPHEN = "[a-zA-Z-]" + + // Non-numeric identifier pattern. (used for parsing pre-release) + private const val NON_NUMERIC = "\\d*$LETTER_OR_HYPHEN$ALPHANUMERIC_OR_HYPHEN*" + + // Dot-separated numeric identifier pattern. (..) + private const val CORE_VERSION = "($NUMERIC)\\.($NUMERIC)\\.($NUMERIC)" + + // Numeric or non-numeric pre-release part pattern. + private const val PRE_RELEASE_PART = "(?:$NUMERIC|$NON_NUMERIC)" + + // Pre-release identifier pattern. A hyphen followed by dot-separated + // numeric or non-numeric pre-release parts. + private const val PRE_RELEASE = "(?:-($PRE_RELEASE_PART(?:\\.$PRE_RELEASE_PART)*))" + + // Build-metadata identifier pattern. A + sign followed by dot-separated + // alphanumeric build-metadata parts. + private const val BUILD = "(?:\\+($ALPHANUMERIC_OR_HYPHEN+(?:\\.$ALPHANUMERIC_OR_HYPHEN+)*))" + + // Version parsing pattern: 1.2.3-alpha+build + internal const val VERSION_REGEX: String = "$CORE_VERSION$PRE_RELEASE?$BUILD?" +} diff --git a/src/main/kotlin/com/figure/gradle/semver/internal/command/TagList.kt b/src/main/kotlin/com/figure/gradle/semver/internal/command/TagList.kt index cebfb1a..c5cda41 100644 --- a/src/main/kotlin/com/figure/gradle/semver/internal/command/TagList.kt +++ b/src/main/kotlin/com/figure/gradle/semver/internal/command/TagList.kt @@ -15,6 +15,7 @@ */ package com.figure.gradle.semver.internal.command +import com.figure.gradle.semver.internal.calculator.stripNonSemverText import com.figure.gradle.semver.internal.extensions.isNotPreRelease import com.figure.gradle.semver.internal.properties.Stage import io.github.z4kn4fein.semver.Version @@ -34,7 +35,9 @@ class TagList( invoke().find { it.name == tagName } val versionedTags: List - get() = invoke().mapNotNull { it.name.replace(Constants.R_TAGS, "").toVersionOrNull(strict = false) } + get() = invoke() + .map { it.name.replace(Constants.R_TAGS, "").stripNonSemverText() } + .mapNotNull { it.toVersionOrNull() } private fun latest(forMajorVersion: Int?): Version? { val stages = Stage.entries.map { stage -> stage.value.lowercase() } diff --git a/src/test/kotlin/com/figure/gradle/semver/internal/calculator/MatchersKtTest.kt b/src/test/kotlin/com/figure/gradle/semver/internal/calculator/MatchersKtTest.kt new file mode 100644 index 0000000..d5f953c --- /dev/null +++ b/src/test/kotlin/com/figure/gradle/semver/internal/calculator/MatchersKtTest.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 Figure Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.figure.gradle.semver.internal.calculator + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe + +class MatchersKtTest : FunSpec({ + test("should strip off prefix and return version") { + "v1.2.3".stripNonSemverText() shouldBe "1.2.3" + "libraries-v1.5.4".stripNonSemverText() shouldBe "1.5.4" + } + + test("should return input if no version found") { + "no-version-here".stripNonSemverText() shouldBe "no-version-here" + } + + test("should handle empty input") { + "".stripNonSemverText() shouldBe "" + } + + test("should handle input with only version") { + "2.3.4".stripNonSemverText() shouldBe "2.3.4" + } +})