| 1 | import java.security.MessageDigest |
| 2 | import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType |
| 3 | import org.jetbrains.intellij.platform.gradle.TestFrameworkType |
| 4 | |
| 5 | plugins { |
| 6 | java |
| 7 | id("org.jetbrains.intellij.platform") version "2.19.0" |
| 8 | } |
| 9 | |
| 10 | group = "com.diffinery" |
| 11 | version = providers.gradleProperty("pluginVersion").get() |
| 12 | |
| 13 | repositories { |
| 14 | intellijPlatform { |
| 15 | localPlatformArtifacts() |
| 16 | defaultRepositories() |
| 17 | } |
| 18 | // Maven Central through JetBrains' cache: Central rate-limits busy networks with 429s. |
| 19 | maven("https://cache-redirector.jetbrains.com/maven-central") |
| 20 | } |
| 21 | |
| 22 | dependencies { |
| 23 | intellijPlatform { |
| 24 | val local = providers.gradleProperty("ideaHome").orNull |
| 25 | if (local != null) local(local) else intellijIdea(providers.gradleProperty("platformVersion")) |
| 26 | bundledPlugin("Git4Idea") |
| 27 | bundledModule("intellij.platform.vcs.dvcs") |
| 28 | bundledModule("intellij.platform.vcs.dvcs.impl") |
| 29 | bundledModule("intellij.platform.vcs.impl") |
| 30 | testFramework(TestFrameworkType.Platform) |
| 31 | } |
| 32 | testImplementation("junit:junit:4.13.2") |
| 33 | testImplementation("org.opentest4j:opentest4j:1.3.0") |
| 34 | } |
| 35 | |
| 36 | // Compiled with the JDK the IDE runs on, for Java 21: IDEs from 2024.3 on run it. |
| 37 | tasks.withType<JavaCompile>().configureEach { options.release = 21 } |
| 38 | |
| 39 | intellijPlatform { |
| 40 | buildSearchableOptions = false |
| 41 | instrumentCode = false |
| 42 | // ./gradlew verifyPlugin checks the plugin against the oldest IDE it supports and the ones since, with JetBrains' |
| 43 | // Plugin Verifier (the same check the Marketplace runs). |
| 44 | pluginVerification { |
| 45 | ides { |
| 46 | create(IntelliJPlatformType.IntellijIdeaCommunity, "2024.3.6") |
| 47 | create(IntelliJPlatformType.IntellijIdeaCommunity, "2025.2.4") |
| 48 | create(IntelliJPlatformType.IntellijIdeaUltimate, providers.gradleProperty("platformVersion")) |
| 49 | } |
| 50 | } |
| 51 | pluginConfiguration { |
| 52 | version = providers.gradleProperty("pluginVersion") |
| 53 | ideaVersion { |
| 54 | sinceBuild = providers.gradleProperty("sinceBuild") |
| 55 | untilBuild = provider { null } |
| 56 | } |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | // A fingerprint of src/main inside the plugin, and its version for the plugin's User-Agent. Diffinery's server build (apps/diffinery/scripts/build-docs.mjs) |
| 61 | // recomputes it from the sources and refuses a dist/ zip that was built from older ones. |
| 62 | val sourcesFingerprint = tasks.register("sourcesFingerprint") { |
| 63 | val src = layout.projectDirectory.dir("src/main") |
| 64 | val out = layout.buildDirectory.dir("generated/fingerprint") |
| 65 | val pluginVersion = version.toString() |
| 66 | inputs.dir(src) |
| 67 | inputs.property("version", pluginVersion) |
| 68 | outputs.dir(out) |
| 69 | doLast { |
| 70 | fun sha256(bytes: ByteArray) = MessageDigest.getInstance("SHA-256").digest(bytes).joinToString("") { "%02x".format(it) } |
| 71 | val root = src.asFile |
| 72 | val listing = root.walkTopDown().filter { it.isFile }.map { it.relativeTo(root).invariantSeparatorsPath }.sorted() |
| 73 | .joinToString("") { "$it ${sha256(File(root, it).readBytes())}\n" } |
| 74 | val file = out.get().file("META-INF/diffinery-sources.sha256").asFile |
| 75 | file.parentFile.mkdirs() |
| 76 | file.writeText(sha256(listing.toByteArray()) + "\n") |
| 77 | out.get().file("META-INF/diffinery-version.txt").asFile.writeText(pluginVersion + "\n") |
| 78 | } |
| 79 | } |
| 80 | sourceSets.main { resources.srcDir(sourcesFingerprint) } |
| 81 | |
| 82 | // dist/ holds the zip diffinery.com serves (checked in, since the server's build has no JDK): ./gradlew dist |
| 83 | tasks.register<Copy>("dist") { |
| 84 | from(tasks.named("buildPlugin")) |
| 85 | into(layout.projectDirectory.dir("dist")) |
| 86 | doFirst { layout.projectDirectory.dir("dist").asFile.listFiles()?.filter { it.name.endsWith(".zip") }?.forEach { it.delete() } } |
| 87 | } |
| 88 | |
| 89 | // The same sources make the same zip, so dist/ only changes when the plugin does. |
| 90 | tasks.withType<AbstractArchiveTask>().configureEach { |
| 91 | isPreserveFileTimestamps = false |
| 92 | isReproducibleFileOrder = true |
| 93 | } |