From fca9777595855cd129dba5fc92040acda16c36ba Mon Sep 17 00:00:00 2001 From: "Dmitriy.Panov" Date: Wed, 4 Sep 2024 21:07:23 +0200 Subject: [PATCH] build scripts: Android plugin versions should satisfy the semantic versioning or the development build number scheme IIP-27 IJ-CR-145591 (cherry picked from commit 1dfa1379d0e757c46db1611c8c132dac64ab7b69) GitOrigin-RevId: 021671d2345bc0e5b1d095fecab2206be72111eb --- .../build/CommunityRepositoryModules.kt | 1 + .../intellij/build/impl/BuildContextImpl.kt | 2 +- .../intellij/build/impl/PluginLayout.kt | 19 ++++++++++++++++++- .../intellij/build/impl/PluginXmlPatcher.kt | 16 +++++++++++++++- .../build/impl/SemanticVersioningScheme.kt | 10 ++++++++++ .../build/impl/SnapshotBuildNumber.kt | 16 ++++++++++++---- .../testFramework/buildScriptTestUtils.kt | 6 +++++- 7 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 platform/build-scripts/src/org/jetbrains/intellij/build/impl/SemanticVersioningScheme.kt diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/CommunityRepositoryModules.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/CommunityRepositoryModules.kt index bbea9c4ebcfe..7a573e5ad285 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/CommunityRepositoryModules.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/CommunityRepositoryModules.kt @@ -290,6 +290,7 @@ object CommunityRepositoryModules { pluginAutoWithDeprecatedCustomDirName(mainModuleName) { spec -> spec.directoryName = "android" spec.mainJarName = "android.jar" + spec.semanticVersioning = true spec.withCustomVersion { pluginXmlSupplier, ideBuildVersion, _ -> val pluginXml = pluginXmlSupplier() if (pluginXml.indexOf("") != -1) { diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuildContextImpl.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuildContextImpl.kt index 40b52512976b..18fd7f3d13fd 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuildContextImpl.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BuildContextImpl.kt @@ -64,7 +64,7 @@ class BuildContextImpl internal constructor( override fun reportDistributionBuildNumber() { val suppliedBuildNumber = options.buildNumber - val baseBuildNumber = SnapshotBuildNumber.VALUE.removeSuffix(".SNAPSHOT") + val baseBuildNumber = SnapshotBuildNumber.BASE check(suppliedBuildNumber == null || suppliedBuildNumber.startsWith(baseBuildNumber)) { "Supplied build number '$suppliedBuildNumber' is expected to start with '$baseBuildNumber' base build number " + "defined in ${SnapshotBuildNumber.PATH}" diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PluginLayout.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PluginLayout.kt index f4c79b1df332..e9ca8d0dbfd4 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PluginLayout.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PluginLayout.kt @@ -84,6 +84,13 @@ class PluginLayout private constructor( var retainProductDescriptorForBundledPlugin: Boolean = false var enableSymlinksAndExecutableResources: Boolean = false + /** + * Should be `true` if the semantic versioning is enabled for the plugin in plugins.jetbrains.com. + * Then the plugin version will be checked against [org.jetbrains.intellij.build.impl.SemanticVersioningScheme]. + */ + var semanticVersioning: Boolean = false + private set + @JvmField internal var modulesWithExcludedModuleLibraries: Set = persistentSetOf() @@ -264,6 +271,14 @@ class PluginLayout private constructor( val mainModule get() = layout.mainModule + /** + * @see [PluginLayout.semanticVersioning] + */ + var semanticVersioning: Boolean = false + set(value) { + layout.semanticVersioning = value + } + var mainJarName: String get() = layout.mainJarName /** @@ -408,12 +423,14 @@ class PluginLayout private constructor( * Multiple invocations of this method will add corresponding plugin names to a list of name to be added to scramble classpath * * @param pluginMainModuleName - a name of the dependent plugin's directory, whose jars should be added to scramble classpath - * @param relativePath - a directory where jars should be searched (relative to plugin home directory, "lib" by default) */ fun scrambleClasspathPlugin(pluginMainModuleName: String) { layout.scrambleClasspathPlugins = layout.scrambleClasspathPlugins.add(ScrambleClasspathPluginEntry(pluginMainModuleName = pluginMainModuleName, relativePath = null)) } + /** + * @param relativePath - a directory where jars should be searched (relative to plugin home directory, "lib" by default) + */ fun scrambleClasspathPlugin(pluginId: String, relativePath: String) { layout.scrambleClasspathPlugins = layout.scrambleClasspathPlugins.add(ScrambleClasspathPluginEntry(pluginMainModuleName = pluginId, relativePath = relativePath)) } diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PluginXmlPatcher.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PluginXmlPatcher.kt index bfd5337c3d09..a826f6edb709 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PluginXmlPatcher.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/PluginXmlPatcher.kt @@ -59,7 +59,7 @@ internal suspend fun patchPluginXml( else -> CompatibleBuildRange.NEWER_WITH_SAME_BASELINE } - val pluginVersion = plugin.versionEvaluator.evaluate(pluginXmlSupplier = { descriptorContent }, ideBuildVersion = context.pluginBuildNumber, context = context) + val pluginVersion = getPluginVersion(plugin, descriptorContent, context) @Suppress("TestOnlyProblems") val content = try { val element = doPatchPluginXml( @@ -91,6 +91,20 @@ internal suspend fun patchPluginXml( moduleOutputPatcher.patchModuleOutput(moduleName = plugin.mainModule, path = "META-INF/plugin.xml", content = content, overwrite = PatchOverwriteMode.IF_EQUAL) } +private val DEV_BUILD_SCHEME: Regex = Regex("^${SnapshotBuildNumber.BASE.replace(".", "\\.")}\\.(SNAPSHOT|[0-9]+)$") + +private suspend fun getPluginVersion(plugin: PluginLayout, descriptorContent: String, context: BuildContext): PluginVersionEvaluatorResult { + val pluginVersion = plugin.versionEvaluator.evaluate(pluginXmlSupplier = { descriptorContent }, ideBuildVersion = context.pluginBuildNumber, context = context) + check( + !plugin.semanticVersioning || + SemanticVersioningScheme.matches(pluginVersion.pluginVersion) || + DEV_BUILD_SCHEME.matches(pluginVersion.pluginVersion) + ) { + "$plugin version '${pluginVersion.pluginVersion}' is expected to match either '$DEV_BUILD_SCHEME' or the Semantic Versioning, see https://semver.org" + } + return pluginVersion +} + @TestOnly fun doPatchPluginXml( rootElement: Element, diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/SemanticVersioningScheme.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/SemanticVersioningScheme.kt new file mode 100644 index 000000000000..606b2c0d0cbd --- /dev/null +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/SemanticVersioningScheme.kt @@ -0,0 +1,10 @@ +// This source code is based on the Semantic Versioning specification (https://semver.org/) originally authored by Tom Preston-Werner (https://tom.preston-werner.com/) and copyrighted under the Creative Commons Attribution 3.0 license (https://creativecommons.org/licenses/by/3.0/). +package org.jetbrains.intellij.build.impl + +internal object SemanticVersioningScheme { + private val SEM_VER_REGEX = 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 matches(version: String): Boolean { + return SEM_VER_REGEX.matches(version) + } +} \ No newline at end of file diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/SnapshotBuildNumber.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/SnapshotBuildNumber.kt index 021d3f6a2e0f..8ee244612af9 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/SnapshotBuildNumber.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/SnapshotBuildNumber.kt @@ -10,15 +10,23 @@ object SnapshotBuildNumber { BuildPaths.COMMUNITY_ROOT.communityRoot.resolve("build.txt") } + private const val SNAPSHOTS_SUFFIX = ".SNAPSHOT" + /** - * `${CURRENT_MAJOR_VERSION}.SNAPSHOT`, specified in [PATH] + * `${BASE}.SNAPSHOT`, specified in [PATH] */ val VALUE: String by lazy { val snapshotBuildNumber = Files.readString(PATH).trim() - val snapshotSuffix = ".SNAPSHOT" - check(snapshotBuildNumber.endsWith(snapshotSuffix)) { - "$snapshotBuildNumber is expected to have a '$snapshotSuffix' suffix" + check(snapshotBuildNumber.endsWith(SNAPSHOTS_SUFFIX)) { + "$PATH: '$snapshotBuildNumber' is expected to have a '$SNAPSHOTS_SUFFIX' suffix" } snapshotBuildNumber } + + /** + * [VALUE] without [SNAPSHOTS_SUFFIX] + */ + val BASE: String by lazy { + VALUE.removeSuffix(SNAPSHOTS_SUFFIX) + } } \ No newline at end of file diff --git a/platform/build-scripts/testFramework/src/com/intellij/platform/buildScripts/testFramework/buildScriptTestUtils.kt b/platform/build-scripts/testFramework/src/com/intellij/platform/buildScripts/testFramework/buildScriptTestUtils.kt index 7c1c5da88926..e8795ccf1175 100644 --- a/platform/build-scripts/testFramework/src/com/intellij/platform/buildScripts/testFramework/buildScriptTestUtils.kt +++ b/platform/build-scripts/testFramework/src/com/intellij/platform/buildScripts/testFramework/buildScriptTestUtils.kt @@ -19,6 +19,7 @@ import org.assertj.core.api.SoftAssertions import org.jetbrains.intellij.build.* import org.jetbrains.intellij.build.dependencies.TeamCityHelper.isUnderTeamCity import org.jetbrains.intellij.build.impl.BuildContextImpl +import org.jetbrains.intellij.build.impl.SnapshotBuildNumber import org.jetbrains.intellij.build.impl.buildDistributions import org.jetbrains.intellij.build.telemetry.JaegerJsonSpanExporterManager import org.jetbrains.intellij.build.telemetry.TraceManager @@ -55,7 +56,10 @@ private inline fun createBuildOptionsForTest(productProperties: ProductPropertie fun customizeBuildOptionsForTest(options: BuildOptions, outDir: Path, skipDependencySetup: Boolean = false, testInfo: TestInfo?) { options.skipDependencySetup = skipDependencySetup options.isTestBuild = true - + /** + * Differs from [SnapshotBuildNumber] to closer match the production + */ + options.buildNumber = "${SnapshotBuildNumber.BASE}.1" options.buildStepsToSkip += listOf( BuildOptions.LIBRARY_URL_CHECK_STEP, BuildOptions.TEAMCITY_ARTIFACTS_PUBLICATION_STEP,