From f4b3cc353c913036935f948b982a557edf00a610 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Fri, 21 Aug 2026 12:24:09 +0200 Subject: [PATCH 1/2] Apply the `revision` convention to `flume-parent` Every published module inherits from `flume-parent`, not from the aggregator, so the CI-friendly properties have to be declared there as well. Without them the release automation bumps only the aggregator and the artifacts ship a stale version and build timestamp. Assisted-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011QwMh1JvFaPBgWEGrMgMj7 --- flume-ng-auth/pom.xml | 2 +- flume-ng-channels/flume-file-channel/pom.xml | 2 +- .../flume-spillable-memory-channel/pom.xml | 2 +- flume-ng-channels/pom.xml | 2 +- .../flume-ng-config-filter-api/pom.xml | 2 +- .../pom.xml | 2 +- .../flume-ng-external-process-config-filter/pom.xml | 2 +- flume-ng-configfilters/pom.xml | 2 +- flume-ng-configuration/pom.xml | 2 +- flume-ng-core/pom.xml | 2 +- flume-ng-dist/pom.xml | 2 +- .../flume-ganglia-monitor/pom.xml | 2 +- flume-ng-instrumentation/flume-http-monitor/pom.xml | 2 +- .../flume-prometheus-monitor/pom.xml | 2 +- flume-ng-instrumentation/pom.xml | 2 +- flume-ng-node/pom.xml | 2 +- flume-ng-sdk/pom.xml | 2 +- flume-ng-sources/flume-http-source/pom.xml | 2 +- flume-ng-sources/flume-netcat-source/pom.xml | 2 +- flume-ng-sources/flume-syslog-source/pom.xml | 2 +- flume-ng-sources/flume-taildir-source/pom.xml | 2 +- flume-ng-sources/pom.xml | 2 +- flume-parent/pom.xml | 13 ++++++++----- flume-tools/pom.xml | 2 +- 24 files changed, 31 insertions(+), 28 deletions(-) diff --git a/flume-ng-auth/pom.xml b/flume-ng-auth/pom.xml index e288313d1e..b13267f33e 100644 --- a/flume-ng-auth/pom.xml +++ b/flume-ng-auth/pom.xml @@ -21,7 +21,7 @@ org.apache.flume flume-parent - 2.0.0-SNAPSHOT + ${revision} ../flume-parent/pom.xml diff --git a/flume-ng-channels/flume-file-channel/pom.xml b/flume-ng-channels/flume-file-channel/pom.xml index 245e4d5679..6183926cb8 100644 --- a/flume-ng-channels/flume-file-channel/pom.xml +++ b/flume-ng-channels/flume-file-channel/pom.xml @@ -21,7 +21,7 @@ org.apache.flume flume-ng-channels - 2.0.0-SNAPSHOT + ${revision} org.apache.flume.flume-ng-channels diff --git a/flume-ng-channels/flume-spillable-memory-channel/pom.xml b/flume-ng-channels/flume-spillable-memory-channel/pom.xml index b891a42aae..2fc1378d11 100644 --- a/flume-ng-channels/flume-spillable-memory-channel/pom.xml +++ b/flume-ng-channels/flume-spillable-memory-channel/pom.xml @@ -21,7 +21,7 @@ org.apache.flume flume-ng-channels - 2.0.0-SNAPSHOT + ${revision} org.apache.flume.flume-ng-channels diff --git a/flume-ng-channels/pom.xml b/flume-ng-channels/pom.xml index a8aeef90ba..42343fadbe 100644 --- a/flume-ng-channels/pom.xml +++ b/flume-ng-channels/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-parent - 2.0.0-SNAPSHOT + ${revision} ../flume-parent/pom.xml diff --git a/flume-ng-configfilters/flume-ng-config-filter-api/pom.xml b/flume-ng-configfilters/flume-ng-config-filter-api/pom.xml index 5f4b9cd6d9..0fa090bd43 100644 --- a/flume-ng-configfilters/flume-ng-config-filter-api/pom.xml +++ b/flume-ng-configfilters/flume-ng-config-filter-api/pom.xml @@ -20,7 +20,7 @@ org.apache.flume flume-ng-configfilters - 2.0.0-SNAPSHOT + ${revision} flume-ng-config-filter-api diff --git a/flume-ng-configfilters/flume-ng-environment-variable-config-filter/pom.xml b/flume-ng-configfilters/flume-ng-environment-variable-config-filter/pom.xml index 78c30b0c8f..f0d39f5d79 100644 --- a/flume-ng-configfilters/flume-ng-environment-variable-config-filter/pom.xml +++ b/flume-ng-configfilters/flume-ng-environment-variable-config-filter/pom.xml @@ -20,7 +20,7 @@ org.apache.flume flume-ng-configfilters - 2.0.0-SNAPSHOT + ${revision} flume-ng-environment-variable-config-filter diff --git a/flume-ng-configfilters/flume-ng-external-process-config-filter/pom.xml b/flume-ng-configfilters/flume-ng-external-process-config-filter/pom.xml index 339fdfdb21..1ae97a67e0 100644 --- a/flume-ng-configfilters/flume-ng-external-process-config-filter/pom.xml +++ b/flume-ng-configfilters/flume-ng-external-process-config-filter/pom.xml @@ -20,7 +20,7 @@ org.apache.flume flume-ng-configfilters - 2.0.0-SNAPSHOT + ${revision} flume-ng-external-process-config-filter diff --git a/flume-ng-configfilters/pom.xml b/flume-ng-configfilters/pom.xml index 3a219835d7..7c6a1c7480 100644 --- a/flume-ng-configfilters/pom.xml +++ b/flume-ng-configfilters/pom.xml @@ -20,7 +20,7 @@ org.apache.flume flume-parent - 2.0.0-SNAPSHOT + ${revision} ../flume-parent/pom.xml diff --git a/flume-ng-configuration/pom.xml b/flume-ng-configuration/pom.xml index 8ab25fd658..0ae2b9d21c 100644 --- a/flume-ng-configuration/pom.xml +++ b/flume-ng-configuration/pom.xml @@ -20,7 +20,7 @@ org.apache.flume flume-parent - 2.0.0-SNAPSHOT + ${revision} ../flume-parent/pom.xml flume-ng-configuration diff --git a/flume-ng-core/pom.xml b/flume-ng-core/pom.xml index e8b6a0d28c..3a9bbdd973 100644 --- a/flume-ng-core/pom.xml +++ b/flume-ng-core/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-parent - 2.0.0-SNAPSHOT + ${revision} ../flume-parent/pom.xml diff --git a/flume-ng-dist/pom.xml b/flume-ng-dist/pom.xml index 6e92bc1d6c..2faad49d51 100644 --- a/flume-ng-dist/pom.xml +++ b/flume-ng-dist/pom.xml @@ -21,7 +21,7 @@ org.apache.flume flume-parent - 2.0.0-SNAPSHOT + ${revision} ../flume-parent/pom.xml diff --git a/flume-ng-instrumentation/flume-ganglia-monitor/pom.xml b/flume-ng-instrumentation/flume-ganglia-monitor/pom.xml index 1911b1209f..cb357256b1 100644 --- a/flume-ng-instrumentation/flume-ganglia-monitor/pom.xml +++ b/flume-ng-instrumentation/flume-ganglia-monitor/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-ng-instrumentation - 2.0.0-SNAPSHOT + ${revision} org.apache.flume.flume-ng-instrumentation diff --git a/flume-ng-instrumentation/flume-http-monitor/pom.xml b/flume-ng-instrumentation/flume-http-monitor/pom.xml index 6c266c6863..e1e33e695f 100644 --- a/flume-ng-instrumentation/flume-http-monitor/pom.xml +++ b/flume-ng-instrumentation/flume-http-monitor/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-ng-instrumentation - 2.0.0-SNAPSHOT + ${revision} org.apache.flume.flume-ng-instrumentation diff --git a/flume-ng-instrumentation/flume-prometheus-monitor/pom.xml b/flume-ng-instrumentation/flume-prometheus-monitor/pom.xml index 27b90fda76..6d66a863e4 100644 --- a/flume-ng-instrumentation/flume-prometheus-monitor/pom.xml +++ b/flume-ng-instrumentation/flume-prometheus-monitor/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-ng-instrumentation - 2.0.0-SNAPSHOT + ${revision} org.apache.flume.flume-ng-instrumentation diff --git a/flume-ng-instrumentation/pom.xml b/flume-ng-instrumentation/pom.xml index e664afbbb3..1a049f90d2 100644 --- a/flume-ng-instrumentation/pom.xml +++ b/flume-ng-instrumentation/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-parent - 2.0.0-SNAPSHOT + ${revision} ../flume-parent/pom.xml diff --git a/flume-ng-node/pom.xml b/flume-ng-node/pom.xml index 40f6a4d090..5878f0f551 100644 --- a/flume-ng-node/pom.xml +++ b/flume-ng-node/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-parent - 2.0.0-SNAPSHOT + ${revision} ../flume-parent/pom.xml diff --git a/flume-ng-sdk/pom.xml b/flume-ng-sdk/pom.xml index bbe85ee1e3..6b231c2708 100644 --- a/flume-ng-sdk/pom.xml +++ b/flume-ng-sdk/pom.xml @@ -21,7 +21,7 @@ org.apache.flume flume-parent - 2.0.0-SNAPSHOT + ${revision} ../flume-parent/pom.xml diff --git a/flume-ng-sources/flume-http-source/pom.xml b/flume-ng-sources/flume-http-source/pom.xml index 43770d964c..d7f2b58500 100644 --- a/flume-ng-sources/flume-http-source/pom.xml +++ b/flume-ng-sources/flume-http-source/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-ng-sources - 2.0.0-SNAPSHOT + ${revision} org.apache.flume.flume-ng-sources diff --git a/flume-ng-sources/flume-netcat-source/pom.xml b/flume-ng-sources/flume-netcat-source/pom.xml index d329266189..5c8becc1a3 100644 --- a/flume-ng-sources/flume-netcat-source/pom.xml +++ b/flume-ng-sources/flume-netcat-source/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-ng-sources - 2.0.0-SNAPSHOT + ${revision} org.apache.flume.flume-ng-sources diff --git a/flume-ng-sources/flume-syslog-source/pom.xml b/flume-ng-sources/flume-syslog-source/pom.xml index e45b597f72..1f0beb2f9a 100644 --- a/flume-ng-sources/flume-syslog-source/pom.xml +++ b/flume-ng-sources/flume-syslog-source/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-ng-sources - 2.0.0-SNAPSHOT + ${revision} org.apache.flume.flume-ng-sources diff --git a/flume-ng-sources/flume-taildir-source/pom.xml b/flume-ng-sources/flume-taildir-source/pom.xml index 89edc39556..d371c0f3b4 100644 --- a/flume-ng-sources/flume-taildir-source/pom.xml +++ b/flume-ng-sources/flume-taildir-source/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-ng-sources - 2.0.0-SNAPSHOT + ${revision} org.apache.flume.flume-ng-sources diff --git a/flume-ng-sources/pom.xml b/flume-ng-sources/pom.xml index 3318e3fd2e..5e12ea542f 100644 --- a/flume-ng-sources/pom.xml +++ b/flume-ng-sources/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-parent - 2.0.0-SNAPSHOT + ${revision} ../flume-parent/pom.xml diff --git a/flume-parent/pom.xml b/flume-parent/pom.xml index f02aa175f0..335b5bbf60 100644 --- a/flume-parent/pom.xml +++ b/flume-parent/pom.xml @@ -28,7 +28,7 @@ org.apache.flume flume-parent - 2.0.0-SNAPSHOT + ${revision} pom Apache Flume Parent @@ -59,7 +59,7 @@ scm:git:https://gitbox.apache.org/repos/asf/logging-flume.git scm:git:https://gitbox.apache.org/repos/asf/logging-flume.git - rel/${project.version} + rel/${revision} https://gitbox.apache.org/repos/asf/logging-flume.git @@ -75,12 +75,15 @@ + 2.0.0-SNAPSHOT + - 2.0.0-SNAPSHOT + ${revision} true - - 2022-01-02T00:00:00Z + + 2026-01-01T00:00:00Z + UTF-8 diff --git a/flume-tools/pom.xml b/flume-tools/pom.xml index 7d58b3fb53..9d3d65fbcb 100644 --- a/flume-tools/pom.xml +++ b/flume-tools/pom.xml @@ -22,7 +22,7 @@ org.apache.flume flume-parent - 2.0.0-SNAPSHOT + ${revision} ../flume-parent/pom.xml From d32d9cb00b74c98b72d34737886f0adf9f1a94fe Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Fri, 21 Aug 2026 12:25:15 +0200 Subject: [PATCH 2/2] Derive the version metadata from the JAR manifest `saveVersion.sh` recorded the builder's user name, host, clock and a checksum of the working copy, so no two builds agreed and the source distribution, which carries no repository metadata, agreed with none. The same facts now come from POM-derived manifest headers, which a Git checkout and the source archive produce identically. `getUser()` and `getSrcChecksum()` are deprecated for removal, and `getRevision()` reports nothing until the build records a commit id again. Assisted-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011QwMh1JvFaPBgWEGrMgMj7 --- flume-ng-core/pom.xml | 111 --------- flume-ng-core/scripts/saveVersion.ps1 | 61 ----- flume-ng-core/scripts/saveVersion.sh | 69 ------ .../org/apache/flume/VersionAnnotation.java | 72 ------ .../org/apache/flume/tools/VersionInfo.java | 223 ++++++++++++++---- .../apache/flume/tools/TestVersionInfo.java | 138 +++++++++-- flume-ng-sdk/pom.xml | 14 ++ flume-parent/pom.xml | 10 + 8 files changed, 312 insertions(+), 386 deletions(-) delete mode 100644 flume-ng-core/scripts/saveVersion.ps1 delete mode 100755 flume-ng-core/scripts/saveVersion.sh delete mode 100644 flume-ng-core/src/main/java/org/apache/flume/VersionAnnotation.java diff --git a/flume-ng-core/pom.xml b/flume-ng-core/pom.xml index 3a9bbdd973..a26093c9d8 100644 --- a/flume-ng-core/pom.xml +++ b/flume-ng-core/pom.xml @@ -177,115 +177,4 @@ - - - not-windows - - - !Windows - - - - - - org.apache.maven.plugins - maven-antrun-plugin - - - generate-version - - run - - generate-sources - - - - - - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-source - - add-source - - generate-sources - - - target/generated-sources/java - - - - - - - - - - - - windows - - - Windows - - - - - - org.apache.maven.plugins - maven-antrun-plugin - - - generate-version - - run - - generate-sources - - - - - - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-source - - add-source - - generate-sources - - - target/generated-sources/java - - - - - - - - - - - - diff --git a/flume-ng-core/scripts/saveVersion.ps1 b/flume-ng-core/scripts/saveVersion.ps1 deleted file mode 100644 index dff88134ee..0000000000 --- a/flume-ng-core/scripts/saveVersion.ps1 +++ /dev/null @@ -1,61 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script is used to generate the annotation of package info that -# records the version, revision, branch, url, user and timestamp. - -$version=$args[0] -$buildDirectory=$args[1] -$outputFile= "$buildDirectory\generated-sources\java\org\apache\flume\package-info.java" -$user = $Env:username -$date = Get-Date - -cmd /c svn info 2>&1 | Out-Null -if ( $LastExitCode -eq 0 ) { - $revision=svn info | % { if( $_ -match "Last Changed Rev: (?.*)" ) { $matches['rev'];} } - $url=svn info | % { if( $_ -match "URL: (?.*)" ) { $matches['url'];} } - $branch= if( $url -match ".*(?(branches.*)|(tags.*)|(trunk.*))" ) { $matches['branch']; } else { "Unknown"; } -} -else { - cmd /c git rev-parse HEAD 2>&1 | Out-Null - if ( $LastExitCode -eq 0 ) { - $revision=$(git log -1 --pretty=format:"%H") - $branch=$(git name-rev --name-only HEAD) - $remote=$(git config branch.$branch.remote) - $url=$(git config remote.$remote.url) - } - else { - revision="Unknown" - branch="Unknown" - url="file://$cwd" - } -} - -$srcChecksum="N/A" - - -$fileContent = @" -/* - * Generated by scripts/saveVersion.ps1 - */ -@VersionAnnotation(version="$version", revision="$revision", branch="$branch", - user="$user", date="$date", url="$url", - srcChecksum="$srcChecksum") -package org.apache.flume; -"@ - -New-Item $outputFile -value $fileContent -force -type file - diff --git a/flume-ng-core/scripts/saveVersion.sh b/flume-ng-core/scripts/saveVersion.sh deleted file mode 100755 index ad3f8b195d..0000000000 --- a/flume-ng-core/scripts/saveVersion.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script is used to generate the annotation of package info that -# records the version, revision, branch, url, user and timestamp. - -unset LANG -unset LC_CTYPE -unset LC_TIME -version=$1 -buildDirectory=$2 -user=`whoami` -date=`date` -dir=`pwd` -cwd=`dirname $dir` -if [ -d ../.svn ]; then - revision=`svn info ../ | sed -n -e 's/Last Changed Rev: \(.*\)/\1/p'` - url=`svn info ../ | sed -n -e 's/URL: \(.*\)/\1/p'` - branch=`echo $url | sed -n -e 's,.*\(branches/.*\)$,\1,p' \ - -e 's,.*\(tags/.*\)$,\1,p' \ - -e 's,.*trunk$,trunk,p'` -elif git rev-parse HEAD 2>/dev/null > /dev/null ; then - revision=`git log -1 --pretty=format:"%H"` - hostname=`hostname` - branch=`git branch | sed -n -e 's/^* //p'` - url="git://${hostname}${cwd}" -else - revision="Unknown" - branch="Unknown" - url="file://$cwd" -fi - -if [ -n "$(which md5sum)" ]; then - srcChecksum=`find ../ -name '*.java' | grep -v generated-sources | LC_ALL=C sort | \ - xargs md5sum | md5sum | cut -d ' ' -f 1` -else - srcChecksum=`find ../ -name '*.java' | grep -v generated-sources | LC_ALL=C sort | \ - xargs md5 | md5 | cut -d ' ' -f 1` -fi - -mkdir -p $buildDirectory/generated-sources/java/org/apache/flume/ -cat << EOF | \ - sed -e "s/VERSION/$version/" -e "s/USER/$user/" -e "s/DATE/$date/" \ - -e "s|URL|$url|" -e "s/REV/$revision/" \ - -e "s|BRANCH|$branch|" -e "s/SRCCHECKSUM/$srcChecksum/" \ - > $buildDirectory/generated-sources/java/org/apache/flume/package-info.java -/* - * Generated by scripts/saveVersion.sh - */ -@VersionAnnotation(version="VERSION", revision="REV", branch="BRANCH", - user="USER", date="DATE", url="URL", - srcChecksum="SRCCHECKSUM") -package org.apache.flume; -EOF \ No newline at end of file diff --git a/flume-ng-core/src/main/java/org/apache/flume/VersionAnnotation.java b/flume-ng-core/src/main/java/org/apache/flume/VersionAnnotation.java deleted file mode 100644 index 06269ac350..0000000000 --- a/flume-ng-core/src/main/java/org/apache/flume/VersionAnnotation.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.flume; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * This class is about package attribute that captures - * version info of Flume that was compiled. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PACKAGE) -public @interface VersionAnnotation { - - /** - * Get the Flume version - * @return the version string "1.1" - */ - String version(); - - /** - * Get the subversion revision. - * @return the revision number as a string (eg. "100755") - */ - String revision(); - - /** - * Get the branch from which this was compiled. - * @return The branch name, e.g. "trunk" - */ - String branch(); - - /** - * Get the username that compiled Flume. - */ - String user(); - - /** - * Get the date when Flume was compiled. - * @return the date in unix 'date' format - */ - String date(); - - /** - * Get the url for the subversion repository. - */ - String url(); - - /** - * Get a checksum of the source files from which - * Flume was compiled. - * @return a string that uniquely identifies the source - **/ - String srcChecksum(); -} diff --git a/flume-ng-core/src/main/java/org/apache/flume/tools/VersionInfo.java b/flume-ng-core/src/main/java/org/apache/flume/tools/VersionInfo.java index f5220e4dfd..8e9dd47524 100644 --- a/flume-ng-core/src/main/java/org/apache/flume/tools/VersionInfo.java +++ b/flume-ng-core/src/main/java/org/apache/flume/tools/VersionInfo.java @@ -16,105 +16,228 @@ */ package org.apache.flume.tools; -import org.apache.flume.VersionAnnotation; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.util.Properties; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; -/* - * This class provides version info of Flume NG +/** + * Provides the build metadata of the Flume artifact this class was loaded from. + * + *

The values come from the manifest of that artifact, falling back to its Maven descriptor when + * Flume has been shaded into another artifact and the manifest is no longer its own. Every accessor + * returns {@value #UNKNOWN} rather than {@code null} when the information is unavailable. */ - public class VersionInfo { - private static Package myPackage; - private static VersionAnnotation version; + private static final String UNKNOWN = "Unknown"; + + private static final String GROUP_ID = "org.apache.flume"; + private static final String ARTIFACT_ID = "flume-ng-core"; + + private static final String IMPLEMENTATION_TIMESTAMP = "Implementation-Timestamp"; + private static final String PURL = "Purl"; + private static final String BUNDLE_SCM = "Bundle-SCM"; + + private static final String PURL_PREFIX = "pkg:maven/" + GROUP_ID + "/" + ARTIFACT_ID + "@"; + private static final String CLASS_PATH = "org/apache/flume/tools/VersionInfo.class"; + private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; + private static final String POM_PROPERTIES_PATH = + "/META-INF/maven/" + GROUP_ID + "/" + ARTIFACT_ID + "/pom.properties"; + + /** Matches one {@code name=value} pair of an OSGi header, with an optionally quoted value. */ + private static final Pattern SCM_ATTRIBUTE = + Pattern.compile("(?:^|,)\\s*([A-Za-z0-9_-]+)\\s*=\\s*(?:\"([^\"]*)\"|([^,]*))"); + + private static final Attributes MANIFEST = ownManifest(); + private static final Properties POM_PROPERTIES = pomProperties(); + + private static final String VERSION = version(MANIFEST, POM_PROPERTIES); + private static final String PURL_VALUE = purl(MANIFEST, POM_PROPERTIES); + private static final String URL_VALUE = orUnknown(scmAttribute(MANIFEST.getValue(BUNDLE_SCM), "url")); + private static final String TAG = orUnknown(scmAttribute(MANIFEST.getValue(BUNDLE_SCM), "tag")); + private static final String DATE = orUnknown(MANIFEST.getValue(IMPLEMENTATION_TIMESTAMP)); + + /** + * Reads the manifest of the artifact this class was loaded from. + * + *

Resolving it against the location of this class, instead of looking up + * {@code META-INF/MANIFEST.MF} on the class path, keeps another artifact from answering. Returns + * empty attributes when the manifest is missing or belongs to an artifact Flume was shaded into. + */ + private static Attributes ownManifest() { + URL self = VersionInfo.class.getResource("VersionInfo.class"); + if (self == null) { + return new Attributes(); + } + String location = self.toString(); + if (!location.endsWith(CLASS_PATH)) { + return new Attributes(); + } + String root = location.substring(0, location.length() - CLASS_PATH.length()); + try (InputStream stream = URI.create(root + MANIFEST_PATH).toURL().openStream()) { + Attributes attributes = new Manifest(stream).getMainAttributes(); + return isOwn(attributes) ? attributes : new Attributes(); + } catch (IOException | RuntimeException ignored) { + return new Attributes(); + } + } + + private static Properties pomProperties() { + Properties properties = new Properties(); + try (InputStream stream = VersionInfo.class.getResourceAsStream(POM_PROPERTIES_PATH)) { + if (stream != null) { + properties.load(stream); + } + } catch (IOException | RuntimeException ignored) { + // Falls through to the empty properties. + } + return properties; + } + + /** Tells whether the manifest describes this artifact rather than one Flume was shaded into. */ + static boolean isOwn(Attributes manifest) { + String purl = manifest.getValue(PURL); + return purl != null && purl.startsWith(PURL_PREFIX); + } - static { - myPackage = VersionAnnotation.class.getPackage(); - version = myPackage.getAnnotation(VersionAnnotation.class); + static String version(Attributes manifest, Properties pomProperties) { + String version = manifest.getValue(Attributes.Name.IMPLEMENTATION_VERSION); + return orUnknown(version != null ? version : pomProperties.getProperty("version")); + } + + static String purl(Attributes manifest, Properties pomProperties) { + String purl = manifest.getValue(PURL); + if (purl == null) { + String groupId = pomProperties.getProperty("groupId"); + String artifactId = pomProperties.getProperty("artifactId"); + String version = pomProperties.getProperty("version"); + if (groupId != null && artifactId != null && version != null) { + purl = "pkg:maven/" + groupId + "/" + artifactId + "@" + version; + } + } + return orUnknown(purl); } /** - * Get the meta-data for the Flume package. - * @return + * Returns one attribute of an OSGi {@code Bundle-SCM} header, or {@code null} if absent. + * + *

The header is specified by OSGi Core R8, section 3.2.1, as a comma separated list of + * {@code url}, {@code connection}, {@code developer-connection} and {@code tag} attributes. */ - static Package getPackage() { - return myPackage; + static String scmAttribute(String header, String attribute) { + if (header == null) { + return null; + } + Matcher matcher = SCM_ATTRIBUTE.matcher(header); + while (matcher.find()) { + if (attribute.equals(matcher.group(1))) { + String quoted = matcher.group(2); + return quoted != null ? quoted : matcher.group(3).trim(); + } + } + return null; + } + + private static String orUnknown(String value) { + return value != null && !value.isEmpty() ? value : UNKNOWN; } /** - * Get the Flume version. - * @return the Flume version string, eg. "1.1" + * Gets the Flume version. + * + * @return the Flume version string, eg. "2.0.0" */ public static String getVersion() { - return version != null ? version.version() : "Unknown"; + return VERSION; + } + + /** + * Gets the Package URL of the Flume artifact this class was loaded from. + * + * @return the Package URL, eg. "pkg:maven/org.apache.flume/flume-ng-core@2.0.0" + */ + public static String getPurl() { + return PURL_VALUE; } /** - * Get the subversion revision number for the root directory - * @return the revision number, eg. "100755" + * Gets the source control revision this was built from. + * + *

The build no longer records a commit id, since it has to produce the same artifact from a + * Git checkout and from the source distribution, which carries no repository metadata. + * + * @return always "Unknown" */ public static String getRevision() { - if (version != null && version.revision() != null && !version.revision().isEmpty()) { - return version.revision(); - } - return "Unknown"; + return UNKNOWN; } /** - * Get the branch on which this originated. - * @return The branch name, e.g. "trunk" or "branches/branch-1.1" + * Gets the source control tag or branch this was built from. + * + * @return the tag, eg. "rel/2.0.0" */ public static String getBranch() { - return version != null ? version.branch() : "Unknown"; + return TAG; } /** - * The date that Flume was compiled. - * @return the compilation date in unix date format + * Gets the date Flume was built. + * + * @return the build date in ISO-8601 format */ public static String getDate() { - return version != null ? version.date() : "Unknown"; + return DATE; } /** - * The user that compiled Flume. - * @return the username of the user + * Gets the user that compiled Flume. + * + * @return always "Unknown" + * @deprecated Recording the user would make the build unreproducible. */ + @Deprecated(since = "2.0.0", forRemoval = true) public static String getUser() { - return version != null ? version.user() : "Unknown"; + return UNKNOWN; } /** - * Get the subversion URL for the root Flume directory. + * Gets the source control URL of the Flume repository. + * + * @return the repository URL */ public static String getUrl() { - return version != null ? version.url() : "Unknown"; + return URL_VALUE; } /** - * Get the checksum of the source files from which Flume was - * built. - **/ + * Gets the checksum of the source files Flume was built from. + * + * @return always "Unknown" + * @deprecated Use {@link #getPurl()} to identify the artifact, and verify it against the + * checksums published with the release. + */ + @Deprecated(since = "2.0.0", forRemoval = true) public static String getSrcChecksum() { - return version != null ? version.srcChecksum() : "Unknown"; + return UNKNOWN; } - /** - * Returns the build version info which includes version, - * revision, user, date and source checksum - */ + /** Returns the build version info, which includes the version, the tag and the build date. */ public static String getBuildVersion() { - return VersionInfo.getVersion() + " from " - + VersionInfo.getRevision() + " by " - + VersionInfo.getUser() + " on " - + VersionInfo.getDate() + " source checksum " - + VersionInfo.getSrcChecksum(); + return getVersion() + " from " + getBranch() + " built on " + getDate(); } public static void main(String[] args) { System.out.println("Flume " + getVersion()); - System.out.println("Source code repository: " + "https://git.apache.org/repos/asf/flume.git"); - System.out.println("Revision: " + getRevision()); - System.out.println("Compiled by " + getUser() + " on " + getDate()); - System.out.println("From source with checksum " + getSrcChecksum()); + System.out.println("Package URL: " + getPurl()); + System.out.println("Source code repository: " + getUrl()); + System.out.println("Tag: " + getBranch()); + System.out.println("Compiled on " + getDate()); } } diff --git a/flume-ng-core/src/test/java/org/apache/flume/tools/TestVersionInfo.java b/flume-ng-core/src/test/java/org/apache/flume/tools/TestVersionInfo.java index 59457f96e8..66d70734f7 100644 --- a/flume-ng-core/src/test/java/org/apache/flume/tools/TestVersionInfo.java +++ b/flume-ng-core/src/test/java/org/apache/flume/tools/TestVersionInfo.java @@ -16,42 +16,134 @@ */ package org.apache.flume.tools; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import java.time.Instant; +import java.util.Properties; +import java.util.jar.Attributes; import org.junit.Test; public class TestVersionInfo { - private static final Logger logger = LogManager.getLogger(); + private static final String PURL = "pkg:maven/org.apache.flume/flume-ng-core@2.0.0"; + + private static final String BUNDLE_SCM = "url=\"https://gitbox.apache.org/repos/asf/logging-flume.git\"," + + "connection=\"scm:git:https://gitbox.apache.org/repos/asf/logging-flume.git\"," + + "developer-connection=\"scm:git:https://gitbox.apache.org/repos/asf/logging-flume.git\"," + + "tag=\"rel/2.0.0\""; /** - * Make sure that Unknown is expected when no version info + * Checks the metadata of the artifact the test runs against. + * + *

BND writes the manifest to the output directory before the tests run, so the values are + * available whether Flume is loaded from a JAR or from the compiled classes. */ @Test - public void testVersionInfoUnknown() { + public void testMetadataOfOwnArtifact() { + assertTrue( + "getVersion returned " + VersionInfo.getVersion(), + VersionInfo.getVersion().matches("\\d+\\.\\d+.*")); + assertTrue( + "getPurl returned " + VersionInfo.getPurl(), + VersionInfo.getPurl().startsWith("pkg:maven/org.apache.flume/flume-ng-core@")); + assertTrue( + "getUrl returned " + VersionInfo.getUrl(), + VersionInfo.getUrl().startsWith("https://") + && VersionInfo.getUrl().contains("logging-flume")); + assertTrue( + "getBranch returned " + VersionInfo.getBranch(), + VersionInfo.getBranch().startsWith("rel/")); + // Throws if the timestamp is not ISO-8601. + Instant.parse(VersionInfo.getDate()); + assertTrue( + "getBuildVersion returned " + VersionInfo.getBuildVersion(), + VersionInfo.getBuildVersion().matches(".+ from .+ built on .+")); + } - logger.debug("Flume " + VersionInfo.getVersion()); - logger.debug("Subversion " + VersionInfo.getUrl() + " -r " + VersionInfo.getRevision()); - logger.debug("Compiled by " + VersionInfo.getUser() + " on " + VersionInfo.getDate()); - logger.debug("From source with checksum " + VersionInfo.getSrcChecksum()); - logger.debug("Flume " + VersionInfo.getBuildVersion()); + @Test + @SuppressWarnings({"deprecation", "removal"}) + public void testUnrecordedMetadata() { + assertEquals("Unknown", VersionInfo.getRevision()); + assertEquals("Unknown", VersionInfo.getUser()); + assertEquals("Unknown", VersionInfo.getSrcChecksum()); + } - assertTrue("getVersion returned Unknown", !VersionInfo.getVersion().equals("Unknown")); - assertTrue("getUser returned Unknown", !VersionInfo.getUser().equals("Unknown")); - assertTrue("getUrl returned Unknown", !VersionInfo.getUrl().equals("Unknown")); - assertTrue( - "getSrcChecksum returned Unknown", !VersionInfo.getSrcChecksum().equals("Unknown")); + @Test + public void testVersionPrefersTheManifest() { + Attributes manifest = new Attributes(); + manifest.putValue("Implementation-Version", "2.0.0"); + assertEquals("2.0.0", VersionInfo.version(manifest, pomProperties("1.11.0"))); + } - // check getBuildVersion() return format - assertTrue( - "getBuildVersion returned unexpected format", - VersionInfo.getBuildVersion().matches(".+from.+by.+on.+source checksum.+")); + @Test + public void testVersionFallsBackToPomProperties() { + assertEquals("1.11.0", VersionInfo.version(new Attributes(), pomProperties("1.11.0"))); + } + + @Test + public void testVersionWithoutAnySource() { + assertEquals("Unknown", VersionInfo.version(new Attributes(), new Properties())); + } + + @Test + public void testPurlPrefersTheManifest() { + Attributes manifest = new Attributes(); + manifest.putValue("Purl", PURL); + assertEquals(PURL, VersionInfo.purl(manifest, pomProperties("1.11.0"))); + } + + @Test + public void testPurlIsBuiltFromPomProperties() { + assertEquals( + "pkg:maven/org.apache.flume/flume-ng-core@1.11.0", + VersionInfo.purl(new Attributes(), pomProperties("1.11.0"))); + } + + @Test + public void testPurlWithoutAnySource() { + assertEquals("Unknown", VersionInfo.purl(new Attributes(), new Properties())); + } + + /** A manifest of an artifact Flume was shaded into must not be mistaken for our own. */ + @Test + public void testForeignManifestIsRejected() { + Attributes foreign = new Attributes(); + foreign.putValue("Purl", "pkg:maven/com.example/uber-jar@1.0.0"); + foreign.putValue("Implementation-Version", "1.0.0"); + assertFalse(VersionInfo.isOwn(foreign)); + assertFalse(VersionInfo.isOwn(new Attributes())); + + Attributes own = new Attributes(); + own.putValue("Purl", PURL); + assertTrue(VersionInfo.isOwn(own)); + } + + @Test + public void testScmAttributes() { + assertEquals("rel/2.0.0", VersionInfo.scmAttribute(BUNDLE_SCM, "tag")); + assertEquals( + "https://gitbox.apache.org/repos/asf/logging-flume.git", VersionInfo.scmAttribute(BUNDLE_SCM, "url")); + assertEquals( + "scm:git:https://gitbox.apache.org/repos/asf/logging-flume.git", + VersionInfo.scmAttribute(BUNDLE_SCM, "developer-connection")); + assertNull(VersionInfo.scmAttribute(BUNDLE_SCM, "revision")); + assertNull(VersionInfo.scmAttribute(null, "tag")); + } + + /** OSGi only requires quoting for values with special characters. */ + @Test + public void testScmAttributeWithoutQuotes() { + assertEquals("rel/2.0.0", VersionInfo.scmAttribute("url=https://example.org,tag=rel/2.0.0", "tag")); + } - // "Unknown" when build without svn or git - assertNotNull("getRevision returned null", VersionInfo.getRevision()); - assertNotNull("getBranch returned null", VersionInfo.getBranch()); + private static Properties pomProperties(String version) { + Properties properties = new Properties(); + properties.setProperty("groupId", "org.apache.flume"); + properties.setProperty("artifactId", "flume-ng-core"); + properties.setProperty("version", version); + return properties; } } diff --git a/flume-ng-sdk/pom.xml b/flume-ng-sdk/pom.xml index 6b231c2708..c5b1d20d69 100644 --- a/flume-ng-sdk/pom.xml +++ b/flume-ng-sdk/pom.xml @@ -90,6 +90,20 @@ test-jar + + + + + true + true + + + pkg:maven/${project.groupId}/${project.artifactId}@${project.version}?classifier=tests&type=test-jar + + + diff --git a/flume-parent/pom.xml b/flume-parent/pom.xml index 335b5bbf60..8036ba0ae2 100644 --- a/flume-parent/pom.xml +++ b/flume-parent/pom.xml @@ -84,6 +84,16 @@ 2026-01-01T00:00:00Z + + -removeheaders: Bundle-DocURL,Bundle-Developers + + Implementation-Timestamp: ${project.build.outputTimestamp} + Purl: pkg:maven/${project.groupId}/${project.artifactId}@${project.version} UTF-8