diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 077b4824..fb906e33 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,8 +16,28 @@ jobs: with: java-version: 11 - name: Run tests - run: bazel test //... + run: bazel test //... --keep_going --test_output=errors --test_tag_filters=unit integration-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Run tests + run: bazel test //... --keep_going --test_output=errors --test_tag_filters=integration + klint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Run tests + run: bazel test //... --keep_going --test_output=errors --test_tag_filters=lint + docker-build-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/README.md b/README.md index 00042c10..f52c86a2 100644 --- a/README.md +++ b/README.md @@ -4,20 +4,26 @@ Bazel-Steward is a bot that helps you keep your library dependencies up-to-date. ## How to work on the Project with IntelliJ ->Note: Be sure to install Bazelisk, Intellij with Kotlin and Scala Plugin. +**Note:** Be sure to install: + * Bazelisk (instead of Bazel directly) + * IntelliJ with Kotlin and Scala Plugins -### With Bazel Plugin installed +### If you have Bazel Plugin Installed + +Bazel Plugin is useful for highlighting BUILD, WORKSPACE and .bzl files, but the project doesn't import well with it. +It will force import the project using its own model instead of Bazel-BSP. Follow the steps below to avoid this. 1. Ensure you have Bazel Plugin installed in your IntelliJ 2. Go to `Registry...` 3. Set `bazel.auto.import.disabled` to True. 4. Close project in IntelliJ. -5. If old project was created, you need to delete `.ijwb` directory (most safe solution is to use git clean -dfx) -6. Run setup-bsp.sh script -7. Open this project in IntelliJ +5. If old project was created, you need to delete `.ijwb` directory (most safe solution is to use `git clean -dfx`) + +### Common Setup Steps -### Without Bazel Plugin installed +If you do not have Bazel Plugin or followed the instruction above, follow these steps: 1. Run setup-bsp.sh script 2. Open this project in IntelliJ +3. Select all `./bazel-*` directories and mark them as excluded. Otherwise they will pollute search scope and other features. diff --git a/WORKSPACE b/WORKSPACE index 9563bd37..5e5433ce 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -86,6 +86,10 @@ maven_install( "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.6.0", "io.get-coursier:interface:1.0.11", "commons-io:commons-io:2.11.0", + "com.fasterxml.jackson.module:jackson-module-kotlin:2.11.2", + "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.1", + "com.networknt:json-schema-validator:1.0.76", + "org.slf4j:slf4j-nop:2.0.6", "org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.5", "org.kohsuke:github-api:1.313", ], diff --git a/app/src/main/kotlin/org/virtuslab/bazelsteward/app/BUILD.bazel b/app/src/main/kotlin/org/virtuslab/bazelsteward/app/BUILD.bazel index 672ae886..c7d0fabb 100644 --- a/app/src/main/kotlin/org/virtuslab/bazelsteward/app/BUILD.bazel +++ b/app/src/main/kotlin/org/virtuslab/bazelsteward/app/BUILD.bazel @@ -1,14 +1,11 @@ -kt_jvm_library( - name = "app", +library( srcs = glob(["**/*.kt"]), - visibility = ["//visibility:public"], deps = [ "//common", + "//config", "//core", "//github", "//maven", "@maven//:org_jetbrains_kotlinx_kotlinx_cli_jvm", ], ) - -lint(srcs = glob(["**/*.kt"])) diff --git a/app/src/main/kotlin/org/virtuslab/bazelsteward/app/Context.kt b/app/src/main/kotlin/org/virtuslab/bazelsteward/app/Context.kt index 6b94413f..775025c2 100644 --- a/app/src/main/kotlin/org/virtuslab/bazelsteward/app/Context.kt +++ b/app/src/main/kotlin/org/virtuslab/bazelsteward/app/Context.kt @@ -10,6 +10,7 @@ import org.virtuslab.bazelsteward.common.FileUpdateSearch import org.virtuslab.bazelsteward.common.GitClient import org.virtuslab.bazelsteward.common.GitOperations import org.virtuslab.bazelsteward.common.UpdateLogic +import org.virtuslab.bazelsteward.config.BazelStewardConfiguration import org.virtuslab.bazelsteward.core.Config import org.virtuslab.bazelsteward.core.Environment import org.virtuslab.bazelsteward.core.GitHostClient @@ -49,6 +50,7 @@ data class Context( val config = Config(repoPath, pushToRemote, baseBranchName) + val bsc = runBlocking { BazelStewardConfiguration(repoPath).get() } // will be used later val bfs = BazelFileSearch(config) val mde = MavenDataExtractor(config) val mr = MavenRepository() diff --git a/app/src/test/kotlin/org/virtuslab/bazelsteward/app/BUILD.bazel b/app/src/test/kotlin/org/virtuslab/bazelsteward/app/BUILD.bazel index 9ab9430d..a466dfa2 100644 --- a/app/src/test/kotlin/org/virtuslab/bazelsteward/app/BUILD.bazel +++ b/app/src/test/kotlin/org/virtuslab/bazelsteward/app/BUILD.bazel @@ -1,11 +1,7 @@ -kt_junit5_test( - name = "app", - size = "small", +unit_tests( srcs = glob(["**/*.kt"]), test_package = "org.virtuslab.bazelsteward.app", deps = [ "//app/src/main/kotlin/org/virtuslab/bazelsteward/app", ], ) - -lint(srcs = glob(["**/*.kt"])) diff --git a/common/src/main/kotlin/org/virtuslab/bazelsteward/common/BUILD.bazel b/common/src/main/kotlin/org/virtuslab/bazelsteward/common/BUILD.bazel index 063d6d21..b277297b 100644 --- a/common/src/main/kotlin/org/virtuslab/bazelsteward/common/BUILD.bazel +++ b/common/src/main/kotlin/org/virtuslab/bazelsteward/common/BUILD.bazel @@ -1,10 +1,6 @@ -kt_jvm_library( - name = "common", +library( srcs = glob(["**/*.kt"]), - visibility = ["//visibility:public"], deps = [ "//core", ], ) - -lint(srcs = glob(["**/*.kt"])) diff --git a/config/BUILD.bazel b/config/BUILD.bazel new file mode 100644 index 00000000..98dbe88d --- /dev/null +++ b/config/BUILD.bazel @@ -0,0 +1,5 @@ +alias( + name = "config", + actual = "//config/src/main", + visibility = ["//visibility:public"], +) diff --git a/config/src/main/BUILD.bazel b/config/src/main/BUILD.bazel new file mode 100644 index 00000000..249a98e7 --- /dev/null +++ b/config/src/main/BUILD.bazel @@ -0,0 +1,16 @@ +library( + srcs = glob(["kotlin/**/*.kt"]), + resources = glob( + ["resources/**"], + ), + deps = [ + "//maven", + "@maven//:com_fasterxml_jackson_dataformat_jackson_dataformat_yaml", + "@maven//:com_fasterxml_jackson_module_jackson_module_kotlin", + "@maven//:com_networknt_json_schema_validator", + "@maven//:io_arrow_kt_arrow_core", + "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_core", + "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_jdk8", + "@maven//:org_slf4j_slf4j_nop", + ], +) diff --git a/config/src/main/kotlin/org/virtuslab/bazelsteward/config/BazelStewardConfiguration.kt b/config/src/main/kotlin/org/virtuslab/bazelsteward/config/BazelStewardConfiguration.kt new file mode 100644 index 00000000..50158b67 --- /dev/null +++ b/config/src/main/kotlin/org/virtuslab/bazelsteward/config/BazelStewardConfiguration.kt @@ -0,0 +1,63 @@ +package org.virtuslab.bazelsteward.config + +import com.fasterxml.jackson.annotation.JsonSetter +import com.fasterxml.jackson.annotation.Nulls +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.fasterxml.jackson.module.kotlin.KotlinModule +import com.networknt.schema.JsonSchemaFactory +import com.networknt.schema.SpecVersion +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.virtuslab.bazelsteward.maven.MavenLibraryId +import java.nio.file.Path +import kotlin.io.path.exists + +data class Configuration( + @JsonSetter(nulls = Nulls.AS_EMPTY) + val maven: MavenConfig = MavenConfig() +) + +data class MavenConfig( + @JsonSetter(nulls = Nulls.AS_EMPTY) + val ruledDependencies: List = emptyList() +) + +data class MavenDependency( + val id: MavenLibraryId, + val versioning: String +) + +class BazelStewardConfiguration(repoRoot: Path) { + + private val configFilePath = repoRoot.resolve(".bazel-steward.yaml") + + suspend fun get(): Configuration { + + return withContext(Dispatchers.IO) { + val schemaContent = javaClass.classLoader.getResource("bazel-steward-schema.json")?.readText() + ?: throw Exception("Could not find schema to validate configuration file") + val schema = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909).getSchema(schemaContent) + + runCatching { + if (!configFilePath.exists()) return@withContext Configuration() + val configContent = configFilePath.toFile() + .readLines() + .filterNot { it.startsWith("#") } + .joinToString("\n") + .ifEmpty { return@withContext Configuration() } + val yamlReader = ObjectMapper(YAMLFactory()) + yamlReader.registerModule(KotlinModule()) + val validationResult = schema.validate(yamlReader.readTree(configContent)) + if (validationResult.isNotEmpty()) { + throw Exception(validationResult.joinToString(System.lineSeparator()) { it.message.removePrefix("$.") }) + } else { + yamlReader.readValue(configContent, Configuration::class.java) + } + }.getOrElse { + println("Could not parse $configFilePath file!") + throw it + } + } + } +} diff --git a/config/src/main/resources/bazel-steward-schema.json b/config/src/main/resources/bazel-steward-schema.json new file mode 100644 index 00000000..a2889212 --- /dev/null +++ b/config/src/main/resources/bazel-steward-schema.json @@ -0,0 +1,35 @@ +{ + "type": "object", + "properties": { + "maven": { + "properties": { + "ruledDependencies": { + "type": ["array", "null"], + "items": { + "type": "object", + "properties": { + "id": { + "type": "object", + "properties": { + "group": { + "type": "string" + }, + "artifact": { + "type": "string" + } + }, + "required": ["group", "artifact"] + }, + "versioning": { + "type": "string", + "anyOf": [{"enum": ["loose", "semver"]}, {"pattern": "^regex:"}] + } + }, + "required": ["id", "versioning"] + } + } + }, + "required": ["ruledDependencies"] + } + } +} \ No newline at end of file diff --git a/config/src/test/BUILD.bazel b/config/src/test/BUILD.bazel new file mode 100644 index 00000000..37832f68 --- /dev/null +++ b/config/src/test/BUILD.bazel @@ -0,0 +1,11 @@ +unit_tests( + srcs = glob(["kotlin/**/*.kt"]), + resources = glob( + ["resources/**"], + ), + test_package = "org.virtuslab.bazelsteward.config", + deps = [ + "//config/src/main", + "@maven//:commons_io_commons_io", + ], +) diff --git a/config/src/test/kotlin/org/virtuslab/bazelsteward/config/BazelStewardConfigurationTest.kt b/config/src/test/kotlin/org/virtuslab/bazelsteward/config/BazelStewardConfigurationTest.kt new file mode 100644 index 00000000..e51df157 --- /dev/null +++ b/config/src/test/kotlin/org/virtuslab/bazelsteward/config/BazelStewardConfigurationTest.kt @@ -0,0 +1,56 @@ +package org.virtuslab.bazelsteward.config + +import io.kotest.common.runBlocking +import org.apache.commons.io.FileUtils +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir +import org.virtuslab.bazelsteward.maven.MavenLibraryId +import java.io.File + +class BazelStewardConfigurationTest { + + @Test + fun `should throw an exception when maven object in config file is not correct`(@TempDir tempDir: File) { + copyConfigFileToTempLocation(tempDir, ".bazel-steward-fail.yaml") + Assertions.assertThatThrownBy { runBlocking { BazelStewardConfiguration(tempDir.toPath()).get() } } + .hasMessage( + listOf( + "maven.ruledDependencies[0].id.group: is missing but it is required", + "maven.ruledDependencies[0].id.artifact: is missing but it is required", + "maven.ruledDependencies[1].versioning: does not have a value in the enumeration [loose, semver]", + "maven.ruledDependencies[1].versioning: does not match the regex pattern ^regex:", + "maven.ruledDependencies[2].id.group: integer found, string expected" + ).joinToString(System.lineSeparator()) + ) + } + + @Test + fun `should create default configuration when config file is not declared`(@TempDir tempDir: File) { + val configuration = runBlocking { BazelStewardConfiguration(tempDir.toPath()).get() } + Assertions.assertThat(configuration).isEqualTo(Configuration()) + } + + @Test + fun `should create configuration when config file is correct`(@TempDir tempDir: File) { + copyConfigFileToTempLocation(tempDir, ".bazel-steward-correct.yaml") + val configuration = runBlocking { BazelStewardConfiguration(tempDir.toPath()).get() } + val expectedConfiguration = Configuration( + MavenConfig( + listOf( + MavenDependency(MavenLibraryId("commons-io", "commons-io"), "loose"), + MavenDependency(MavenLibraryId("io.get-coursier", "interface"), "semver"), + MavenDependency(MavenLibraryId("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8"), "regex:(?\\d+)") + ) + ) + ) + Assertions.assertThat(configuration).isEqualTo(expectedConfiguration) + } + + private fun copyConfigFileToTempLocation(tempDir: File, configFileName: String) { + FileUtils.copyURLToFile( + javaClass.classLoader.getResource(configFileName), + File(tempDir, ".bazel-steward.yaml") + ) + } +} diff --git a/config/src/test/resources/.bazel-steward-correct.yaml b/config/src/test/resources/.bazel-steward-correct.yaml new file mode 100644 index 00000000..31dade22 --- /dev/null +++ b/config/src/test/resources/.bazel-steward-correct.yaml @@ -0,0 +1,17 @@ +maven: + ruledDependencies: + - + id: + group: "commons-io" + artifact: "commons-io" + versioning: "loose" + - + id: + group: "io.get-coursier" + artifact: "interface" + versioning: "semver" + - + id: + group: "org.jetbrains.kotlinx" + artifact: "kotlinx-coroutines-jdk8" + versioning: "regex:(?\\d+)" \ No newline at end of file diff --git a/config/src/test/resources/.bazel-steward-fail.yaml b/config/src/test/resources/.bazel-steward-fail.yaml new file mode 100644 index 00000000..8a90096b --- /dev/null +++ b/config/src/test/resources/.bazel-steward-fail.yaml @@ -0,0 +1,17 @@ +maven: + ruledDependencies: + - + id: + grouop: "commons-io" + artifactt: "commons-io" + versioning: "loose" + - + id: + group: "io.get-coursier" + artifact: "interface" + versioning: "semverr" + - + id: + group: 2 + artifact: "kotlinx-coroutines-jdk8" + versioning: "regex:(?\\d+)(?\\.\\d+)?(?\\.\\d+)?$" \ No newline at end of file diff --git a/core/src/main/kotlin/org/virtuslab/bazelsteward/core/BUILD.bazel b/core/src/main/kotlin/org/virtuslab/bazelsteward/core/BUILD.bazel index b1405021..aff33037 100644 --- a/core/src/main/kotlin/org/virtuslab/bazelsteward/core/BUILD.bazel +++ b/core/src/main/kotlin/org/virtuslab/bazelsteward/core/BUILD.bazel @@ -1,12 +1,8 @@ -kt_jvm_library( - name = "core", +library( srcs = glob(["**/*.kt"]), - visibility = ["//visibility:public"], deps = [ "@maven//:io_arrow_kt_arrow_core", "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_core", "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_jdk8", ], ) - -lint(srcs = glob(["**/*.kt"])) diff --git a/core/src/main/kotlin/org/virtuslab/bazelsteward/core/library/SemanticVersion.kt b/core/src/main/kotlin/org/virtuslab/bazelsteward/core/library/SemanticVersion.kt index d2bdc909..beb70f7f 100644 --- a/core/src/main/kotlin/org/virtuslab/bazelsteward/core/library/SemanticVersion.kt +++ b/core/src/main/kotlin/org/virtuslab/bazelsteward/core/library/SemanticVersion.kt @@ -29,22 +29,75 @@ data class SemanticVersion( else if (this.patch != other.patch) this.patch.compareTo(other.patch) else if (this.prerelease != other.prerelease) - this.prerelease.compareTo(other.prerelease) + this.comparePreReleases(this.prerelease, other.prerelease) else 0 + private fun comparePreReleases(first: String, second: String): Int { + // "alpha" < "beta" < "milestone" < "rc" = "cr" < "snapshot" < "" = "final" = "ga" < "sp" + val qualifiers: Map = mapOf( + "alpha" to 1, + "beta" to 2, + "milestone" to 3, + "rc" to 4, + "cr" to 4, + "snapshot" to 5, + "ga" to 6, + "final" to 6, + "release" to 6, + "sp" to 7, + "" to 6, + ) + + val firstQualifier: String? = getQualifierForPreRelease(qualifiers, first) + val otherQualifier: String? = getQualifierForPreRelease(qualifiers, second) + + return when { + qualifiers[firstQualifier] == null -> if (qualifiers[otherQualifier] == null) first.compareTo(second) else -1 + qualifiers[otherQualifier] == null -> 1 + else -> qualifiers[firstQualifier]!!.compareTo(qualifiers[otherQualifier]!!) + }.let { + if (it == 0) { + val firstWithoutQualifier = getPreReleaseWithoutQualifier(firstQualifier, first) + val secondWithoutQualifier = getPreReleaseWithoutQualifier(otherQualifier, second) + firstWithoutQualifier.compareTo(secondWithoutQualifier) + } else it + } + } + + private fun getQualifierForPreRelease(qualifiers: Map, preRelease: String): String? { + return when { + preRelease.startsWith('a') -> "alpha" + preRelease.startsWith('b') -> "beta" + preRelease.startsWith('m') -> "milestone" + else -> qualifiers.keys.firstOrNull { preRelease.contains(it) } + } + } + + private fun getPreReleaseWithoutQualifier(qualifier: String?, preRelease: String): String { + return qualifier?.let { + if (preRelease.contains(it)) { + preRelease.replaceFirst(it, "") + } else { + preRelease.replaceFirst(it.first().toString(), "") + } + } ?: preRelease + } + companion object { + private val canonicalSemVerRegex = + Regex("""^(?0|[1-9]\d*)\.(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:-(?(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?${'$'}""") private val semVerRegex = - Regex("""^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?${'$'}""") + Regex("""^(?0|[1-9]\d*)(?:[.-](?(0|[1-9]\d*)))?(?:[.-]?(?(0|[1-9]\d*)))?(?:[-.]?(?((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)))?(?:\+(?([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)))?${'$'}""") fun fromString(value: String): Option { - return semVerRegex.matchEntire(value).toOption().map { - val values = it.groupValues + return semVerRegex.matchEntire(value).toOption().map { matchResult -> + val values = matchResult.groups as MatchNamedGroupCollection SemanticVersion( - values[1].toInt(), - values[2].toInt(), - values[3].toInt(), - values[4], - values[5] + values["major"]?.value?.toIntOrNull() ?: 0, + values["minor"]?.value?.toIntOrNull() ?: 0, + values["patch"]?.value?.toIntOrNull() ?: 0, + values["preRelease"]?.value ?: "", + values["buildMetaData"]?.value ?: "", ) } } diff --git a/core/src/test/kotlin/org/virtuslab/bazelsteward/core/BUILD.bazel b/core/src/test/kotlin/org/virtuslab/bazelsteward/core/BUILD.bazel index ec8a3e10..e91a799d 100644 --- a/core/src/test/kotlin/org/virtuslab/bazelsteward/core/BUILD.bazel +++ b/core/src/test/kotlin/org/virtuslab/bazelsteward/core/BUILD.bazel @@ -1,11 +1,7 @@ -kt_junit5_test( - name = "core", - size = "small", +unit_tests( srcs = glob(["**/*.kt"]), test_package = "org.virtuslab.bazelsteward.core", deps = [ "//core/src/main/kotlin/org/virtuslab/bazelsteward/core", ], ) - -lint(srcs = glob(["**/*.kt"])) diff --git a/core/src/test/kotlin/org/virtuslab/bazelsteward/core/library/SemanticVersionTest.kt b/core/src/test/kotlin/org/virtuslab/bazelsteward/core/library/SemanticVersionTest.kt index d7bc67cb..eefcff55 100644 --- a/core/src/test/kotlin/org/virtuslab/bazelsteward/core/library/SemanticVersionTest.kt +++ b/core/src/test/kotlin/org/virtuslab/bazelsteward/core/library/SemanticVersionTest.kt @@ -11,7 +11,28 @@ class SemanticVersionTest { fun `test fromString`() { testSemanticVersion("1.2.3", 1, 2, 3, "", "") testSemanticVersion("2.0.0-alpha+beta", 2, 0, 0, "alpha", "beta") - SemanticVersion.fromString("1.2") shouldBe None + testSemanticVersion("1.0.1", 1, 0, 1, "", "") + testSemanticVersion("1.0.0-alpha", 1, 0, 0, "alpha", "") + testSemanticVersion("1.0.0-alpha.1", 1, 0, 0, "alpha.1", "") + testSemanticVersion("1.0.0-0.3.7", 1, 0, 0, "0.3.7", "") + testSemanticVersion("1.0.0-x.7.z.92", 1, 0, 0, "x.7.z.92", "") + testSemanticVersion("1.0.0-alpha+001", 1, 0, 0, "alpha", "001") + testSemanticVersion("1.0.0+20130313144700", 1, 0, 0, "", "20130313144700") + testSemanticVersion("1.0.0-beta+exp.sha.5114f85", 1, 0, 0, "beta", "exp.sha.5114f85") + testSemanticVersion("1.0.0-alpha", 1, 0, 0, "alpha", "") + testSemanticVersion("1.0.0-alpha.1", 1, 0, 0, "alpha.1", "") + testSemanticVersion("1.0.0-alpha.beta", 1, 0, 0, "alpha.beta", "") + testSemanticVersion("1.0.0-beta", 1, 0, 0, "beta", "") + testSemanticVersion("1.0.0-beta.2", 1, 0, 0, "beta.2", "") + testSemanticVersion("1.0.0-beta.11", 1, 0, 0, "beta.11", "") + testSemanticVersion("1.0.0-rc.1", 1, 0, 0, "rc.1", "") + testSemanticVersion("1.0.0", 1, 0, 0, "", "") + } + + @Test + fun `test fromString wrong versions`() { + SemanticVersion.fromString("1!1.0") shouldBe None + SemanticVersion.fromString("NotAVersionSting") shouldBe None } private fun testSemanticVersion( diff --git a/core/src/test/kotlin/org/virtuslab/bazelsteward/core/library/VersionTest.kt b/core/src/test/kotlin/org/virtuslab/bazelsteward/core/library/VersionTest.kt new file mode 100644 index 00000000..68e9a8e8 --- /dev/null +++ b/core/src/test/kotlin/org/virtuslab/bazelsteward/core/library/VersionTest.kt @@ -0,0 +1,74 @@ +package org.virtuslab.bazelsteward.core.library + +import arrow.core.None +import arrow.core.compareTo +import io.kotest.matchers.ints.shouldBeGreaterThan +import io.kotest.matchers.shouldNotBe +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import java.util.stream.Stream + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class VersionTest { + private val mavenVersions = listOf( + "1.0+whatever", + "1.0a1-SNAPSHOT+whatever", + "1.final-2a", + "1.final", + "1.final1+whatever", + "1.0-0", + "1.0beta1-SNAPSHOT", + "1.0-alpha1", + "1.0-beta3.SNAPSHOT", + "1.0-b2", + "1.0-milestone1-SNAPSHOT", + "1.0-beta3", + "1.0-rc1-SNAPSHOT", + "1.0-m2", + "1.0-SNAPSHOT", + "1.0-cr1", + "1.0-sp", + "1.0", + "1.0-RELEASE", + "1.0-a", + "1.0.z", + "1.0-whatever", + "1-1.foo-bar-1-baz-0.1", + "1.0.1.0.0.0.0.0.0.0.0.0.0.0.1", + "2022.11.29.0.1-api-version-222", + "1.0.0b1", + "7.10-final", + "7.9-628", + "7.9-sp", + "7.9", + ) + + private fun argumentsForCheckVersion(): Stream { + return mavenVersions.stream() + } + + @ParameterizedTest + @MethodSource("argumentsForCheckVersion") + fun `check version`(version: String) { + val ver = SimpleVersion(version) + ver.toSemVer() shouldNotBe None + } + + private fun argumentsForCompareVersions(): Stream { + val listWithArguments: MutableList = mutableListOf() + for (i in mavenVersions.indices step 2) { + listWithArguments.add(Arguments.of(mavenVersions[i], mavenVersions[i + 1])) + } + return listWithArguments.stream() + } + + @ParameterizedTest + @MethodSource("argumentsForCompareVersions") + fun `compare Versions`(first: String, second: String) { + val firstVer = SimpleVersion(first).toSemVer() + val secondVer = SimpleVersion(second).toSemVer() + firstVer.compareTo(secondVer) shouldBeGreaterThan 0 + } +} diff --git a/e2e/src/test/BUILD.bazel b/e2e/src/test/BUILD.bazel index 79a30629..90eae2a4 100644 --- a/e2e/src/test/BUILD.bazel +++ b/e2e/src/test/BUILD.bazel @@ -1,6 +1,4 @@ -kt_junit5_test( - name = "e2e", - size = "medium", +integration_tests( srcs = glob(["kotlin/**/*.kt"]), resources = glob( ["resources/**"], @@ -11,5 +9,3 @@ kt_junit5_test( "@maven//:commons_io_commons_io", ], ) - -lint(srcs = glob(["kotlin/**/*.kt"])) diff --git a/e2e/src/test/kotlin/org/virtuslab/bazelsteward/e2e/E2EBase.kt b/e2e/src/test/kotlin/org/virtuslab/bazelsteward/e2e/E2EBase.kt index 7096d67b..7556913f 100644 --- a/e2e/src/test/kotlin/org/virtuslab/bazelsteward/e2e/E2EBase.kt +++ b/e2e/src/test/kotlin/org/virtuslab/bazelsteward/e2e/E2EBase.kt @@ -45,14 +45,44 @@ open class E2EBase { return finalFile } - protected fun checkBranches(tempDir: File, testResourcePath: String, branches: List) { + protected fun checkBranchesWithVersions(tempDir: File, testResourcePath: String, branches: List) { val localRepo = File(File(tempDir, "local"), testResourcePath) val remoteRepo = File(tempDir, "remote") - checkForBranches(localRepo, branches) - checkForBranches(remoteRepo, branches) + checkForBranchesWithVersions(localRepo, branches) + checkForBranchesWithVersions(remoteRepo, branches) - val git = GitClient(localRepo) + checkStatusOfBranches(localRepo, branches) + } + + protected fun checkBranchesWithoutVersions(tempDir: File, testResourcePath: String, branchesPattern: List) { + val localRepo = File(File(tempDir, "local"), testResourcePath) + val remoteRepo = File(tempDir, "remote") + + checkForBranchesWithoutVersions(localRepo, branchesPattern) + checkForBranchesWithoutVersions(remoteRepo, branchesPattern) + + val git = GitClient(tempDir) + val gitBranches = runBlocking { git.showRef(heads = true) } + checkStatusOfBranches(localRepo, gitBranches) + } + + private fun checkForBranchesWithoutVersions(tempDir: File, requiredBranches: List) { + val git = GitClient(tempDir) + val gitBranches = runBlocking { git.showRef(heads = true) } + + Assertions.assertThat(gitBranches).hasSameSizeAs(requiredBranches) + Assertions.assertThat(requiredBranches.all { branch -> gitBranches.any { it.contains(branch) } }).isTrue + } + + private fun checkForBranchesWithVersions(tempDir: File, branches: List) { + val git = GitClient(tempDir) + val gitBranches = runBlocking { git.showRef(heads = true) } + Assertions.assertThat(gitBranches).containsExactlyInAnyOrderElementsOf(branches) + } + + private fun checkStatusOfBranches(tempDir: File, branches: List) { + val git = GitClient(tempDir) runBlocking { branches.forEach { branchRef -> val branch = branchRef.removePrefix(heads) @@ -64,10 +94,4 @@ open class E2EBase { } } } - - private fun checkForBranches(tempDir: File, branches: List) { - val git = GitClient(tempDir) - val gitBranches = runBlocking { git.showRef(heads = true) } - Assertions.assertThat(gitBranches).containsExactlyInAnyOrderElementsOf(branches) - } } diff --git a/e2e/src/test/kotlin/org/virtuslab/bazelsteward/e2e/MavenE2E.kt b/e2e/src/test/kotlin/org/virtuslab/bazelsteward/e2e/MavenE2E.kt deleted file mode 100644 index 81e60624..00000000 --- a/e2e/src/test/kotlin/org/virtuslab/bazelsteward/e2e/MavenE2E.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.virtuslab.bazelsteward.e2e - -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.io.TempDir -import org.virtuslab.bazelsteward.app.Main -import java.io.File - -class MavenE2E : E2EBase() { - - @Test - fun `Maven trivial local test`(@TempDir tempDir: File) { - val testResourcePath = "maven/trivial" - val file = loadTest(tempDir, testResourcePath) - Main.main(args = arrayOf(file.toString(), "-p")) - val expectedBranches = listOf("arrow-core", "arrow-fx-coroutines").map { "$branchRef/$it/1.1.3" } + masterRef - checkBranches(tempDir, testResourcePath, expectedBranches) - } -} diff --git a/e2e/src/test/kotlin/org/virtuslab/bazelsteward/e2e/MavenE2ETest.kt b/e2e/src/test/kotlin/org/virtuslab/bazelsteward/e2e/MavenE2ETest.kt new file mode 100644 index 00000000..ebde8be0 --- /dev/null +++ b/e2e/src/test/kotlin/org/virtuslab/bazelsteward/e2e/MavenE2ETest.kt @@ -0,0 +1,27 @@ +package org.virtuslab.bazelsteward.e2e + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir +import org.virtuslab.bazelsteward.app.Main +import java.io.File + +class MavenE2ETest : E2EBase() { + + @Test + fun `Maven trivial local test`(@TempDir tempDir: File) { + val testResourcePath = "maven/trivial" + val file = loadTest(tempDir, testResourcePath) + Main.main(args = arrayOf(file.toString(), "--pushToRemote")) + val expectedBranches = listOf("arrow-core", "arrow-fx-coroutines").map { "$branchRef/$it/1.1.5" } + masterRef + checkBranchesWithVersions(tempDir, testResourcePath, expectedBranches) + } + + @Test + fun `Check dependency update not in maven central repository`(@TempDir tempDir: File) { + val testResourcePath = "maven/external" + val file = loadTest(tempDir, testResourcePath) + Main.main(args = arrayOf(file.toString(), "--pushToRemote")) + val expectedBranches = listOf("$branchRef/utilis", masterRef) + checkBranchesWithoutVersions(tempDir, testResourcePath, expectedBranches) + } +} diff --git a/e2e/src/test/resources/maven/external/.bazelrc b/e2e/src/test/resources/maven/external/.bazelrc new file mode 100644 index 00000000..d4d79d29 --- /dev/null +++ b/e2e/src/test/resources/maven/external/.bazelrc @@ -0,0 +1,2 @@ +build --java_language_version=11 +build --tool_java_language_version=11 diff --git a/e2e/src/test/resources/maven/external/.bazelversion b/e2e/src/test/resources/maven/external/.bazelversion new file mode 100644 index 00000000..c7cb1311 --- /dev/null +++ b/e2e/src/test/resources/maven/external/.bazelversion @@ -0,0 +1 @@ +5.3.1 diff --git a/e2e/src/test/resources/maven/external/BUILD.bzlignore b/e2e/src/test/resources/maven/external/BUILD.bzlignore new file mode 100644 index 00000000..fb5e6204 --- /dev/null +++ b/e2e/src/test/resources/maven/external/BUILD.bzlignore @@ -0,0 +1,8 @@ +load("@io_bazel_rules_kotlin//kotlin:core.bzl", "define_kt_toolchain") + +define_kt_toolchain( + name = "kotlin_toolchain", + api_version = "1.6", + jvm_target = "11", + language_version = "1.6", +) diff --git a/e2e/src/test/resources/maven/external/WORKSPACE.bzlignore b/e2e/src/test/resources/maven/external/WORKSPACE.bzlignore new file mode 100644 index 00000000..4c559d91 --- /dev/null +++ b/e2e/src/test/resources/maven/external/WORKSPACE.bzlignore @@ -0,0 +1,79 @@ +workspace(name = "maven-external") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +# rules_jvm_external - for maven dependencies +RULES_JVM_EXTERNAL_TAG = "4.4.2" + +RULES_JVM_EXTERNAL_SHA = "735602f50813eb2ea93ca3f5e43b1959bd80b213b836a07a62a29d757670b77b" + +http_archive( + name = "rules_jvm_external", + sha256 = RULES_JVM_EXTERNAL_SHA, + strip_prefix = "rules_jvm_external-{}".format(RULES_JVM_EXTERNAL_TAG), + url = "https://github.com/bazelbuild/rules_jvm_external/archive/{}.zip".format(RULES_JVM_EXTERNAL_TAG), +) + +IO_BAZEL_KOTLIN_RULES_TAG = "v1.6.0" + +IO_BAZEL_KOTLIN_RULES_SHA = "a57591404423a52bd6b18ebba7979e8cd2243534736c5c94d35c89718ea38f94" + +http_archive( + name = "io_bazel_rules_kotlin", + sha256 = IO_BAZEL_KOTLIN_RULES_SHA, + url = "https://github.com/bazelbuild/rules_kotlin/releases/download/{}/rules_kotlin_release.tgz".format(IO_BAZEL_KOTLIN_RULES_TAG), +) + +load("@io_bazel_rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") + +kotlin_repositories() + +register_toolchains("//:kotlin_toolchain") + +# bazel_skylib - starlark functions +BAZEL_SKYLIB_TAG = "1.3.0" + +BAZEL_SKYLIB_SHA = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506" + +http_archive( + name = "bazel_skylib", + sha256 = BAZEL_SKYLIB_SHA, + url = "https://github.com/bazelbuild/bazel-skylib/releases/download/{}/bazel-skylib-{}.tar.gz".format(BAZEL_SKYLIB_TAG, BAZEL_SKYLIB_TAG), +) + +# io_bazel_rules_scala - required to avoid cyclic init error +IO_BAZEL_RULES_SCALA_TAG = "20220201" + +IO_BAZEL_RULES_SCALA_SHA = "77a3b9308a8780fff3f10cdbbe36d55164b85a48123033f5e970fdae262e8eb2" + +http_archive( + name = "io_bazel_rules_scala", + sha256 = IO_BAZEL_RULES_SCALA_SHA, + strip_prefix = "rules_scala-{}".format(IO_BAZEL_RULES_SCALA_TAG), + url = "https://github.com/bazelbuild/rules_scala/releases/download/{}/rules_scala-{}.zip".format(IO_BAZEL_RULES_SCALA_TAG, IO_BAZEL_RULES_SCALA_TAG), +) + +load("@io_bazel_rules_scala//:scala_config.bzl", "scala_config") + +scala_config(scala_version = "2.13.6") + +load("@io_bazel_rules_scala//scala:toolchains.bzl", "scala_register_toolchains") + +scala_register_toolchains() + +load("@io_bazel_rules_scala//scala:scala.bzl", "scala_repositories") + +scala_repositories() + +# maven deps +load("@rules_jvm_external//:defs.bzl", "maven_install") + +maven_install( + artifacts = [ + "com.7theta:utilis:2.3.5" + ], + fetch_sources = False, + repositories = [ + "https://clojars.org/repo", + ], +) diff --git a/github/src/main/kotlin/org/virtuslab/bazelsteward/github/BUILD.bazel b/github/src/main/kotlin/org/virtuslab/bazelsteward/github/BUILD.bazel index e4466eff..e6c75917 100644 --- a/github/src/main/kotlin/org/virtuslab/bazelsteward/github/BUILD.bazel +++ b/github/src/main/kotlin/org/virtuslab/bazelsteward/github/BUILD.bazel @@ -1,11 +1,7 @@ -kt_jvm_library( - name = "github", +library( srcs = glob(["**/*.kt"]), - visibility = ["//visibility:public"], deps = [ "//core", "@maven//:org_kohsuke_github_api", ], ) - -lint(srcs = glob(["**/*.kt"])) diff --git a/maven/src/main/kotlin/org/virtuslab/bazelsteward/maven/BUILD.bazel b/maven/src/main/kotlin/org/virtuslab/bazelsteward/maven/BUILD.bazel index 9b22cf0e..f029c69e 100644 --- a/maven/src/main/kotlin/org/virtuslab/bazelsteward/maven/BUILD.bazel +++ b/maven/src/main/kotlin/org/virtuslab/bazelsteward/maven/BUILD.bazel @@ -1,11 +1,7 @@ -kt_jvm_library( - name = "maven", +library( srcs = glob(["**/*.kt"]), - visibility = ["//visibility:public"], deps = [ "//core", "@maven//:io_get_coursier_interface", ], ) - -lint(srcs = glob(["**/*.kt"])) diff --git a/maven/src/test/kotlin/org/virtuslab/bazelsteward/maven/BUILD.bazel b/maven/src/test/kotlin/org/virtuslab/bazelsteward/maven/BUILD.bazel index cd5f380b..fd39c0b2 100644 --- a/maven/src/test/kotlin/org/virtuslab/bazelsteward/maven/BUILD.bazel +++ b/maven/src/test/kotlin/org/virtuslab/bazelsteward/maven/BUILD.bazel @@ -1,11 +1,7 @@ -kt_junit5_test( - name = "maven", - size = "small", +unit_tests( srcs = glob(["**/*.kt"]), test_package = "org.virtuslab.bazelsteward.maven", deps = [ "//maven/src/main/kotlin/org/virtuslab/bazelsteward/maven", ], ) - -lint(srcs = glob(["**/*.kt"])) diff --git a/tools/build_rules/lint.bzl b/tools/build_rules/lint.bzl index 358569b8..69e8d33c 100644 --- a/tools/build_rules/lint.bzl +++ b/tools/build_rules/lint.bzl @@ -5,6 +5,7 @@ def lint(srcs): name = "lint_test", srcs = srcs, config = "//:lint_config", + tags = ["lint"], ) ktlint_fix( diff --git a/tools/build_rules/prelude_bazel b/tools/build_rules/prelude_bazel index b8b75225..3d1ba49c 100644 --- a/tools/build_rules/prelude_bazel +++ b/tools/build_rules/prelude_bazel @@ -1,4 +1,4 @@ -load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") load("@rules_java//java:defs.bzl", "java_binary") load("//tools/build_rules:junit5.bzl", "kt_junit5_test") load("//tools/build_rules:lint.bzl", "lint") +load("//tools/build_rules:rules.bzl", "library", "unit_tests", "integration_tests") diff --git a/tools/build_rules/rules.bzl b/tools/build_rules/rules.bzl new file mode 100644 index 00000000..b03ff53a --- /dev/null +++ b/tools/build_rules/rules.bzl @@ -0,0 +1,36 @@ +load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") +load("//tools/build_rules:lint.bzl", "lint") +load("//tools/build_rules:junit5.bzl", "kt_junit5_test") + +def default_target_name(): + _, _, target_name = native.package_name().rpartition("/") + return target_name + +def library(**kwargs): + if "name" not in kwargs: + kwargs["name"] = default_target_name() + if "visibility" not in kwargs: + kwargs["visibility"] = ["//visibility:public"] + + kt_jvm_library(**kwargs) + lint(kwargs["srcs"]) + +def unit_tests(**kwargs): + if "name" not in kwargs: + kwargs["name"] = default_target_name() + if "tags" not in kwargs: + kwargs["tags"] = [] + kwargs["tags"].append("unit") + + kt_junit5_test(size = "small", **kwargs) + lint(kwargs["srcs"]) + +def integration_tests(**kwargs): + if "name" not in kwargs: + kwargs["name"] = default_target_name() + if "tags" not in kwargs: + kwargs["tags"] = [] + kwargs["tags"].append("integration") + + kt_junit5_test(size = "medium", **kwargs) + lint(kwargs["srcs"]) diff --git a/tools/lint_kotlin.sh b/tools/lint_kotlin.sh index 765dda72..d57ca4be 100755 --- a/tools/lint_kotlin.sh +++ b/tools/lint_kotlin.sh @@ -3,4 +3,6 @@ set -u -e -o pipefail for target in $(bazel query --noshow_progress --output=label "kind('ktlint_fix rule', //...)"); do bazel run "$target" -done \ No newline at end of file +done + +bazel build //... \ No newline at end of file