This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git


The following commit(s) were added to refs/heads/master by this push:
     new fccb3e475c2 [improve][build] Keep release build info stable with a 
snapshot file (#26014)
fccb3e475c2 is described below

commit fccb3e475c2b7632eb26bbad9ac3bccaf329396f
Author: Lari Hotari <[email protected]>
AuthorDate: Sat Jun 13 08:16:48 2026 +0300

    [improve][build] Keep release build info stable with a snapshot file 
(#26014)
---
 gradle.properties                                  | 12 ++--
 pulsar-common/build.gradle.kts                     | 80 ++++++++++++++++------
 .../main/java/org/apache/pulsar/PulsarVersion.java |  2 +-
 3 files changed, 69 insertions(+), 25 deletions(-)

diff --git a/gradle.properties b/gradle.properties
index d370ce90209..4ddf1bce2bc 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -37,7 +37,11 @@ 
systemProp.org.gradle.internal.publish.checksums.insecure=true
 # default `false` in CI is important because changing the metadata would 
invalidate
 # the build cache entries that downstream jobs reuse.
 #
-# Release builds must include the real metadata — pass 
`-Ppulsar.includeBuildInfo=true`
-# on the gradle command line, or set `pulsar.includeBuildInfo=true` in
-# `~/.gradle/gradle.properties` before running the release build.
-pulsar.includeBuildInfo=false
+# Release builds must include the real metadata — pass 
`-PpulsarIncludeBuildInfo=true`
+# on the gradle command line, or export the 
`ORG_GRADLE_PROJECT_pulsarIncludeBuildInfo=true`
+# environment variable before running the release build. To keep the captured 
metadata
+# identical across the separate Gradle invocations of a release, also point the
+# `pulsarBuildInfoFile` property (e.g. via 
`ORG_GRADLE_PROJECT_pulsarBuildInfoFile`) at a
+# snapshot file outside the working tree: the metadata is captured into the 
file on the
+# first invocation and reused from it afterwards.
+pulsarIncludeBuildInfo=false
diff --git a/pulsar-common/build.gradle.kts b/pulsar-common/build.gradle.kts
index b3aa7c1bf2d..6e192df5a64 100644
--- a/pulsar-common/build.gradle.kts
+++ b/pulsar-common/build.gradle.kts
@@ -32,17 +32,29 @@ plugins {
 // git metadata only invalidate this module's processResources / jar tasks and 
do NOT trigger
 // a recompile of pulsar-common or any downstream module's compileJava.
 //
-// Set `pulsar.includeBuildInfo=false` (e.g. in `~/.gradle/gradle.properties`) 
to skip generation
-// entirely during development. PulsarVersion then returns placeholder values 
at runtime.
-val includeBuildInfo = providers.gradleProperty("pulsar.includeBuildInfo")
+// `pulsarIncludeBuildInfo` (default `false` in the root gradle.properties) 
controls whether the
+// git/build metadata is captured at all. Release builds enable it with
+// `-PpulsarIncludeBuildInfo=true` or the 
`ORG_GRADLE_PROJECT_pulsarIncludeBuildInfo=true`
+// environment variable. When disabled, PulsarVersion returns placeholder 
values at runtime.
+val includeBuildInfo = providers.gradleProperty("pulsarIncludeBuildInfo")
     .map { it.toBoolean() }
     .orElse(true)
 
+// `pulsarBuildInfoFile` (optional) points to a snapshot file that keeps the 
captured build
+// metadata identical across separate Gradle invocations (the release process 
runs several).
+// If the file exists, its entries are used as-is; otherwise the metadata is 
captured and
+// written to the file so that subsequent invocations reuse it. Without the 
snapshot, values
+// such as `git.build.time` change on every capture and invalidate the build 
outputs.
+// A relative path is resolved against the root project directory.
+val buildInfoFile = providers.gradleProperty("pulsarBuildInfoFile")
+    .map { rootDir.resolve(it) }
+
 val generatePulsarBuildInfo by tasks.registering {
     description = "Generates pulsar-version.properties with version and 
(optionally) git/build metadata."
     val outputFile = 
layout.buildDirectory.file("generated-resources/buildinfo/org/apache/pulsar/pulsar-version.properties")
     val projectVersion = project.version.toString()
     val includeBuildInfoValue = includeBuildInfo
+    val buildInfoFileValue = buildInfoFile.orNull
 
     // Lazy providers — evaluated at execution time only (no impact on 
configuration cache).
     val gitCommitId = providers.exec {
@@ -68,33 +80,61 @@ val generatePulsarBuildInfo by tasks.registering {
 
     inputs.property("version", projectVersion)
     inputs.property("includeBuildInfo", includeBuildInfoValue)
+    // The snapshot file contents take part in up-to-date checking so that 
pointing
+    // `pulsarBuildInfoFile` at a different snapshot regenerates the resource.
+    inputs.property("buildInfoFileContents", 
providers.fileContents(layout.file(buildInfoFile)).asText.orElse(""))
     outputs.file(outputFile)
 
     doLast {
-        val entries = linkedMapOf<String, String>()
-        entries["version"] = projectVersion
-        if (includeBuildInfoValue.get()) {
-            entries["git.commit.id"] = gitCommitId.getOrElse("")
-            entries["git.dirty"] = gitDirty.getOrElse("true")
-            entries["git.branch"] = gitBranch.getOrElse("")
-            entries["git.build.user.email"] = gitUserEmail.getOrElse("")
-            entries["git.build.user.name"] = gitUserName.getOrElse("")
-            entries["git.build.host"] = InetAddress.getLocalHost().hostName
-            entries["git.build.time"] =
-                
ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"))
-        }
-
-        val outFile = outputFile.get().asFile
-        outFile.parentFile.mkdirs()
         // Hand-rolled .properties writer so we don't get the 
non-deterministic timestamp comment
         // that java.util.Properties.store always emits. Values from 
git/InetAddress are ASCII
         // identifiers, so backslash escaping is sufficient.
-        outFile.writeText(buildString {
+        fun formatProperties(entries: Map<String, String>) = buildString {
             append("# Pulsar build info\n")
             entries.forEach { (key, value) ->
                 append(key).append('=').append(value.replace("\\", 
"\\\\")).append('\n')
             }
-        })
+        }
+
+        val entries = linkedMapOf<String, String>()
+        entries["version"] = projectVersion
+        if (includeBuildInfoValue.get()) {
+            val buildInfoEntries = linkedMapOf<String, String>()
+            if (buildInfoFileValue != null && buildInfoFileValue.exists()) {
+                // Reuse the previously captured snapshot so that the metadata 
stays identical
+                // across the multiple Gradle invocations of a release build.
+                buildInfoFileValue.readLines()
+                    .filter { it.isNotBlank() && !it.startsWith("#") }
+                    .forEach { line ->
+                        val separator = line.indexOf('=')
+                        if (separator > 0) {
+                            val key = line.substring(0, separator)
+                            // the version always comes from the project, 
never from the snapshot
+                            if (key != "version") {
+                                buildInfoEntries[key] = 
line.substring(separator + 1).replace("\\\\", "\\")
+                            }
+                        }
+                    }
+            } else {
+                buildInfoEntries["git.commit.id"] = gitCommitId.getOrElse("")
+                buildInfoEntries["git.dirty"] = gitDirty.getOrElse("true")
+                buildInfoEntries["git.branch"] = gitBranch.getOrElse("")
+                buildInfoEntries["git.build.user.email"] = 
gitUserEmail.getOrElse("")
+                buildInfoEntries["git.build.user.name"] = 
gitUserName.getOrElse("")
+                buildInfoEntries["git.build.host"] = 
InetAddress.getLocalHost().hostName
+                buildInfoEntries["git.build.time"] =
+                    
ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"))
+                if (buildInfoFileValue != null) {
+                    buildInfoFileValue.parentFile?.mkdirs()
+                    
buildInfoFileValue.writeText(formatProperties(buildInfoEntries))
+                }
+            }
+            entries += buildInfoEntries
+        }
+
+        val outFile = outputFile.get().asFile
+        outFile.parentFile.mkdirs()
+        outFile.writeText(formatProperties(entries))
     }
 }
 
diff --git a/pulsar-common/src/main/java/org/apache/pulsar/PulsarVersion.java 
b/pulsar-common/src/main/java/org/apache/pulsar/PulsarVersion.java
index ea144cdf359..ae9a0281af7 100644
--- a/pulsar-common/src/main/java/org/apache/pulsar/PulsarVersion.java
+++ b/pulsar-common/src/main/java/org/apache/pulsar/PulsarVersion.java
@@ -29,7 +29,7 @@ import java.util.regex.Pattern;
  *
  * <p>Values are loaded from {@code 
/org/apache/pulsar/pulsar-version.properties}, a resource
  * generated by the build only when the build info is included (controlled by 
the
- * {@code pulsar.includeBuildInfo} Gradle property). During development the 
resource is typically
+ * {@code pulsarIncludeBuildInfo} Gradle property). During development the 
resource is typically
  * absent, in which case this class returns {@link #UNKNOWN} placeholders.
  */
 public class PulsarVersion {

Reply via email to