diff --git a/build.gradle.kts b/build.gradle.kts index 4a33812..7f94567 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ plugins { id("java") alias(libs.plugins.kotlinJvm) alias(libs.plugins.changelog) - id("org.jetbrains.intellij.platform") version "2.10.4" // See https://github.com/JetBrains/intellij-platform-gradle-plugin/releases + id("org.jetbrains.intellij.platform") version "2.11.0" // See https://github.com/JetBrains/intellij-platform-gradle-plugin/releases id("me.filippov.gradle.jvm.wrapper") version "0.14.0" } @@ -49,6 +49,36 @@ tasks.wrapper { version = extra["PluginVersion"] as String + +// ============ Plugin File Definitions ============== + +val pluginStagingDir = layout.buildDirectory.dir("plugin-staging").get().asFile +val pluginContentDir = file("${pluginStagingDir}/${rootProject.name}") +val signingManifestFile = file("${pluginStagingDir}/files-to-sign.txt") + +val dotNetSrcDir = File(projectDir, "src/dotnet") +val dotNetOutputDir = "${dotNetSrcDir}/${DotnetPluginId}/bin/${DotnetPluginId}.Rider/${BuildConfiguration}" + +// All .NET files to include in the plugin +val dotNetOutputFiles = listOf( + "${DotnetPluginId}.dll", + "${DotnetPluginId}.pdb", + "Microsoft.Extensions.Caching.Abstractions.dll", + "Microsoft.Extensions.Caching.Memory.dll", +) + +// .NET files that need signing (only our own code, not third-party dependencies) +val dotNetFilesToSign = listOf( + "${DotnetPluginId}.dll", +) + +// JAR files that need signing (only our own code, not third-party dependencies) +val jarFilesToSign = listOf( + "${rootProject.name}-${version}.jar", + "${rootProject.name}-${version}-searchableOptions.jar", +) + + tasks.processResources { from("dependencies.json") { into("META-INF") } } @@ -85,7 +115,6 @@ tasks.test { useTestNG() } -val dotNetSrcDir = File(projectDir, "src/dotnet") val riderSdkPath by lazy { val path = intellijPlatform.platformPath.resolve("lib/DotNetSdkForRdPlugins").absolute() @@ -143,11 +172,118 @@ val testDotNet by tasks.registering { } } -tasks.buildPlugin { - dependsOn(compileDotNet) +// ========= Two-Phase Build for Signing Support ================ + +// Preparation for signing. Build all dll's and jar's and puts them into ${pluginStagingDir} +val preparePluginForSigning by tasks.registering { + description = "Prepares plugin files for signing and generates signing manifest" + group = "build" + + // Mirror BuildPluginTask dependencies: + // - prepareSandbox provides the main plugin directory + // - jarSearchableOptions provides the searchable options JAR + dependsOn(tasks.prepareSandbox) + dependsOn(tasks.jarSearchableOptions) + + // Clean staging directory FIRST to prevent poisoning + doFirst { + delete(pluginStagingDir) + mkdir(pluginStagingDir) + } + + doLast { + // Step 1: Copy from prepareSandbox.pluginDirectory (same as BuildPluginTask) + val sandboxPluginDir = tasks.prepareSandbox.get().pluginDirectory.get().asFile + copy { + from(sandboxPluginDir) + into(pluginContentDir) + } + + // Step 2: Copy searchable options JAR to lib/ (same as BuildPluginTask) + copy { + from(tasks.jarSearchableOptions.get().archiveFile) + into(file("${pluginContentDir}/lib")) + } + + // Step 3: Generate signing manifest + val filesToSign = mutableListOf() + + // Add JAR files that need signing (only our own code) + jarFilesToSign.forEach { jarName -> + filesToSign.add("${rootProject.name}/lib/${jarName}") + } + + // Add .NET files that need signing + dotNetFilesToSign.forEach { fileName -> + filesToSign.add("${rootProject.name}/dotnet/${fileName}") + } + + // Write manifest + signingManifestFile.writeText(filesToSign.joinToString("\n")) + + // Summary + println("Plugin prepared for signing: ${pluginStagingDir}") + println("Signing manifest: ${signingManifestFile}") + println("Files to sign: ${filesToSign.size}") + filesToSign.forEach { println(" - $it") } + } +} + +// Validates that ${pluginStagingDir} has all required files to assemble the plugin +val validatePluginStaging by tasks.registering { + description = "Validates that plugin staging directory exists and contains required files" + group = "build" + doLast { + if (!pluginContentDir.exists()) { + throw RuntimeException( + "Plugin staging directory not found: ${pluginContentDir}\n" + + "Run './gradlew preparePluginForSigning' first." + ) + } + + // Validate expected .NET output files exist + dotNetOutputFiles.forEach { fileName -> + val file = file("${pluginContentDir}/dotnet/${fileName}") + if (!file.exists()) throw RuntimeException("Expected .NET file not found: ${file}") + } + + // Validate expected JAR files exist + jarFilesToSign.forEach { jarName -> + val file = file("${pluginContentDir}/lib/${jarName}") + if (!file.exists()) throw RuntimeException("Expected JAR file not found: ${file}") + } + } +} + +// Assembles the final zip-archive by taking the files from ${pluginStagingDir} +val assemblePlugin by tasks.registering(Zip::class) { + description = "Assembles the final plugin ZIP from staged files" + group = "build" + + dependsOn(validatePluginStaging) + + from(pluginStagingDir) { + // Include plugin content directory + include("${rootProject.name}/**") + // Exclude signing manifest from final ZIP + exclude("files-to-sign.txt") + } + + // Use same naming convention as original BuildPluginTask: + // archiveBaseName comes from plugin.xml (via IntelliJ plugin extension) + archiveBaseName.convention(intellijPlatform.projectName) + destinationDirectory.set(layout.buildDirectory.dir("distributions")) + + // Register artifact to INTELLIJ_PLATFORM_DISTRIBUTION configuration (same as original BuildPluginTask) + // This ensures publishPlugin and other dependent tasks can find the archive + val intellijPlatformDistributionConfiguration = configurations[Constants.Configurations.INTELLIJ_PLATFORM_DISTRIBUTION] + artifacts.add(intellijPlatformDistributionConfiguration.name, this) + + doLast { + // Copy to output directory copy { - from("${buildDir}/distributions/${rootProject.name}-${version}.zip") + from(archiveFile) into("${rootDir}/output") } @@ -163,6 +299,24 @@ tasks.buildPlugin { args("pack", "${DotnetSolution}", "--configuration", BuildConfiguration, "--output", "${rootDir}/output", "/p:PackageReleaseNotes=${changeNotes}", "/p:PackageVersion=${version}") workingDir(rootDir) } + + println("Plugin assembled: output/${rootProject.name}-${version}.zip") + } +} + +tasks.buildPlugin { + // Disable the IntelliJ plugin's default buildPlugin behavior for ZIP creation + // and make it use our two-phase approach instead + actions.clear() + + // Make it depend on our assembly task + dependsOn(preparePluginForSigning) + dependsOn(assemblePlugin) + + // Phase 2 (assemblePlugin + all its dependencies) must run after Phase 1 + assemblePlugin.get().mustRunAfter(preparePluginForSigning) + assemblePlugin.get().taskDependencies.getDependencies(assemblePlugin.get()).forEach { task -> + task.mustRunAfter(preparePluginForSigning) } } @@ -244,24 +398,19 @@ tasks.patchPluginXml { tasks.prepareSandbox { dependsOn(compileDotNet) - val outputFolder = "${rootDir}/src/dotnet/${DotnetPluginId}/bin/${DotnetPluginId}.Rider/${BuildConfiguration}" - val dllFiles = listOf( - "$outputFolder/${DotnetPluginId}.dll", - "$outputFolder/${DotnetPluginId}.pdb", - "$outputFolder/Microsoft.Extensions.Caching.Abstractions.dll", - "$outputFolder/Microsoft.Extensions.Caching.Memory.dll", - ) - - dllFiles.forEach({ f -> - val file = file(f) - from(file, { into("${rootProject.name}/dotnet") }) - }) + // Use shared .NET output files list + dotNetOutputFiles.forEach { fileName -> + from("${dotNetOutputDir}/${fileName}") { + into("${rootProject.name}/dotnet") + } + } doLast { - dllFiles.forEach({ f -> - val file = file(f) + // Validation: ensure all .NET output files exist + dotNetOutputFiles.forEach { fileName -> + val file = file("${dotNetOutputDir}/${fileName}") if (!file.exists()) throw RuntimeException("File ${file} does not exist") - }) + } } } diff --git a/settings.gradle b/settings.gradle index 60ed53d..9806cbe 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,9 +1,9 @@ pluginManagement { // Provide repositories to resolve plugins repositories { - maven { url "https://cache-redirector.jetbrains.com/plugins.gradle.org" } - maven { url "https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap" } - maven { url "https://cache-redirector.jetbrains.com/myget.org.rd-snapshots.maven" } + maven { url = "https://cache-redirector.jetbrains.com/plugins.gradle.org" } + maven { url = "https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap" } + maven { url = "https://cache-redirector.jetbrains.com/myget.org.rd-snapshots.maven" } } resolutionStrategy { eachPlugin {