From 829b7ea97d98eeb834540c75c604b71d459b111a Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 22 Jun 2026 10:41:01 +0800 Subject: [PATCH 01/31] HDDS-15560. Integrate Infer static analysis into CI - Add dev-support/checks/install/infer.sh to download Infer v1.3.0 - Add dev-support/checks/infer.sh check script (#checks:basic) - Add .inferconfig to exclude generated and test sources from analysis - Add infer-annotation dependency (provided scope) to ozone-manager and server-scm - Annotate 7 thread-safe classes with @ThreadSafe: OzoneManager, SCMStateMachine, OzoneManagerLock, SCMNodeManager, SnapshotChainManager, OzoneDelegationTokenSecretManager, SCMSafeModeManager - Add .github/workflows/infer.yml (push-only, non-blocking, uploads artifacts) - Add .inferconfig to RAT exclusions Generated-by: Codex (GPT 5.5) --- .github/workflows/infer.yml | 59 +++++++++++++++++ .inferconfig | 15 +++++ dev-support/rat/rat-exclusions.txt | 1 + hadoop-hdds/server-scm/pom.xml | 6 ++ .../hadoop/hdds/scm/ha/SCMStateMachine.java | 2 + .../hadoop/hdds/scm/node/SCMNodeManager.java | 2 + .../hdds/scm/safemode/SCMSafeModeManager.java | 2 + hadoop-ozone/dev-support/checks/infer.sh | 64 +++++++++++++++++++ .../dev-support/checks/install/infer.sh | 42 ++++++++++++ hadoop-ozone/ozone-manager/pom.xml | 6 ++ .../apache/hadoop/ozone/om/OzoneManager.java | 2 + .../hadoop/ozone/om/SnapshotChainManager.java | 2 + .../ozone/om/lock/OzoneManagerLock.java | 2 + .../OzoneDelegationTokenSecretManager.java | 2 + 14 files changed, 207 insertions(+) create mode 100644 .github/workflows/infer.yml create mode 100644 .inferconfig create mode 100755 hadoop-ozone/dev-support/checks/infer.sh create mode 100755 hadoop-ozone/dev-support/checks/install/infer.sh diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml new file mode 100644 index 000000000000..f91e6a7f89a5 --- /dev/null +++ b/.github/workflows/infer.yml @@ -0,0 +1,59 @@ +# 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. + +name: infer + +on: + push: + branches: + - master + - 'HDDS-*' + - 'HDDS/**' + +permissions: + contents: read + +jobs: + infer: + name: infer + runs-on: ubuntu-24.04 + continue-on-error: true + timeout-minutes: 60 + steps: + - name: Checkout project + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - name: Setup java 21 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + distribution: 'temurin' + java-version: '21' + - name: Run Infer analysis + run: hadoop-ozone/dev-support/checks/infer.sh + - name: Archive Infer results + if: ${{ !cancelled() }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: infer + path: target/infer + continue-on-error: true + - name: Summary of failures + if: ${{ failure() }} + run: | + if [[ -s "target/infer/summary.md" ]]; then + cat target/infer/summary.md >> $GITHUB_STEP_SUMMARY + fi + hadoop-ozone/dev-support/checks/_summary.sh target/infer/summary.txt diff --git a/.inferconfig b/.inferconfig new file mode 100644 index 000000000000..30f8b196397e --- /dev/null +++ b/.inferconfig @@ -0,0 +1,15 @@ +{ + "force-delete-results-dir": true, + "capture-block-list": [ + { + "source_contains": "generated-sources" + }, + { + "source_contains": "target/" + } + ], + "report-block-list-files-containing": [ + "@generated", + "generated-sources" + ] +} diff --git a/dev-support/rat/rat-exclusions.txt b/dev-support/rat/rat-exclusions.txt index 23d3707a65c4..4c1199760ec0 100644 --- a/dev-support/rat/rat-exclusions.txt +++ b/dev-support/rat/rat-exclusions.txt @@ -18,6 +18,7 @@ **/*.json .gitattributes .github/* +.inferconfig AGENTS.md CLAUDE.md CONTRIBUTING.md diff --git a/hadoop-hdds/server-scm/pom.xml b/hadoop-hdds/server-scm/pom.xml index 5a8a779e4e25..fddf78031bd0 100644 --- a/hadoop-hdds/server-scm/pom.xml +++ b/hadoop-hdds/server-scm/pom.xml @@ -169,6 +169,12 @@ org.slf4j slf4j-api + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + provided + org.apache.ozone hdds-annotation-processing diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMStateMachine.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMStateMachine.java index 5036769ed3a6..c70cc1d4fb49 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMStateMachine.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SCMStateMachine.java @@ -19,6 +19,7 @@ import static java.util.Objects.requireNonNull; +import com.facebook.infer.annotation.ThreadSafe; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -69,6 +70,7 @@ * responsible for applying ratis committed transactions to * {@link StorageContainerManager}. */ +@ThreadSafe public class SCMStateMachine extends BaseStateMachine { private static final Logger LOG = LoggerFactory.getLogger(SCMStateMachine.class); diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/SCMNodeManager.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/SCMNodeManager.java index fe886bf0c20e..3ef7af642a66 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/SCMNodeManager.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/node/SCMNodeManager.java @@ -23,6 +23,7 @@ import static org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState.HEALTHY; import static org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState.HEALTHY_READONLY; +import com.facebook.infer.annotation.ThreadSafe; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Strings; @@ -118,6 +119,7 @@ * get functions in this file as a snap-shot of information that is inconsistent * as soon as you read it. */ +@ThreadSafe public class SCMNodeManager implements NodeManager, ContainerReplicaPendingOpsSubscriber { private static final Logger LOG = diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/safemode/SCMSafeModeManager.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/safemode/SCMSafeModeManager.java index b185f3a37fcb..2caf77316f28 100644 --- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/safemode/SCMSafeModeManager.java +++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/safemode/SCMSafeModeManager.java @@ -22,6 +22,7 @@ import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_SCM_SAFEMODE_RULE_REFRESH_INTERVAL; import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_SCM_SAFEMODE_RULE_REFRESH_INTERVAL_DEFAULT; +import com.facebook.infer.annotation.ThreadSafe; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.util.HashMap; import java.util.HashSet; @@ -74,6 +75,7 @@ * @see RatisContainerSafeModeRule * @see ECContainerSafeModeRule */ +@ThreadSafe public class SCMSafeModeManager implements SafeModeManager { private static final Logger LOG = LoggerFactory.getLogger(SCMSafeModeManager.class); diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh new file mode 100755 index 000000000000..dc4c1e2efd30 --- /dev/null +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# 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. + +#checks:basic + +set -u -o pipefail + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +cd "$DIR/../../.." || exit 1 + +source "${DIR}/_lib.sh" +source "${DIR}/install/infer.sh" + +REPORT_DIR=${OUTPUT_DIR:-"$DIR/../../../target/infer"} +mkdir -p "$REPORT_DIR" +REPORT_FILE="$REPORT_DIR/summary.txt" + +MAVEN_OPTIONS='-B -DskipTests -DskipDocs -DskipRecon -DskipShade --no-transfer-progress' + +# Infer runs by wrapping javac during the Maven build to capture and analyze +# Java sources. We use 'clean compile' to ensure all sources are freshly +# compiled and captured. --keep-going tells Infer to continue past errors. +infer run --keep-going -- mvn ${MAVEN_OPTIONS} clean compile "$@" 2>&1 | tee "${REPORT_DIR}/output.log" +rc=$? + +# Copy infer output to report directory for artifact upload and reporting +if [[ -d infer-out ]]; then + cp -r infer-out/* "${REPORT_DIR}/" 2>/dev/null || true +fi + +touch "$REPORT_FILE" + +if [[ -f "${REPORT_DIR}/report.txt" ]]; then + echo "Infer analysis complete." >> "$REPORT_FILE" + echo "" >> "$REPORT_FILE" + cat "${REPORT_DIR}/report.txt" >> "$REPORT_FILE" + + # Count issue lines (lines containing .java: which indicate findings) + grep -c '\.java:' "${REPORT_DIR}/report.txt" > "${REPORT_DIR}/failures" 2>/dev/null || echo "0" > "${REPORT_DIR}/failures" +else + echo "Infer completed without generating issues." >> "$REPORT_FILE" + echo "0" > "${REPORT_DIR}/failures" +fi + +# Generate HTML summary if possible +if [[ -f "${REPORT_DIR}/report.txt" ]]; then + infer report --issues-json "${REPORT_DIR}/report.json" 2>/dev/null || true +fi + +ERROR_PATTERN="\[ERROR\]" +source "${DIR}/_post_process.sh" diff --git a/hadoop-ozone/dev-support/checks/install/infer.sh b/hadoop-ozone/dev-support/checks/install/infer.sh new file mode 100755 index 000000000000..a573c8252e4d --- /dev/null +++ b/hadoop-ozone/dev-support/checks/install/infer.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# 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 installs Infer. +# Requires _install_tool from _lib.sh. Use `source` for both scripts, because it modifies $PATH. + +: ${INFER_VERSION:=1.3.0} + +_install_infer() { + local os + os=$(uname -s) + local arch + arch=$(uname -m) + + if [[ "${os}" == "Linux" ]]; then + local infer_archive="infer-linux-x86_64-v${INFER_VERSION}.tar.xz" + elif [[ "${os}" == "Darwin" ]] && [[ "${arch}" == "arm64" ]]; then + local infer_archive="infer-osx-arm64-v${INFER_VERSION}.tar.xz" + else + echo "Unsupported platform: ${os} ${arch}" >&2 + exit 1 + fi + + local url="https://github.com/facebook/infer/releases/download/v${INFER_VERSION}/${infer_archive}" + mkdir -p "infer-v${INFER_VERSION}" + curl -LSs "${url}" | tar -xJ -C "infer-v${INFER_VERSION}" --strip-components=1 -f - +} + +_install_tool infer "infer-v${INFER_VERSION}/bin" "infer" "infer" diff --git a/hadoop-ozone/ozone-manager/pom.xml b/hadoop-ozone/ozone-manager/pom.xml index 0715e3e95630..66ddd5769f4f 100644 --- a/hadoop-ozone/ozone-manager/pom.xml +++ b/hadoop-ozone/ozone-manager/pom.xml @@ -233,6 +233,12 @@ org.yaml snakeyaml + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + provided + org.apache.ozone hdds-annotation-processing diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java index 101cbbfadb05..e752dc75fc87 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java @@ -115,6 +115,7 @@ import static org.apache.hadoop.util.ExitUtil.terminate; import static org.apache.hadoop.util.Time.monotonicNow; +import com.facebook.infer.annotation.ThreadSafe; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; @@ -373,6 +374,7 @@ * Ozone Manager is the metadata manager of ozone. */ @InterfaceAudience.LimitedPrivate({"HDFS", "CBLOCK", "OZONE", "HBASE"}) +@ThreadSafe public final class OzoneManager extends ServiceRuntimeInfoImpl implements OzoneManagerProtocol, OMInterServiceProtocol, OMMXBean, Auditor { public static final Logger LOG = diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/SnapshotChainManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/SnapshotChainManager.java index 5951c1303ffe..6708d73d5121 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/SnapshotChainManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/SnapshotChainManager.java @@ -17,6 +17,7 @@ package org.apache.hadoop.ozone.om; +import com.facebook.infer.annotation.ThreadSafe; import com.google.common.annotations.VisibleForTesting; import java.io.IOException; import java.io.UncheckedIOException; @@ -47,6 +48,7 @@ * On start, the snapshot chains are initialized from the on disk * SnapshotInfoTable from the OM RocksDB. */ +@ThreadSafe public class SnapshotChainManager { private static final Logger LOG = LoggerFactory.getLogger(SnapshotChainManager.class); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/lock/OzoneManagerLock.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/lock/OzoneManagerLock.java index 1847dd31b7cc..81edbc29bbca 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/lock/OzoneManagerLock.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/lock/OzoneManagerLock.java @@ -22,6 +22,7 @@ import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_MANAGER_STRIPED_LOCK_SIZE_DEFAULT; import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_MANAGER_STRIPED_LOCK_SIZE_PREFIX; +import com.facebook.infer.annotation.ThreadSafe; import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.Striped; import java.util.Arrays; @@ -91,6 +92,7 @@ *
*/ +@ThreadSafe public class OzoneManagerLock implements IOzoneManagerLock { private static final Logger LOG = LoggerFactory.getLogger(OzoneManagerLock.class); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSecretManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSecretManager.java index 01818aa2d57b..e0afd6288343 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSecretManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSecretManager.java @@ -21,6 +21,7 @@ import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_EXPIRED; import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMTokenProto.Type.S3AUTHINFO; +import com.facebook.infer.annotation.ThreadSafe; import com.google.common.annotations.VisibleForTesting; import java.io.ByteArrayInputStream; import java.io.DataInputStream; @@ -66,6 +67,7 @@ */ @InterfaceAudience.Private @InterfaceStability.Unstable +@ThreadSafe public class OzoneDelegationTokenSecretManager extends OzoneSecretManager { From 44b27dff1604171fe851167e184c8dbdbea00c10 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 22 Jun 2026 15:00:40 +0800 Subject: [PATCH 02/31] HDDS-15560. Fix infer CI: compile scope for infer-annotation and robust infer.sh - Change infer-annotation from provided to compile scope so downstream modules (ozone-recon) can resolve @ThreadSafe annotations transitively - Remove 'clean' from infer.sh mvn command to avoid deleting target/infer/ - Recreate REPORT_DIR after infer run as a defensive measure - Restore ozonefs pom.xml files after infer run via git checkout, since Infer's Maven capture can corrupt profile-governed modules Generated-by: Codex (GPT 5.5) --- hadoop-hdds/server-scm/pom.xml | 2 +- hadoop-ozone/dev-support/checks/infer.sh | 18 ++++++++++++++---- hadoop-ozone/ozone-manager/pom.xml | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/hadoop-hdds/server-scm/pom.xml b/hadoop-hdds/server-scm/pom.xml index fddf78031bd0..cbb69f8cd284 100644 --- a/hadoop-hdds/server-scm/pom.xml +++ b/hadoop-hdds/server-scm/pom.xml @@ -173,7 +173,7 @@ com.facebook.infer.annotation infer-annotation 0.18.0 - provided + compile
org.apache.ozone diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index dc4c1e2efd30..fd15b6e31eab 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -25,22 +25,32 @@ source "${DIR}/_lib.sh" source "${DIR}/install/infer.sh" REPORT_DIR=${OUTPUT_DIR:-"$DIR/../../../target/infer"} -mkdir -p "$REPORT_DIR" REPORT_FILE="$REPORT_DIR/summary.txt" MAVEN_OPTIONS='-B -DskipTests -DskipDocs -DskipRecon -DskipShade --no-transfer-progress' # Infer runs by wrapping javac during the Maven build to capture and analyze -# Java sources. We use 'clean compile' to ensure all sources are freshly -# compiled and captured. --keep-going tells Infer to continue past errors. -infer run --keep-going -- mvn ${MAVEN_OPTIONS} clean compile "$@" 2>&1 | tee "${REPORT_DIR}/output.log" +# Java sources. We use 'compile' (not 'clean') to avoid deleting target/, +# which would wipe the REPORT_DIR. --keep-going tells Infer to continue past +# compilation errors in individual modules. +mkdir -p "$REPORT_DIR" +infer run --keep-going -- mvn ${MAVEN_OPTIONS} compile "$@" 2>&1 | tee "${REPORT_DIR}/output.log" rc=$? +# Recreate REPORT_DIR after infer run, in case it was deleted (e.g., during +# a 'clean' phase or interleaved Maven runs). +mkdir -p "$REPORT_DIR" + # Copy infer output to report directory for artifact upload and reporting if [[ -d infer-out ]]; then cp -r infer-out/* "${REPORT_DIR}/" 2>/dev/null || true fi +# Infer's Maven integration may inject profiles into pom.xml files in +# profile-governed modules (ozonefs-hadoop2, ozonefs-shaded) and fail to +# restore them, leaving them corrupted. Restore from git checkout. +git checkout -- hadoop-ozone/ozonefs-hadoop2/pom.xml hadoop-ozone/ozonefs-shaded/pom.xml 2>/dev/null || true + touch "$REPORT_FILE" if [[ -f "${REPORT_DIR}/report.txt" ]]; then diff --git a/hadoop-ozone/ozone-manager/pom.xml b/hadoop-ozone/ozone-manager/pom.xml index 66ddd5769f4f..3893a3d38a53 100644 --- a/hadoop-ozone/ozone-manager/pom.xml +++ b/hadoop-ozone/ozone-manager/pom.xml @@ -237,7 +237,7 @@ com.facebook.infer.annotation infer-annotation 0.18.0 - provided + compile org.apache.ozone From 15e8e9d5ed060b4771012e3b4e0315709774774d Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 22 Jun 2026 15:35:46 +0800 Subject: [PATCH 03/31] HDDS-15560. Fix sortpom ordering and infer epilogue exit code - Move infer-annotation dependency to correct alphabetical position (com.facebook before com.fasterxml) in server-scm and ozone-manager pom.xml - Reset infer exit code to 0 when infer-out/ exists, since epilogue pom.xml restoration errors are non-fatal and the analysis completes Generated-by: Codex (GPT 5.5) --- hadoop-hdds/server-scm/pom.xml | 13 +++++++------ hadoop-ozone/dev-support/checks/infer.sh | 7 +++++++ hadoop-ozone/ozone-manager/pom.xml | 13 +++++++------ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/hadoop-hdds/server-scm/pom.xml b/hadoop-hdds/server-scm/pom.xml index cbb69f8cd284..e57a6800350d 100644 --- a/hadoop-hdds/server-scm/pom.xml +++ b/hadoop-hdds/server-scm/pom.xml @@ -30,6 +30,13 @@ + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + compile + + com.fasterxml.jackson.core jackson-annotations @@ -169,12 +176,6 @@ org.slf4j slf4j-api - - com.facebook.infer.annotation - infer-annotation - 0.18.0 - compile - org.apache.ozone hdds-annotation-processing diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index fd15b6e31eab..504e3e140953 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -41,6 +41,13 @@ rc=$? # a 'clean' phase or interleaved Maven runs). mkdir -p "$REPORT_DIR" +# Infer may exit non-zero solely from epilogue pom.xml restoration errors +# even when the analysis itself completed and produced results. If infer-out +# exists, the analysis ran; treat the exit code as successful. +if [[ -d infer-out ]]; then + rc=0 +fi + # Copy infer output to report directory for artifact upload and reporting if [[ -d infer-out ]]; then cp -r infer-out/* "${REPORT_DIR}/" 2>/dev/null || true diff --git a/hadoop-ozone/ozone-manager/pom.xml b/hadoop-ozone/ozone-manager/pom.xml index 3893a3d38a53..7e55aabb0160 100644 --- a/hadoop-ozone/ozone-manager/pom.xml +++ b/hadoop-ozone/ozone-manager/pom.xml @@ -30,6 +30,13 @@ + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + compile + + com.fasterxml.jackson.core jackson-annotations @@ -233,12 +240,6 @@ org.yaml snakeyaml - - com.facebook.infer.annotation - infer-annotation - 0.18.0 - compile - org.apache.ozone hdds-annotation-processing From dca84919da076194e6ed542eae1f28b0d4a2f412 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 22 Jun 2026 16:46:24 +0800 Subject: [PATCH 04/31] HDDS-15560. Fix infer CI: REPORT_FILE, bats tests, and dependency scope - infer.sh: truncate REPORT_FILE in no-findings case so _post_process.sh exits 0. Remove ERROR_PATTERN to avoid catching infer epilogue pom.xml restoration noise. - selective_ci_checks.bats: add 'infer' to expected basic-checks in 4 tests that hit set_outputs_run_everything_and_exit (which discovers all #checks: scripts). - pom.xml: change infer-annotation from compile to provided scope. @ThreadSafe is RetentionPolicy.CLASS, not needed at runtime. --- dev-support/ci/selective_ci_checks.bats | 8 ++++---- hadoop-hdds/server-scm/pom.xml | 2 +- hadoop-ozone/dev-support/checks/infer.sh | 3 +-- hadoop-ozone/ozone-manager/pom.xml | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/dev-support/ci/selective_ci_checks.bats b/dev-support/ci/selective_ci_checks.bats index 891e04506086..35ab2a8f0880 100644 --- a/dev-support/ci/selective_ci_checks.bats +++ b/dev-support/ci/selective_ci_checks.bats @@ -311,7 +311,7 @@ load bats-assert/load.bash @test "CI lib change" { run dev-support/ci/selective_ci_checks.sh ceb79acaa - assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","pmd","rat"]' + assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","infer","pmd","rat"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -322,7 +322,7 @@ load bats-assert/load.bash @test "CI workflow change" { run dev-support/ci/selective_ci_checks.sh 90a8d7c01 - assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","pmd","rat"]' + assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","infer","pmd","rat"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -345,7 +345,7 @@ load bats-assert/load.bash @test "CI workflow change (check.yml)" { run dev-support/ci/selective_ci_checks.sh 1468af02067ec75b255f605816c32f8bf4dfaabf - assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","pmd","rat"]' + assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","infer","pmd","rat"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -356,7 +356,7 @@ load bats-assert/load.bash @test "CI workflow change (ci.yaml)" { run dev-support/ci/selective_ci_checks.sh 90fd5f2adc - assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","pmd","rat"]' + assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","infer","pmd","rat"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true diff --git a/hadoop-hdds/server-scm/pom.xml b/hadoop-hdds/server-scm/pom.xml index e57a6800350d..0937206db502 100644 --- a/hadoop-hdds/server-scm/pom.xml +++ b/hadoop-hdds/server-scm/pom.xml @@ -34,7 +34,7 @@ com.facebook.infer.annotation infer-annotation 0.18.0 - compile + provided diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index 504e3e140953..6243259c9413 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -68,7 +68,7 @@ if [[ -f "${REPORT_DIR}/report.txt" ]]; then # Count issue lines (lines containing .java: which indicate findings) grep -c '\.java:' "${REPORT_DIR}/report.txt" > "${REPORT_DIR}/failures" 2>/dev/null || echo "0" > "${REPORT_DIR}/failures" else - echo "Infer completed without generating issues." >> "$REPORT_FILE" + : > "$REPORT_FILE" echo "0" > "${REPORT_DIR}/failures" fi @@ -77,5 +77,4 @@ if [[ -f "${REPORT_DIR}/report.txt" ]]; then infer report --issues-json "${REPORT_DIR}/report.json" 2>/dev/null || true fi -ERROR_PATTERN="\[ERROR\]" source "${DIR}/_post_process.sh" diff --git a/hadoop-ozone/ozone-manager/pom.xml b/hadoop-ozone/ozone-manager/pom.xml index 7e55aabb0160..f3218e1fd8f1 100644 --- a/hadoop-ozone/ozone-manager/pom.xml +++ b/hadoop-ozone/ozone-manager/pom.xml @@ -34,7 +34,7 @@ com.facebook.infer.annotation infer-annotation 0.18.0 - compile + provided From bf83eb9b72d2d0997811a119721e5025dde89dc0 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 22 Jun 2026 17:10:11 +0800 Subject: [PATCH 05/31] HDDS-15560. Fix sortpom ordering: move infer-annotation to provided-scope section --- hadoop-hdds/server-scm/pom.xml | 14 +++++++------- hadoop-ozone/ozone-manager/pom.xml | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/hadoop-hdds/server-scm/pom.xml b/hadoop-hdds/server-scm/pom.xml index 0937206db502..70c10e18568d 100644 --- a/hadoop-hdds/server-scm/pom.xml +++ b/hadoop-hdds/server-scm/pom.xml @@ -30,13 +30,6 @@ - - com.facebook.infer.annotation - infer-annotation - 0.18.0 - provided - - com.fasterxml.jackson.core jackson-annotations @@ -176,6 +169,13 @@ org.slf4j slf4j-api + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + provided + + org.apache.ozone hdds-annotation-processing diff --git a/hadoop-ozone/ozone-manager/pom.xml b/hadoop-ozone/ozone-manager/pom.xml index f3218e1fd8f1..8f7e6ddd5b20 100644 --- a/hadoop-ozone/ozone-manager/pom.xml +++ b/hadoop-ozone/ozone-manager/pom.xml @@ -30,13 +30,6 @@ - - com.facebook.infer.annotation - infer-annotation - 0.18.0 - provided - - com.fasterxml.jackson.core jackson-annotations @@ -240,6 +233,13 @@ org.yaml snakeyaml + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + provided + + org.apache.ozone hdds-annotation-processing From 61c4872fa13c296ead16419d0a7d2598910798a6 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 22 Jun 2026 17:32:44 +0800 Subject: [PATCH 06/31] HDDS-15560. Skip sortpom during infer capture to prevent sortpom:verify from failing on root pom.xml --- hadoop-ozone/dev-support/checks/infer.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index 6243259c9413..2d6a4d5652fa 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -27,7 +27,7 @@ source "${DIR}/install/infer.sh" REPORT_DIR=${OUTPUT_DIR:-"$DIR/../../../target/infer"} REPORT_FILE="$REPORT_DIR/summary.txt" -MAVEN_OPTIONS='-B -DskipTests -DskipDocs -DskipRecon -DskipShade --no-transfer-progress' +MAVEN_OPTIONS='-B -DskipTests -DskipDocs -DskipRecon -DskipShade -Dsort.skip=true --no-transfer-progress' # Infer runs by wrapping javac during the Maven build to capture and analyze # Java sources. We use 'compile' (not 'clean') to avoid deleting target/, From f4184a474cc40d7ecb9f4f005fa90789fbe35a05 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 22 Jun 2026 18:09:43 +0800 Subject: [PATCH 07/31] HDDS-15560. Change infer-annotation scope to compile (default) so @ThreadSafe is visible transitively --- hadoop-hdds/server-scm/pom.xml | 10 +++++----- hadoop-ozone/ozone-manager/pom.xml | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hadoop-hdds/server-scm/pom.xml b/hadoop-hdds/server-scm/pom.xml index 70c10e18568d..9fda3ddd4d4c 100644 --- a/hadoop-hdds/server-scm/pom.xml +++ b/hadoop-hdds/server-scm/pom.xml @@ -30,6 +30,11 @@ + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + com.fasterxml.jackson.core jackson-annotations @@ -170,11 +175,6 @@ slf4j-api - com.facebook.infer.annotation - infer-annotation - 0.18.0 - provided - org.apache.ozone diff --git a/hadoop-ozone/ozone-manager/pom.xml b/hadoop-ozone/ozone-manager/pom.xml index 8f7e6ddd5b20..2ae1f2c8f8a5 100644 --- a/hadoop-ozone/ozone-manager/pom.xml +++ b/hadoop-ozone/ozone-manager/pom.xml @@ -30,6 +30,11 @@ + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + com.fasterxml.jackson.core jackson-annotations @@ -234,11 +239,6 @@ snakeyaml - com.facebook.infer.annotation - infer-annotation - 0.18.0 - provided - org.apache.ozone From f48e97d7e884c9b1fefb7765cb26f68afa07a92a Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 22 Jun 2026 18:26:03 +0800 Subject: [PATCH 08/31] HDDS-15560. Use 'mvn install' instead of 'mvn compile' for infer capture so test-jars are available for downstream modules --- hadoop-ozone/dev-support/checks/infer.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index 2d6a4d5652fa..51ca77c94aea 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -34,7 +34,7 @@ MAVEN_OPTIONS='-B -DskipTests -DskipDocs -DskipRecon -DskipShade -Dsort.skip=tru # which would wipe the REPORT_DIR. --keep-going tells Infer to continue past # compilation errors in individual modules. mkdir -p "$REPORT_DIR" -infer run --keep-going -- mvn ${MAVEN_OPTIONS} compile "$@" 2>&1 | tee "${REPORT_DIR}/output.log" +infer run --keep-going -- mvn ${MAVEN_OPTIONS} install "$@" 2>&1 | tee "${REPORT_DIR}/output.log" rc=$? # Recreate REPORT_DIR after infer run, in case it was deleted (e.g., during From 9077ba90faa58e562851f0994db0ec2d58240d85 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Thu, 25 Jun 2026 18:08:38 +0800 Subject: [PATCH 09/31] HDDS-15560. Remove stray empty tags from server-scm and ozone-manager POMs --- hadoop-hdds/server-scm/pom.xml | 2 -- hadoop-ozone/ozone-manager/pom.xml | 2 -- 2 files changed, 4 deletions(-) diff --git a/hadoop-hdds/server-scm/pom.xml b/hadoop-hdds/server-scm/pom.xml index 9fda3ddd4d4c..57e9598541ba 100644 --- a/hadoop-hdds/server-scm/pom.xml +++ b/hadoop-hdds/server-scm/pom.xml @@ -174,8 +174,6 @@ org.slf4j slf4j-api - - org.apache.ozone hdds-annotation-processing diff --git a/hadoop-ozone/ozone-manager/pom.xml b/hadoop-ozone/ozone-manager/pom.xml index 2ae1f2c8f8a5..ec4aea14d6ec 100644 --- a/hadoop-ozone/ozone-manager/pom.xml +++ b/hadoop-ozone/ozone-manager/pom.xml @@ -238,8 +238,6 @@ org.yaml snakeyaml - - org.apache.ozone hdds-annotation-processing From bd891a8684648965b2854d1d6f079311483bc426 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Fri, 26 Jun 2026 14:43:38 +0800 Subject: [PATCH 10/31] HDDS-15560. Fix infer CI: provided scope for infer-annotation, shrink artifact, exit 0 on findings --- .github/workflows/infer.yml | 10 +++-- hadoop-hdds/server-scm/pom.xml | 1 + hadoop-ozone/dev-support/checks/infer.sh | 52 ++++++++++-------------- hadoop-ozone/ozone-manager/pom.xml | 1 + 4 files changed, 30 insertions(+), 34 deletions(-) diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml index f91e6a7f89a5..cfafae79996e 100644 --- a/.github/workflows/infer.yml +++ b/.github/workflows/infer.yml @@ -43,12 +43,16 @@ jobs: java-version: '21' - name: Run Infer analysis run: hadoop-ozone/dev-support/checks/infer.sh - - name: Archive Infer results + - name: Upload Infer report if: ${{ !cancelled() }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: infer - path: target/infer + name: infer-report + path: | + target/infer/report.txt + target/infer/output.log + target/infer/summary.txt + target/infer/failures continue-on-error: true - name: Summary of failures if: ${{ failure() }} diff --git a/hadoop-hdds/server-scm/pom.xml b/hadoop-hdds/server-scm/pom.xml index 57e9598541ba..2fc9df18df76 100644 --- a/hadoop-hdds/server-scm/pom.xml +++ b/hadoop-hdds/server-scm/pom.xml @@ -34,6 +34,7 @@ com.facebook.infer.annotation infer-annotation 0.18.0 + provided com.fasterxml.jackson.core diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index 51ca77c94aea..c4a91f3cb2db 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -29,52 +29,42 @@ REPORT_FILE="$REPORT_DIR/summary.txt" MAVEN_OPTIONS='-B -DskipTests -DskipDocs -DskipRecon -DskipShade -Dsort.skip=true --no-transfer-progress' -# Infer runs by wrapping javac during the Maven build to capture and analyze -# Java sources. We use 'compile' (not 'clean') to avoid deleting target/, -# which would wipe the REPORT_DIR. --keep-going tells Infer to continue past -# compilation errors in individual modules. mkdir -p "$REPORT_DIR" infer run --keep-going -- mvn ${MAVEN_OPTIONS} install "$@" 2>&1 | tee "${REPORT_DIR}/output.log" -rc=$? +infer_rc=$? -# Recreate REPORT_DIR after infer run, in case it was deleted (e.g., during -# a 'clean' phase or interleaved Maven runs). mkdir -p "$REPORT_DIR" -# Infer may exit non-zero solely from epilogue pom.xml restoration errors -# even when the analysis itself completed and produced results. If infer-out -# exists, the analysis ran; treat the exit code as successful. -if [[ -d infer-out ]]; then - rc=0 +# Only copy text reports, not the multi-GB capture/results databases +if [[ -f infer-out/report.txt ]]; then + cp infer-out/report.txt "${REPORT_DIR}/" + cp infer-out/logs "${REPORT_DIR}/" 2>/dev/null || true + cp infer-out/stats "${REPORT_DIR}/" -r 2>/dev/null || true +elif [[ -d infer-out ]]; then + find infer-out -name "report.txt" -exec cp {} "${REPORT_DIR}/" \; 2>/dev/null || true fi -# Copy infer output to report directory for artifact upload and reporting -if [[ -d infer-out ]]; then - cp -r infer-out/* "${REPORT_DIR}/" 2>/dev/null || true -fi - -# Infer's Maven integration may inject profiles into pom.xml files in -# profile-governed modules (ozonefs-hadoop2, ozonefs-shaded) and fail to -# restore them, leaving them corrupted. Restore from git checkout. +# Restore pom.xml files corrupted by infer's Maven profile injection git checkout -- hadoop-ozone/ozonefs-hadoop2/pom.xml hadoop-ozone/ozonefs-shaded/pom.xml 2>/dev/null || true touch "$REPORT_FILE" if [[ -f "${REPORT_DIR}/report.txt" ]]; then - echo "Infer analysis complete." >> "$REPORT_FILE" - echo "" >> "$REPORT_FILE" - cat "${REPORT_DIR}/report.txt" >> "$REPORT_FILE" - - # Count issue lines (lines containing .java: which indicate findings) - grep -c '\.java:' "${REPORT_DIR}/report.txt" > "${REPORT_DIR}/failures" 2>/dev/null || echo "0" > "${REPORT_DIR}/failures" + issue_count=$(grep -c '\.java:' "${REPORT_DIR}/report.txt" 2>/dev/null || echo "0") + echo "Infer analysis complete. Found ${issue_count} issues." >> "$REPORT_FILE" + echo "${issue_count}" > "${REPORT_DIR}/failures" else - : > "$REPORT_FILE" + echo "Infer analysis produced no report." >> "$REPORT_FILE" echo "0" > "${REPORT_DIR}/failures" fi -# Generate HTML summary if possible +# Infer findings are informational, never a failure. +# Only exit non-zero if infer itself failed without producing any report. if [[ -f "${REPORT_DIR}/report.txt" ]]; then - infer report --issues-json "${REPORT_DIR}/report.json" 2>/dev/null || true + exit 0 +elif [[ ${infer_rc} -ne 0 ]]; then + echo "Infer exited with code ${infer_rc} and no report was produced." >> "$REPORT_FILE" + exit 1 +else + exit 0 fi - -source "${DIR}/_post_process.sh" diff --git a/hadoop-ozone/ozone-manager/pom.xml b/hadoop-ozone/ozone-manager/pom.xml index ec4aea14d6ec..1ab7bb553187 100644 --- a/hadoop-ozone/ozone-manager/pom.xml +++ b/hadoop-ozone/ozone-manager/pom.xml @@ -34,6 +34,7 @@ com.facebook.infer.annotation infer-annotation 0.18.0 + provided com.fasterxml.jackson.core From 1e320e8a3231bb189913a0d7dfedca0773341e37 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Fri, 26 Jun 2026 16:09:36 +0800 Subject: [PATCH 11/31] HDDS-15560. Move infer-annotation to provided-scope section for sortpom ordering; restore poms before infer run --- hadoop-hdds/server-scm/pom.xml | 12 ++++++------ hadoop-ozone/dev-support/checks/infer.sh | 6 +++++- hadoop-ozone/ozone-manager/pom.xml | 12 ++++++------ 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/hadoop-hdds/server-scm/pom.xml b/hadoop-hdds/server-scm/pom.xml index 2fc9df18df76..fddf78031bd0 100644 --- a/hadoop-hdds/server-scm/pom.xml +++ b/hadoop-hdds/server-scm/pom.xml @@ -30,12 +30,6 @@ - - com.facebook.infer.annotation - infer-annotation - 0.18.0 - provided - com.fasterxml.jackson.core jackson-annotations @@ -175,6 +169,12 @@ org.slf4j slf4j-api + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + provided + org.apache.ozone hdds-annotation-processing diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index c4a91f3cb2db..35d9620cdcbf 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -29,6 +29,10 @@ REPORT_FILE="$REPORT_DIR/summary.txt" MAVEN_OPTIONS='-B -DskipTests -DskipDocs -DskipRecon -DskipShade -Dsort.skip=true --no-transfer-progress' +# Restore pom.xml files that may have been corrupted by a previous infer run +# (infer injects profiles into pom.xml and sometimes fails to restore them). +git checkout -- hadoop-ozone/ozonefs-hadoop2/pom.xml hadoop-ozone/ozonefs-shaded/pom.xml 2>/dev/null || true + mkdir -p "$REPORT_DIR" infer run --keep-going -- mvn ${MAVEN_OPTIONS} install "$@" 2>&1 | tee "${REPORT_DIR}/output.log" infer_rc=$? @@ -44,7 +48,7 @@ elif [[ -d infer-out ]]; then find infer-out -name "report.txt" -exec cp {} "${REPORT_DIR}/" \; 2>/dev/null || true fi -# Restore pom.xml files corrupted by infer's Maven profile injection +# Restore pom.xml files again after infer (belt and suspenders) git checkout -- hadoop-ozone/ozonefs-hadoop2/pom.xml hadoop-ozone/ozonefs-shaded/pom.xml 2>/dev/null || true touch "$REPORT_FILE" diff --git a/hadoop-ozone/ozone-manager/pom.xml b/hadoop-ozone/ozone-manager/pom.xml index 1ab7bb553187..66ddd5769f4f 100644 --- a/hadoop-ozone/ozone-manager/pom.xml +++ b/hadoop-ozone/ozone-manager/pom.xml @@ -30,12 +30,6 @@ - - com.facebook.infer.annotation - infer-annotation - 0.18.0 - provided - com.fasterxml.jackson.core jackson-annotations @@ -239,6 +233,12 @@ org.yaml snakeyaml + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + provided + org.apache.ozone hdds-annotation-processing From d1d75dfdc819984df2c089d1f1f8c7fed2375d86 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Fri, 26 Jun 2026 17:25:24 +0800 Subject: [PATCH 12/31] HDDS-15560. Change infer-annotation to compile scope so @ThreadSafe is transitively visible to downstream modules --- hadoop-hdds/server-scm/pom.xml | 11 +++++------ hadoop-ozone/ozone-manager/pom.xml | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/hadoop-hdds/server-scm/pom.xml b/hadoop-hdds/server-scm/pom.xml index fddf78031bd0..57e9598541ba 100644 --- a/hadoop-hdds/server-scm/pom.xml +++ b/hadoop-hdds/server-scm/pom.xml @@ -30,6 +30,11 @@ + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + com.fasterxml.jackson.core jackson-annotations @@ -169,12 +174,6 @@ org.slf4j slf4j-api - - com.facebook.infer.annotation - infer-annotation - 0.18.0 - provided - org.apache.ozone hdds-annotation-processing diff --git a/hadoop-ozone/ozone-manager/pom.xml b/hadoop-ozone/ozone-manager/pom.xml index 66ddd5769f4f..ec4aea14d6ec 100644 --- a/hadoop-ozone/ozone-manager/pom.xml +++ b/hadoop-ozone/ozone-manager/pom.xml @@ -30,6 +30,11 @@ + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + com.fasterxml.jackson.core jackson-annotations @@ -233,12 +238,6 @@ org.yaml snakeyaml - - com.facebook.infer.annotation - infer-annotation - 0.18.0 - provided - org.apache.ozone hdds-annotation-processing From 071b577d8e0cb140a17b2a90ca9b4f60e41fe3d9 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 29 Jun 2026 10:48:25 +0800 Subject: [PATCH 13/31] HDDS-15560. Exclude infer-annotation transitive deps from distribution tarball --- hadoop-hdds/server-scm/pom.xml | 10 ++++++++++ hadoop-ozone/ozone-manager/pom.xml | 10 ++++++++++ pom.xml | 1 + 3 files changed, 21 insertions(+) diff --git a/hadoop-hdds/server-scm/pom.xml b/hadoop-hdds/server-scm/pom.xml index 57e9598541ba..ff21e0149c83 100644 --- a/hadoop-hdds/server-scm/pom.xml +++ b/hadoop-hdds/server-scm/pom.xml @@ -34,6 +34,16 @@ com.facebook.infer.annotation infer-annotation 0.18.0 + + + com.google.code.findbugs + jsr305 + + + org.jetbrains.kotlin + kotlin-annotations-jvm + + com.fasterxml.jackson.core diff --git a/hadoop-ozone/ozone-manager/pom.xml b/hadoop-ozone/ozone-manager/pom.xml index ec4aea14d6ec..1f438a58790a 100644 --- a/hadoop-ozone/ozone-manager/pom.xml +++ b/hadoop-ozone/ozone-manager/pom.xml @@ -34,6 +34,16 @@ com.facebook.infer.annotation infer-annotation 0.18.0 + + + com.google.code.findbugs + jsr305 + + + org.jetbrains.kotlin + kotlin-annotations-jvm + + com.fasterxml.jackson.core diff --git a/pom.xml b/pom.xml index ae092d591f91..395b737bede8 100644 --- a/pom.xml +++ b/pom.xml @@ -2312,6 +2312,7 @@ ${project.build.directory}/share/ozone/lib runtime ${classpath.skip} + infer-annotation From 73782a881e223cd07902ad797e64c9b46f121da5 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 29 Jun 2026 10:55:02 +0800 Subject: [PATCH 14/31] HDDS-15560. Skip Infer analysis in test and generated source paths --- hadoop-ozone/dev-support/checks/infer.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index 35d9620cdcbf..2b73a3b54f0b 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -34,7 +34,11 @@ MAVEN_OPTIONS='-B -DskipTests -DskipDocs -DskipRecon -DskipShade -Dsort.skip=tru git checkout -- hadoop-ozone/ozonefs-hadoop2/pom.xml hadoop-ozone/ozonefs-shaded/pom.xml 2>/dev/null || true mkdir -p "$REPORT_DIR" -infer run --keep-going -- mvn ${MAVEN_OPTIONS} install "$@" 2>&1 | tee "${REPORT_DIR}/output.log" +infer run --keep-going \ + --skip-analysis-in-path "src/test/" \ + --skip-analysis-in-path "target/generated-test-sources/" \ + --skip-analysis-in-path "target/generated-sources/" \ + -- mvn ${MAVEN_OPTIONS} install "$@" 2>&1 | tee "${REPORT_DIR}/output.log" infer_rc=$? mkdir -p "$REPORT_DIR" From 8b75a23b64305ce104438f4254befde32b24b8bf Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 29 Jun 2026 11:42:23 +0800 Subject: [PATCH 15/31] HDDS-15560. Exclude infer-annotation from dist module copy-dependencies --- hadoop-ozone/dist/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/hadoop-ozone/dist/pom.xml b/hadoop-ozone/dist/pom.xml index d399b1f9cbcd..bff8acbbc975 100644 --- a/hadoop-ozone/dist/pom.xml +++ b/hadoop-ozone/dist/pom.xml @@ -208,6 +208,7 @@ target/ozone-${ozone.version}/share/ozone/lib runtime + infer-annotation From 264fdc2c31f8e12a3ad7ba2c35d53378d77c7fb4 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Tue, 4 Aug 2026 13:58:41 +0800 Subject: [PATCH 16/31] HDDS-15560. Skip Infer analysis in test-utils module --- hadoop-ozone/dev-support/checks/infer.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index 2b73a3b54f0b..618b665ddf85 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -38,6 +38,7 @@ infer run --keep-going \ --skip-analysis-in-path "src/test/" \ --skip-analysis-in-path "target/generated-test-sources/" \ --skip-analysis-in-path "target/generated-sources/" \ + --skip-analysis-in-path "test-utils/" \ -- mvn ${MAVEN_OPTIONS} install "$@" 2>&1 | tee "${REPORT_DIR}/output.log" infer_rc=$? From 122d83b427e53fcf68a33fbd8e27d9f7b617e21b Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Tue, 4 Aug 2026 14:16:59 +0800 Subject: [PATCH 17/31] HDDS-15560. Gate Infer CI on relevant source changes via paths filter and selective checks - Add paths filter to infer.yml so the standalone workflow only triggers when Java sources, POMs, .inferconfig, or infer scripts change. - Add check_needs_infer to selective_ci_checks.sh so the basic matrix only runs infer when Java sources or infer config/scripts change. - Add .inferconfig to get_count_misc_files so it doesn't force a full CI run on unrelated changes. --- .github/workflows/infer.yml | 7 +++++++ dev-support/ci/selective_ci_checks.sh | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml index cfafae79996e..295bed810916 100644 --- a/.github/workflows/infer.yml +++ b/.github/workflows/infer.yml @@ -21,6 +21,13 @@ on: - master - 'HDDS-*' - 'HDDS/**' + paths: + - '**/pom.xml' + - '**/src/main/java/**' + - '.inferconfig' + - 'hadoop-ozone/dev-support/checks/infer.sh' + - 'hadoop-ozone/dev-support/checks/install/infer.sh' + - '.github/workflows/infer.yml' permissions: contents: read diff --git a/dev-support/ci/selective_ci_checks.sh b/dev-support/ci/selective_ci_checks.sh index 57e863470771..154e96caff72 100755 --- a/dev-support/ci/selective_ci_checks.sh +++ b/dev-support/ci/selective_ci_checks.sh @@ -444,6 +444,25 @@ function check_needs_pmd() { start_end::group_end } +function check_needs_infer() { + start_end::group_start "Check if infer is needed" + local pattern_array=( + "^.inferconfig$" + "^hadoop-ozone/dev-support/checks/infer.sh$" + "^hadoop-ozone/dev-support/checks/install/infer.sh$" + "^.github/workflows/infer.yml$" + "pom.xml$" + "src/..../java$" + ) + filter_changed_files + + if [[ ${match_count} != "0" ]]; then + add_basic_check infer + fi + + start_end::group_end +} + # Counts other files which do not need to trigger any functional test # (i.e. no compose/integration/kubernetes) @@ -462,6 +481,7 @@ function get_count_misc_files() { "\.bats$" "\.txt$" "\.md$" + ".inferconfig$" "findbugsExcludeFile.xml" "pmd-ruleset.xml" "/NOTICE$" @@ -582,5 +602,6 @@ check_needs_checkstyle check_needs_docs check_needs_findbugs check_needs_pmd +check_needs_infer calculate_test_types_to_run set_outputs From 9ba48ee300639c7c2b7ce8dae58a91328d61495d Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Tue, 4 Aug 2026 14:41:09 +0800 Subject: [PATCH 18/31] HDDS-15560. Annotate ContainerData as @ThreadSafe for RacerD analysis - Add @ThreadSafe annotation to ContainerData to enable inter-procedural thread-safety analysis by Infer's RacerD checker. - Add infer-annotation dependency to hdds-container-service module with transitive exclusions (jsr305, kotlin-annotations-jvm) consistent with hdds-server-scm and ozone-manager. --- hadoop-hdds/container-service/pom.xml | 15 +++++++++++++++ .../container/common/impl/ContainerData.java | 2 ++ 2 files changed, 17 insertions(+) diff --git a/hadoop-hdds/container-service/pom.xml b/hadoop-hdds/container-service/pom.xml index 489fd7e21391..564086a9cfb3 100644 --- a/hadoop-hdds/container-service/pom.xml +++ b/hadoop-hdds/container-service/pom.xml @@ -26,6 +26,21 @@ Apache Ozone Distributed Data Store Container Service + + com.facebook.infer.annotation + infer-annotation + 0.18.0 + + + com.google.code.findbugs + jsr305 + + + org.jetbrains.kotlin + kotlin-annotations-jvm + + + com.fasterxml.jackson.core jackson-annotations diff --git a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/ContainerData.java b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/ContainerData.java index 7dd8157ead69..100751fed812 100644 --- a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/ContainerData.java +++ b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/ContainerData.java @@ -28,6 +28,7 @@ import static org.apache.hadoop.ozone.OzoneConsts.ORIGIN_PIPELINE_ID; import static org.apache.hadoop.ozone.OzoneConsts.STATE; +import com.facebook.infer.annotation.ThreadSafe; import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; @@ -56,6 +57,7 @@ * ContainerData is the in-memory representation of container metadata and is * represented on disk by the .container file. */ +@ThreadSafe public abstract class ContainerData { //Type of the container. From 9860c9b03284efb66c1fd232300647e63e340987 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Tue, 4 Aug 2026 15:35:22 +0800 Subject: [PATCH 19/31] HDDS-15560. Switch Infer CI from JDK 21 to JDK 25 for consistency with other CI jobs --- .github/workflows/infer.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml index 295bed810916..4757286b5c3a 100644 --- a/.github/workflows/infer.yml +++ b/.github/workflows/infer.yml @@ -43,11 +43,11 @@ jobs: uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - - name: Setup java 21 + - name: Setup java 25 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: 'temurin' - java-version: '21' + java-version: '25' - name: Run Infer analysis run: hadoop-ozone/dev-support/checks/infer.sh - name: Upload Infer report From d2427e5fda502466b38f21866e37fd0edd29e7e2 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Tue, 18 Aug 2026 17:02:07 +0800 Subject: [PATCH 20/31] HDDS-15560. Fix Infer CI timeout and selective checks --- .github/workflows/ci.yml | 3 ++- dev-support/ci/selective_ci_checks.bats | 30 ++++++++++++------------- dev-support/ci/selective_ci_checks.sh | 4 ++-- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0243a62f65ea..570be2db018a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -198,6 +198,7 @@ jobs: needs: - build-info if: needs.build-info.outputs.needs-basic-check == 'true' + continue-on-error: ${{ matrix.check == 'infer' }} uses: ./.github/workflows/check.yml with: checkout-fetch-depth: ${{ matrix.check != 'bats' && 1 || 0 }} @@ -206,7 +207,7 @@ jobs: ratis-args: ${{ inputs.ratis_args }} script: ${{ matrix.check }} sha: ${{ needs.build-info.outputs.sha }} - timeout-minutes: 30 + timeout-minutes: ${{ matrix.check == 'infer' && 60 || 30 }} secrets: DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} strategy: diff --git a/dev-support/ci/selective_ci_checks.bats b/dev-support/ci/selective_ci_checks.bats index 35ab2a8f0880..505ccd11250c 100644 --- a/dev-support/ci/selective_ci_checks.bats +++ b/dev-support/ci/selective_ci_checks.bats @@ -80,7 +80,7 @@ load bats-assert/load.bash @test "runner image update" { run dev-support/ci/selective_ci_checks.sh b95eeba82a - assert_output -p 'basic-checks=["rat"]' + assert_output -p 'basic-checks=["rat","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -102,7 +102,7 @@ load bats-assert/load.bash @test "java test + pmd change" { run dev-support/ci/selective_ci_checks.sh 250bd5f317 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -113,7 +113,7 @@ load bats-assert/load.bash @test "integration and unit: java change" { run dev-support/ci/selective_ci_checks.sh 9aebf6e25 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -157,7 +157,7 @@ load bats-assert/load.bash @test "unit only" { run dev-support/ci/selective_ci_checks.sh 1dd1d0ba3 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -168,7 +168,7 @@ load bats-assert/load.bash @test "unit helper" { run dev-support/ci/selective_ci_checks.sh 88383d1d5 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -179,7 +179,7 @@ load bats-assert/load.bash @test "integration only" { run dev-support/ci/selective_ci_checks.sh 61396ba9f - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -190,7 +190,7 @@ load bats-assert/load.bash @test "mini-cluster" { run dev-support/ci/selective_ci_checks.sh f0388a195411e68aac360de8ab95735b2eb295de - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -201,7 +201,7 @@ load bats-assert/load.bash @test "test-utils" { run dev-support/ci/selective_ci_checks.sh 60e4ab121853c182e1272b04fd1a93d55b12cfc7 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -212,7 +212,7 @@ load bats-assert/load.bash @test "native only" { run dev-support/ci/selective_ci_checks.sh 5b1319a8c2 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -223,7 +223,7 @@ load bats-assert/load.bash @test "native test in other module" { run dev-support/ci/selective_ci_checks.sh 822c0dee1a - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -256,7 +256,7 @@ load bats-assert/load.bash @test "main/java change" { run dev-support/ci/selective_ci_checks.sh 86a771dfe - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -267,7 +267,7 @@ load bats-assert/load.bash @test "..../java change" { run dev-support/ci/selective_ci_checks.sh 01c616536 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -278,7 +278,7 @@ load bats-assert/load.bash @test "java and compose change" { run dev-support/ci/selective_ci_checks.sh d0f0f806e - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -289,7 +289,7 @@ load bats-assert/load.bash @test "java and docs change" { run dev-support/ci/selective_ci_checks.sh 2c0adac26 - assert_output -p 'basic-checks=["rat","author","checkstyle","docs","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","docs","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -300,7 +300,7 @@ load bats-assert/load.bash @test "pom change" { run dev-support/ci/selective_ci_checks.sh 9129424a9 - assert_output -p 'basic-checks=["rat","checkstyle","findbugs","pmd"]' + assert_output -p 'basic-checks=["rat","checkstyle","findbugs","pmd","infer"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true diff --git a/dev-support/ci/selective_ci_checks.sh b/dev-support/ci/selective_ci_checks.sh index 154e96caff72..874966aafe0e 100755 --- a/dev-support/ci/selective_ci_checks.sh +++ b/dev-support/ci/selective_ci_checks.sh @@ -451,8 +451,8 @@ function check_needs_infer() { "^hadoop-ozone/dev-support/checks/infer.sh$" "^hadoop-ozone/dev-support/checks/install/infer.sh$" "^.github/workflows/infer.yml$" - "pom.xml$" - "src/..../java$" + "pom.xml" + "src/..../java" ) filter_changed_files From 95a41f6a3f89a9e1b4659165216e496dff7236b5 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Tue, 18 Aug 2026 17:13:31 +0800 Subject: [PATCH 21/31] HDDS-15560. Fix reusable Infer CI workflow syntax --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 570be2db018a..6c363cb0ffae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -198,7 +198,6 @@ jobs: needs: - build-info if: needs.build-info.outputs.needs-basic-check == 'true' - continue-on-error: ${{ matrix.check == 'infer' }} uses: ./.github/workflows/check.yml with: checkout-fetch-depth: ${{ matrix.check != 'bats' && 1 || 0 }} From 2c96cb72777d5e2537cb61045a1221f7fad9334f Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Tue, 18 Aug 2026 17:53:04 +0800 Subject: [PATCH 22/31] HDDS-15560. Run Infer with approval-gated CI --- .github/workflows/infer.yml | 70 --------------------------- dev-support/ci/selective_ci_checks.sh | 1 - 2 files changed, 71 deletions(-) delete mode 100644 .github/workflows/infer.yml diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml deleted file mode 100644 index 4757286b5c3a..000000000000 --- a/.github/workflows/infer.yml +++ /dev/null @@ -1,70 +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. - -name: infer - -on: - push: - branches: - - master - - 'HDDS-*' - - 'HDDS/**' - paths: - - '**/pom.xml' - - '**/src/main/java/**' - - '.inferconfig' - - 'hadoop-ozone/dev-support/checks/infer.sh' - - 'hadoop-ozone/dev-support/checks/install/infer.sh' - - '.github/workflows/infer.yml' - -permissions: - contents: read - -jobs: - infer: - name: infer - runs-on: ubuntu-24.04 - continue-on-error: true - timeout-minutes: 60 - steps: - - name: Checkout project - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - with: - persist-credentials: false - - name: Setup java 25 - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 - with: - distribution: 'temurin' - java-version: '25' - - name: Run Infer analysis - run: hadoop-ozone/dev-support/checks/infer.sh - - name: Upload Infer report - if: ${{ !cancelled() }} - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: infer-report - path: | - target/infer/report.txt - target/infer/output.log - target/infer/summary.txt - target/infer/failures - continue-on-error: true - - name: Summary of failures - if: ${{ failure() }} - run: | - if [[ -s "target/infer/summary.md" ]]; then - cat target/infer/summary.md >> $GITHUB_STEP_SUMMARY - fi - hadoop-ozone/dev-support/checks/_summary.sh target/infer/summary.txt diff --git a/dev-support/ci/selective_ci_checks.sh b/dev-support/ci/selective_ci_checks.sh index 874966aafe0e..1ac280670769 100755 --- a/dev-support/ci/selective_ci_checks.sh +++ b/dev-support/ci/selective_ci_checks.sh @@ -450,7 +450,6 @@ function check_needs_infer() { "^.inferconfig$" "^hadoop-ozone/dev-support/checks/infer.sh$" "^hadoop-ozone/dev-support/checks/install/infer.sh$" - "^.github/workflows/infer.yml$" "pom.xml" "src/..../java" ) From a1373ab18e780188e94c2ff0c90568f4608e51c3 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Tue, 18 Aug 2026 17:56:56 +0800 Subject: [PATCH 23/31] HDDS-15560. Run Infer outside basic CI checks --- .github/workflows/ci.yml | 18 ++++++++++- dev-support/ci/selective_ci_checks.bats | 41 +++++++++++++----------- dev-support/ci/selective_ci_checks.sh | 5 ++- hadoop-ozone/dev-support/checks/infer.sh | 2 -- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c363cb0ffae..c7b04e7be669 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,6 +61,7 @@ jobs: needs-build: ${{ steps.selective-checks.outputs.needs-build }} needs-compile: ${{ steps.selective-checks.outputs.needs-compile }} needs-compose-tests: ${{ steps.selective-checks.outputs.needs-compose-tests }} + needs-infer: ${{ steps.selective-checks.outputs.needs-infer }} needs-integration-tests: ${{ steps.selective-checks.outputs.needs-integration-tests }} needs-kubernetes-tests: ${{ steps.selective-checks.outputs.needs-kubernetes-tests }} sha: ${{ steps.get-sha.outputs.sha }} @@ -206,7 +207,7 @@ jobs: ratis-args: ${{ inputs.ratis_args }} script: ${{ matrix.check }} sha: ${{ needs.build-info.outputs.sha }} - timeout-minutes: ${{ matrix.check == 'infer' && 60 || 30 }} + timeout-minutes: 30 secrets: DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} strategy: @@ -214,6 +215,21 @@ jobs: check: ${{ fromJson(needs.build-info.outputs.basic-checks) }} fail-fast: false + infer: + needs: + - build-info + if: needs.build-info.outputs.needs-infer == 'true' + uses: ./.github/workflows/check.yml + with: + java-version: ${{ needs.build-info.outputs.java-version }} + needs-maven-cache: true + ratis-args: ${{ inputs.ratis_args }} + script: infer + sha: ${{ needs.build-info.outputs.sha }} + timeout-minutes: 60 + secrets: + DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} + dependency: needs: - build-info diff --git a/dev-support/ci/selective_ci_checks.bats b/dev-support/ci/selective_ci_checks.bats index 505ccd11250c..dbc33b00487e 100644 --- a/dev-support/ci/selective_ci_checks.bats +++ b/dev-support/ci/selective_ci_checks.bats @@ -51,6 +51,7 @@ load bats-assert/load.bash assert_output -p needs-build=true assert_output -p needs-compile=false assert_output -p needs-compose-tests=true + assert_output -p needs-infer=false assert_output -p needs-integration-tests=false assert_output -p needs-kubernetes-tests=false } @@ -80,10 +81,11 @@ load bats-assert/load.bash @test "runner image update" { run dev-support/ci/selective_ci_checks.sh b95eeba82a - assert_output -p 'basic-checks=["rat","infer"]' + assert_output -p 'basic-checks=["rat"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true + assert_output -p needs-infer=true assert_output -p needs-integration-tests=false assert_output -p needs-kubernetes-tests=true } @@ -102,7 +104,7 @@ load bats-assert/load.bash @test "java test + pmd change" { run dev-support/ci/selective_ci_checks.sh 250bd5f317 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -113,7 +115,7 @@ load bats-assert/load.bash @test "integration and unit: java change" { run dev-support/ci/selective_ci_checks.sh 9aebf6e25 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -157,7 +159,7 @@ load bats-assert/load.bash @test "unit only" { run dev-support/ci/selective_ci_checks.sh 1dd1d0ba3 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -168,7 +170,7 @@ load bats-assert/load.bash @test "unit helper" { run dev-support/ci/selective_ci_checks.sh 88383d1d5 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -179,7 +181,7 @@ load bats-assert/load.bash @test "integration only" { run dev-support/ci/selective_ci_checks.sh 61396ba9f - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -190,7 +192,7 @@ load bats-assert/load.bash @test "mini-cluster" { run dev-support/ci/selective_ci_checks.sh f0388a195411e68aac360de8ab95735b2eb295de - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -201,7 +203,7 @@ load bats-assert/load.bash @test "test-utils" { run dev-support/ci/selective_ci_checks.sh 60e4ab121853c182e1272b04fd1a93d55b12cfc7 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -212,7 +214,7 @@ load bats-assert/load.bash @test "native only" { run dev-support/ci/selective_ci_checks.sh 5b1319a8c2 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -223,7 +225,7 @@ load bats-assert/load.bash @test "native test in other module" { run dev-support/ci/selective_ci_checks.sh 822c0dee1a - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false @@ -256,10 +258,11 @@ load bats-assert/load.bash @test "main/java change" { run dev-support/ci/selective_ci_checks.sh 86a771dfe - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true + assert_output -p needs-infer=true assert_output -p needs-integration-tests=true assert_output -p needs-kubernetes-tests=true } @@ -267,7 +270,7 @@ load bats-assert/load.bash @test "..../java change" { run dev-support/ci/selective_ci_checks.sh 01c616536 - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -278,7 +281,7 @@ load bats-assert/load.bash @test "java and compose change" { run dev-support/ci/selective_ci_checks.sh d0f0f806e - assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -289,7 +292,7 @@ load bats-assert/load.bash @test "java and docs change" { run dev-support/ci/selective_ci_checks.sh 2c0adac26 - assert_output -p 'basic-checks=["rat","author","checkstyle","docs","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","author","checkstyle","docs","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -300,7 +303,7 @@ load bats-assert/load.bash @test "pom change" { run dev-support/ci/selective_ci_checks.sh 9129424a9 - assert_output -p 'basic-checks=["rat","checkstyle","findbugs","pmd","infer"]' + assert_output -p 'basic-checks=["rat","checkstyle","findbugs","pmd"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -311,7 +314,7 @@ load bats-assert/load.bash @test "CI lib change" { run dev-support/ci/selective_ci_checks.sh ceb79acaa - assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","infer","pmd","rat"]' + assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","pmd","rat"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -322,7 +325,7 @@ load bats-assert/load.bash @test "CI workflow change" { run dev-support/ci/selective_ci_checks.sh 90a8d7c01 - assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","infer","pmd","rat"]' + assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","pmd","rat"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -345,7 +348,7 @@ load bats-assert/load.bash @test "CI workflow change (check.yml)" { run dev-support/ci/selective_ci_checks.sh 1468af02067ec75b255f605816c32f8bf4dfaabf - assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","infer","pmd","rat"]' + assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","pmd","rat"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true @@ -356,7 +359,7 @@ load bats-assert/load.bash @test "CI workflow change (ci.yaml)" { run dev-support/ci/selective_ci_checks.sh 90fd5f2adc - assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","infer","pmd","rat"]' + assert_output -p 'basic-checks=["author","bats","checkstyle","docs","findbugs","pmd","rat"]' assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true diff --git a/dev-support/ci/selective_ci_checks.sh b/dev-support/ci/selective_ci_checks.sh index 1ac280670769..0c1d9ead5a09 100755 --- a/dev-support/ci/selective_ci_checks.sh +++ b/dev-support/ci/selective_ci_checks.sh @@ -87,6 +87,7 @@ function set_outputs_run_everything_and_exit() { | cut -f1 -d'.') compile_needed=true compose_tests_needed=true + infer_needed=true integration_tests_needed=true kubernetes_tests_needed=true @@ -98,6 +99,7 @@ function set_outputs_run_everything_and_exit() { function set_output_skip_all_tests_and_exit() { BASIC_CHECKS="" compose_tests_needed=false + infer_needed=false integration_tests_needed=false kubernetes_tests_needed=false @@ -456,7 +458,7 @@ function check_needs_infer() { filter_changed_files if [[ ${match_count} != "0" ]]; then - add_basic_check infer + infer_needed=true fi start_end::group_end @@ -555,6 +557,7 @@ function set_outputs() { initialization::ga_output needs-build "${build_needed:-false}" initialization::ga_output needs-compile "${compile_needed}" initialization::ga_output needs-compose-tests "${compose_tests_needed}" + initialization::ga_output needs-infer "${infer_needed:-false}" initialization::ga_output needs-integration-tests "${integration_tests_needed}" initialization::ga_output needs-kubernetes-tests "${kubernetes_tests_needed}" } diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index 618b665ddf85..8fb643a3d659 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -14,8 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -#checks:basic - set -u -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" From d0d8b1367df2f9534c562bcbf89a91e0ab6ca013 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Wed, 19 Aug 2026 13:02:46 +0800 Subject: [PATCH 24/31] HDDS-15560. Exclude test sources from Infer analysis --- hadoop-ozone/dev-support/checks/infer.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index 8fb643a3d659..059ec12644cf 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -33,10 +33,10 @@ git checkout -- hadoop-ozone/ozonefs-hadoop2/pom.xml hadoop-ozone/ozonefs-shaded mkdir -p "$REPORT_DIR" infer run --keep-going \ - --skip-analysis-in-path "src/test/" \ - --skip-analysis-in-path "target/generated-test-sources/" \ - --skip-analysis-in-path "target/generated-sources/" \ - --skip-analysis-in-path "test-utils/" \ + --skip-analysis-in-path '.*/src/test/.*' \ + --skip-analysis-in-path '.*/target/generated-test-sources/.*' \ + --skip-analysis-in-path '.*/target/generated-sources/.*' \ + --skip-analysis-in-path '.*/test-utils/.*' \ -- mvn ${MAVEN_OPTIONS} install "$@" 2>&1 | tee "${REPORT_DIR}/output.log" infer_rc=$? From 27c4f80bb2e76b82a1d11cbdcfad56ccf071029a Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Wed, 19 Aug 2026 13:50:33 +0800 Subject: [PATCH 25/31] HDDS-15560. Validate GDPR key metadata in RpcClient --- .../apache/hadoop/ozone/client/rpc/RpcClient.java | 9 +++++++-- .../hadoop/ozone/client/rpc/TestRpcClient.java | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java index 01b7b8d97964..748f2ccbacb7 100644 --- a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java +++ b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java @@ -2609,9 +2609,14 @@ static GDPRSymmetricKey getGDPRSymmetricKey(Map metadata, return null; } + String secret = metadata.get(OzoneConsts.GDPR_SECRET); + String algorithm = metadata.get(OzoneConsts.GDPR_ALGORITHM); + if (secret == null || algorithm == null) { + throw new IOException("GDPR metadata is missing secret or algorithm"); + } + final GDPRSymmetricKey gk = new GDPRSymmetricKey( - metadata.get(OzoneConsts.GDPR_SECRET), - metadata.get(OzoneConsts.GDPR_ALGORITHM)); + secret, algorithm); try { gk.getCipher().init(mode, gk.getSecretKey()); } catch (InvalidKeyException e) { diff --git a/hadoop-ozone/client/src/test/java/org/apache/hadoop/ozone/client/rpc/TestRpcClient.java b/hadoop-ozone/client/src/test/java/org/apache/hadoop/ozone/client/rpc/TestRpcClient.java index 999b892ff7bb..58fe8fe96da7 100644 --- a/hadoop-ozone/client/src/test/java/org/apache/hadoop/ozone/client/rpc/TestRpcClient.java +++ b/hadoop-ozone/client/src/test/java/org/apache/hadoop/ozone/client/rpc/TestRpcClient.java @@ -24,11 +24,15 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import javax.crypto.Cipher; import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.protocol.proto.HddsProtos; import org.apache.hadoop.hdds.scm.XceiverClientFactory; +import org.apache.hadoop.ozone.OzoneConsts; import org.apache.hadoop.ozone.OzoneManagerVersion; import org.apache.hadoop.ozone.client.MockOmTransport; import org.apache.hadoop.ozone.client.MockXceiverClientFactory; @@ -228,6 +232,16 @@ public void testFutureVersionShouldNotBeAnExpectedVersion() { () -> validateOmVersion(OzoneManagerVersion.FUTURE_VERSION, null)); } + @Test + public void testGetGDPRSymmetricKeyRejectsIncompleteMetadata() { + Map metadata = new HashMap<>(); + metadata.put(OzoneConsts.GDPR_FLAG, Boolean.TRUE.toString()); + + IOException ex = assertThrows(IOException.class, + () -> RpcClient.getGDPRSymmetricKey(metadata, Cipher.DECRYPT_MODE)); + assertThat(ex).hasMessage("GDPR metadata is missing secret or algorithm"); + } + @Test public void testCloseTwiceDoesNotWarn() throws IOException { RpcClient rpcClient = createRpcClient(); From a100798a336a363883798558231b6d2b2c610882 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Wed, 19 Aug 2026 17:34:58 +0800 Subject: [PATCH 26/31] HDDS-15560. Exclude Infer annotation from classpath descriptors --- hadoop-ozone/datanode/pom.xml | 1 + pom.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/hadoop-ozone/datanode/pom.xml b/hadoop-ozone/datanode/pom.xml index 72916b644d90..b2802cd7955d 100644 --- a/hadoop-ozone/datanode/pom.xml +++ b/hadoop-ozone/datanode/pom.xml @@ -72,6 +72,7 @@ $HDDS_LIB_JARS_DIR true runtime + infer-annotation diff --git a/pom.xml b/pom.xml index 145c65cfb78b..f33212cec815 100644 --- a/pom.xml +++ b/pom.xml @@ -2299,6 +2299,7 @@ $HDDS_LIB_JARS_DIR true runtime + infer-annotation ${classpath.skip} From b198707230f7819172a296f18cc7e119566031bd Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 19 Oct 2026 11:03:38 +0800 Subject: [PATCH 27/31] HDDS-15560. Preserve local ozonefs POM changes when running Infer --- dev-support/ci/infer.bats | 127 +++++++++++++++++++++++ hadoop-ozone/dev-support/checks/bats.sh | 1 + hadoop-ozone/dev-support/checks/infer.sh | 41 ++++++-- 3 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 dev-support/ci/infer.bats diff --git a/dev-support/ci/infer.bats b/dev-support/ci/infer.bats new file mode 100644 index 000000000000..de0efd8468e5 --- /dev/null +++ b/dev-support/ci/infer.bats @@ -0,0 +1,127 @@ +#!/usr/bin/env bats +# 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. + +setup() { + TEST_TMPDIR=$(mktemp -d) + export TEST_TMPDIR + local checks=hadoop-ozone/dev-support/checks + mkdir -p "${TEST_TMPDIR}/${checks}/install" "${TEST_TMPDIR}/bin" + cp "${checks}/infer.sh" "${checks}/_lib.sh" "${TEST_TMPDIR}/${checks}/" + cp "${checks}/install/infer.sh" "${TEST_TMPDIR}/${checks}/install/" + cd "${TEST_TMPDIR}" + POMS=(hadoop-ozone/ozonefs-hadoop2/pom.xml hadoop-ozone/ozonefs-shaded/pom.xml) + for pom in "${POMS[@]}"; do + mkdir -p "$(dirname "${pom}")" + printf '%s\n' '' > "${pom}" + done + git init -q + git add hadoop-ozone + git -c user.name=Test -c user.email=test@example.com -c core.hooksPath=/dev/null commit -qm initial + printf '%s\n' \ + '#!/usr/bin/env bash' \ + 'touch "${TEST_TMPDIR}/infer-called"' \ + 'printf "%s\n" modified > hadoop-ozone/ozonefs-hadoop2/pom.xml' \ + 'printf "%s\n" modified > hadoop-ozone/ozonefs-shaded/pom.xml' \ + 'if [[ -n "${INFER_SIGNAL:-}" ]]; then kill -"${INFER_SIGNAL}" "$PPID"; fi' \ + 'exit "${INFER_EXIT_CODE:-0}"' > bin/infer + chmod +x bin/infer + export PATH="${TEST_TMPDIR}/bin:${PATH}" + export OZONE_PREFER_LOCAL_TOOL=true +} + +teardown() { + cd / + rm -rf "${TEST_TMPDIR}" +} + +@test "Infer refuses unstaged POM edits without changing either file" { + printf '%s\n' 'local edits' >> "${POMS[0]}" + cp "${POMS[0]}" expected + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 1 ] + [ ! -e infer-called ] + cmp expected "${POMS[0]}" + git diff --quiet -- "${POMS[1]}" +} + +@test "Infer refuses staged POM edits without changing the index or worktree" { + printf '%s\n' 'staged edits' >> "${POMS[1]}" + git add "${POMS[1]}" + cp "${POMS[1]}" expected + local index_before + index_before=$(git write-tree) + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 1 ] + [ ! -e infer-called ] + cmp expected "${POMS[1]}" + [ "$(git write-tree)" = "${index_before}" ] +} + +@test "Infer refuses a deleted POM without recreating it" { + rm "${POMS[0]}" + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 1 ] + [ ! -e infer-called ] + [ ! -e "${POMS[0]}" ] +} + +@test "Infer restores POMs after successful analysis and preserves unrelated edits" { + printf '%s\n' '# local edits' >> hadoop-ozone/dev-support/checks/_lib.sh + cp hadoop-ozone/dev-support/checks/_lib.sh expected + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 0 ] + [ -e infer-called ] + git diff --exit-code -- "${POMS[@]}" + cmp expected hadoop-ozone/dev-support/checks/_lib.sh +} + +@test "Infer restores POMs after an analysis failure" { + export INFER_EXIT_CODE=2 + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 1 ] + [ -e infer-called ] + git diff --exit-code -- "${POMS[@]}" +} + +@test "Infer restores POMs when interrupted by TERM" { + export INFER_SIGNAL=TERM + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 143 ] + [ -e infer-called ] + git diff --exit-code -- "${POMS[@]}" +} + +@test "Infer restores POMs when interrupted by INT" { + export INFER_SIGNAL=INT + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 130 ] + [ -e infer-called ] + git diff --exit-code -- "${POMS[@]}" +} diff --git a/hadoop-ozone/dev-support/checks/bats.sh b/hadoop-ozone/dev-support/checks/bats.sh index 839b4dd11e69..5e784332d48a 100755 --- a/hadoop-ozone/dev-support/checks/bats.sh +++ b/hadoop-ozone/dev-support/checks/bats.sh @@ -33,6 +33,7 @@ rm -f "${REPORT_DIR}/output.log" find * \( \ -path '*/src/test/shell/*' -name '*.bats' \ -or -path dev-support/ci/errorprone.bats \ + -or -path dev-support/ci/infer.bats \ -or -path dev-support/ci/selective_ci_checks.bats \ -or -path dev-support/ci/pr_title_check.bats \ -or -path dev-support/ci/find_test_class_project.bats \ diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index 059ec12644cf..c6c5dba0781e 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -19,6 +19,40 @@ set -u -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" cd "$DIR/../../.." || exit 1 +POM_FILES=(hadoop-ozone/ozonefs-hadoop2/pom.xml hadoop-ozone/ozonefs-shaded/pom.xml) +if ! git diff --quiet -- "${POM_FILES[@]}" || ! git diff --cached --quiet -- "${POM_FILES[@]}"; then + echo "Infer requires clean ozonefs POMs. Commit or stash changes to these files first:" >&2 + printf ' %s\n' "${POM_FILES[@]}" >&2 + exit 1 +fi + +POM_BACKUP_DIR=$(mktemp -d) || exit 1 + +restore_poms() { + local rc=$? + local restore_failed=false + local i + for i in "${!POM_FILES[@]}"; do + if [[ -f "${POM_BACKUP_DIR}/${i}" ]] && ! cp "${POM_BACKUP_DIR}/${i}" "${POM_FILES[$i]}"; then + restore_failed=true + fi + done + if [[ "${restore_failed}" == "true" ]]; then + echo "Failed to restore ozonefs POMs. Original files are saved in ${POM_BACKUP_DIR}" >&2 + rc=1 + else + rm -rf "${POM_BACKUP_DIR}" + fi + exit "${rc}" +} + +trap restore_poms EXIT +trap 'exit 130' INT +trap 'exit 143' TERM +for i in "${!POM_FILES[@]}"; do + cp "${POM_FILES[$i]}" "${POM_BACKUP_DIR}/${i}" || exit 1 +done + source "${DIR}/_lib.sh" source "${DIR}/install/infer.sh" @@ -27,10 +61,6 @@ REPORT_FILE="$REPORT_DIR/summary.txt" MAVEN_OPTIONS='-B -DskipTests -DskipDocs -DskipRecon -DskipShade -Dsort.skip=true --no-transfer-progress' -# Restore pom.xml files that may have been corrupted by a previous infer run -# (infer injects profiles into pom.xml and sometimes fails to restore them). -git checkout -- hadoop-ozone/ozonefs-hadoop2/pom.xml hadoop-ozone/ozonefs-shaded/pom.xml 2>/dev/null || true - mkdir -p "$REPORT_DIR" infer run --keep-going \ --skip-analysis-in-path '.*/src/test/.*' \ @@ -51,9 +81,6 @@ elif [[ -d infer-out ]]; then find infer-out -name "report.txt" -exec cp {} "${REPORT_DIR}/" \; 2>/dev/null || true fi -# Restore pom.xml files again after infer (belt and suspenders) -git checkout -- hadoop-ozone/ozonefs-hadoop2/pom.xml hadoop-ozone/ozonefs-shaded/pom.xml 2>/dev/null || true - touch "$REPORT_FILE" if [[ -f "${REPORT_DIR}/report.txt" ]]; then From 8aca4556946cc10152d8e3afe59fb180fd654287 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 19 Oct 2026 11:22:18 +0800 Subject: [PATCH 28/31] HDDS-15560. Fix Infer capture, reporting and selective CI checks --- dev-support/ci/infer.bats | 111 +++++++++++++++++++++++ dev-support/ci/selective_ci_checks.bats | 76 ++++++++++++++++ dev-support/ci/selective_ci_checks.sh | 10 +- hadoop-ozone/dev-support/checks/infer.sh | 83 ++++++++++------- 4 files changed, 246 insertions(+), 34 deletions(-) diff --git a/dev-support/ci/infer.bats b/dev-support/ci/infer.bats index de0efd8468e5..cb82ab9ff833 100644 --- a/dev-support/ci/infer.bats +++ b/dev-support/ci/infer.bats @@ -32,7 +32,19 @@ setup() { git -c user.name=Test -c user.email=test@example.com -c core.hooksPath=/dev/null commit -qm initial printf '%s\n' \ '#!/usr/bin/env bash' \ + 'if [[ "$1" == debug ]]; then' \ + ' if [[ "${INFER_EMPTY_CAPTURE:-false}" != true ]]; then echo src/main/java/Example.java; fi' \ + ' exit 0' \ + 'fi' \ 'touch "${TEST_TMPDIR}/infer-called"' \ + 'printf "%s\n" "$@" > "${TEST_TMPDIR}/infer-args"' \ + 'for arg in "$@"; do if [[ "$arg" == clean ]]; then rm -rf target; fi; done' \ + 'if [[ "${INFER_NO_REPORT:-false}" != true ]]; then' \ + ' mkdir -p infer-out' \ + ' printf "%s\n" "${INFER_REPORT:-No issues found}" > infer-out/report.txt' \ + ' printf "%s\n" "${INFER_JSON-[]}" > infer-out/report.json' \ + 'fi' \ + 'echo "Infer output"' \ 'printf "%s\n" modified > hadoop-ozone/ozonefs-hadoop2/pom.xml' \ 'printf "%s\n" modified > hadoop-ozone/ozonefs-shaded/pom.xml' \ 'if [[ -n "${INFER_SIGNAL:-}" ]]; then kill -"${INFER_SIGNAL}" "$PPID"; fi' \ @@ -125,3 +137,102 @@ teardown() { [ -e infer-called ] git diff --exit-code -- "${POMS[@]}" } + +@test "Infer cleans the build without losing the output log" { + mkdir -p target + touch target/previous-build + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 0 ] + [ ! -e target/previous-build ] + grep -q 'Infer output' target/infer/output.log + grep -qx -- '-Ddevelocity.cache.local.enabled=false' infer-args + grep -qx -- '-Ddevelocity.cache.remote.enabled=false' infer-args +} + +@test "Infer failures are not hidden by a fresh report" { + export INFER_EXIT_CODE=5 + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 1 ] + grep -q 'code 5' target/infer/summary.txt + [ -f target/infer/report.txt ] +} + +@test "Infer failures do not reuse previous reports" { + mkdir -p target/infer infer-out + printf '%s\n' stale > target/infer/report.txt + printf '%s\n' stale > infer-out/report.txt + export INFER_EXIT_CODE=5 INFER_NO_REPORT=true + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 1 ] + [ ! -e target/infer/report.txt ] + grep -q 'code 5' target/infer/summary.txt +} + +@test "Infer requires a report even when the command succeeds" { + export INFER_NO_REPORT=true + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 1 ] + grep -q 'no report' target/infer/summary.txt +} + +@test "Infer rejects empty capture instead of reporting a clean analysis" { + export INFER_EMPTY_CAPTURE=true + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 1 ] + grep -q 'no source files' target/infer/summary.txt +} + +@test "Infer clean reports contain a single zero count" { + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 0 ] + [ "$(cat target/infer/failures)" = 0 ] + grep -qx 'Infer analysis complete. Found 0 issues.' target/infer/summary.txt +} + +@test "Infer counts findings from JSON and keeps them informational" { + export INFER_REPORT='Example.java:1: error: Null Dereference' + export INFER_JSON='[{"severity":"ERROR"},{"severity":"WARNING"},{"suppressed":true},{"censored_reason":"filtered"}]' + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 0 ] + [ "$(cat target/infer/failures)" = 2 ] + cmp infer-out/report.json target/infer/report.json +} + +@test "Infer rejects an invalid JSON report" { + local report + for report in 'invalid JSON' '' '[] []' '{}' 'null'; do + export INFER_JSON="${report}" + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 1 ] + grep -q 'invalid JSON report' target/infer/summary.txt + done +} + +@test "Infer installation failures preserve diagnostics in the artifact" { + export OZONE_PREFER_LOCAL_TOOL=false + printf '%s\n' '#!/usr/bin/env bash' 'echo "download failed" >&2' 'exit 22' > bin/curl + chmod +x bin/curl + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 1 ] + [ ! -e infer-called ] + grep -q 'Failed to install infer' target/infer/summary.txt + grep -q 'download failed' target/infer/output.log + ! grep -q 'unbound variable' target/infer/output.log + git diff --exit-code -- "${POMS[@]}" +} diff --git a/dev-support/ci/selective_ci_checks.bats b/dev-support/ci/selective_ci_checks.bats index f276d46a7577..cc230236a6a5 100644 --- a/dev-support/ci/selective_ci_checks.bats +++ b/dev-support/ci/selective_ci_checks.bats @@ -33,6 +33,77 @@ load bats-support/load.bash load bats-assert/load.bash +function run_with_changed_file() { + local file="$1" + local repo + repo=$(mktemp -d) + git -C "${repo}" init -q + git -C "${repo}" config user.name "Selective CI test" + git -C "${repo}" config user.email "test@example.com" + git -C "${repo}" -c core.hooksPath=/dev/null -c commit.gpgsign=false commit -q --allow-empty -m "Base" + mkdir -p "${repo}/$(dirname "${file}")" + touch "${repo}/${file}" + git -C "${repo}" add "${file}" + git -C "${repo}" -c core.hooksPath=/dev/null -c commit.gpgsign=false commit -q -m "Change ${file}" + + run env GIT_DIR="${repo}/.git" GIT_WORK_TREE="${repo}" dev-support/ci/selective_ci_checks.sh HEAD + rm -rf "${repo}" +} + +@test "Infer configuration only" { + run_with_changed_file .inferconfig + + assert_success + assert_output -p 'basic-checks=["rat"]' + assert_output -p needs-build=false + assert_output -p needs-compile=false + assert_output -p needs-compose-tests=false + assert_output -p needs-infer=true + assert_output -p needs-integration-tests=false + assert_output -p needs-kubernetes-tests=false +} + +@test "Infer scripts" { + local file + for file in hadoop-ozone/dev-support/checks/infer.sh hadoop-ozone/dev-support/checks/install/infer.sh; do + run_with_changed_file "${file}" + + assert_success + assert_output -p needs-infer=true + assert_output -p needs-compose-tests=false + assert_output -p needs-integration-tests=false + assert_output -p needs-kubernetes-tests=false + done +} + +@test "Infer runs for test-utils POM changes" { + run_with_changed_file hadoop-hdds/test-utils/pom.xml + + assert_success + assert_output -p needs-infer=true +} + +@test "Infer skips test-utils Java in other modules" { + run_with_changed_file hadoop-ozone/test-utils/src/main/java/org/apache/ozone/test/TestHelper.java + + assert_success + assert_output -p needs-infer=false +} + +@test "push runs Infer" { + run dev-support/ci/selective_ci_checks.sh + + assert_success + assert_output -p needs-infer=true +} + +@test "full tests needed label runs Infer for test-only changes" { + PR_LABELS="full tests needed" run dev-support/ci/selective_ci_checks.sh bf59f31a7e5fcad1acc85e1228d77c22ee7f036c + + assert_success + assert_output -p needs-infer=true +} + @test "checkstyle and bats" { run dev-support/ci/selective_ci_checks.sh 11b098430 @@ -144,6 +215,7 @@ load bats-assert/load.bash assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false + assert_output -p needs-infer=false assert_output -p needs-integration-tests=false assert_output -p needs-kubernetes-tests=false assert_output -p test-classes=org/apache/ozone/test/Test*,org/apache/hadoop/ozone/container/TestContainerReportHandling.java,org/apache/hadoop/ozone/container/TestContainerReportHandlingWithHA.java,org/apache/hadoop/ozone/container/TestHelper.java @@ -203,6 +275,7 @@ load bats-assert/load.bash assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false + assert_output -p needs-infer=false assert_output -p needs-integration-tests=true assert_output -p needs-kubernetes-tests=false assert_output -p test-classes= @@ -239,6 +312,7 @@ load bats-assert/load.bash assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=false + assert_output -p needs-infer=false assert_output -p needs-integration-tests=true assert_output -p needs-kubernetes-tests=false assert_output -p test-classes= @@ -341,6 +415,7 @@ load bats-assert/load.bash assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true + assert_output -p needs-infer=true assert_output -p needs-integration-tests=true assert_output -p needs-kubernetes-tests=true } @@ -352,6 +427,7 @@ load bats-assert/load.bash assert_output -p needs-build=true assert_output -p needs-compile=true assert_output -p needs-compose-tests=true + assert_output -p needs-infer=true assert_output -p needs-integration-tests=true assert_output -p needs-kubernetes-tests=true } diff --git a/dev-support/ci/selective_ci_checks.sh b/dev-support/ci/selective_ci_checks.sh index 7de819e8e121..c30b93dea3b3 100755 --- a/dev-support/ci/selective_ci_checks.sh +++ b/dev-support/ci/selective_ci_checks.sh @@ -172,6 +172,7 @@ function filter_changed_files() { SOURCES_TRIGGERING_TESTS=( "^.github" + "^\.inferconfig$" "^dev-support" "^hadoop-hdds" "^hadoop-ozone" @@ -519,11 +520,14 @@ function check_needs_pmd() { function check_needs_infer() { start_end::group_start "Check if infer is needed" local pattern_array=( - "^.inferconfig$" + "^\.inferconfig$" "^hadoop-ozone/dev-support/checks/infer.sh$" "^hadoop-ozone/dev-support/checks/install/infer.sh$" "pom.xml" - "src/..../java" + "src/main/java" + ) + local ignore_array=( + ".*/test-utils/src/main/java" ) filter_changed_files @@ -552,7 +556,7 @@ function get_count_misc_files() { "\.bats$" "\.txt$" "\.md$" - ".inferconfig$" + "^\.inferconfig$" "findbugsExcludeFile.xml" "pmd-ruleset.xml" "/NOTICE$" diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index c6c5dba0781e..bb599885c9a2 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -27,11 +27,17 @@ if ! git diff --quiet -- "${POM_FILES[@]}" || ! git diff --cached --quiet -- "${ fi POM_BACKUP_DIR=$(mktemp -d) || exit 1 +REPORT_DIR=${OUTPUT_DIR:-"$DIR/../../../target/infer"} +REPORT_FILE="$REPORT_DIR/summary.txt" +OUTPUT_LOG="${POM_BACKUP_DIR}/output.log" restore_poms() { local rc=$? local restore_failed=false local i + if ! mkdir -p "$REPORT_DIR" || ! cp "$OUTPUT_LOG" "${REPORT_DIR}/output.log"; then + rc=1 + fi for i in "${!POM_FILES[@]}"; do if [[ -f "${POM_BACKUP_DIR}/${i}" ]] && ! cp "${POM_BACKUP_DIR}/${i}" "${POM_FILES[$i]}"; then restore_failed=true @@ -43,62 +49,77 @@ restore_poms() { else rm -rf "${POM_BACKUP_DIR}" fi + if [[ ${rc} -ne 0 ]]; then + echo "Infer check failed with exit code ${rc}." >> "$REPORT_FILE" + echo "1" > "${REPORT_DIR}/failures" + fi exit "${rc}" } trap restore_poms EXIT trap 'exit 130' INT trap 'exit 143' TERM +mkdir -p "$REPORT_DIR" || exit 1 +rm -f "${REPORT_DIR}/report.txt" "${REPORT_DIR}/report.json" "${REPORT_DIR}/captured-sources.txt" \ + "${REPORT_DIR}/logs" infer-out/report.txt infer-out/report.json || exit 1 +: > "$REPORT_FILE" +: > "$OUTPUT_LOG" +echo "0" > "${REPORT_DIR}/failures" for i in "${!POM_FILES[@]}"; do cp "${POM_FILES[$i]}" "${POM_BACKUP_DIR}/${i}" || exit 1 done source "${DIR}/_lib.sh" -source "${DIR}/install/infer.sh" - -REPORT_DIR=${OUTPUT_DIR:-"$DIR/../../../target/infer"} -REPORT_FILE="$REPORT_DIR/summary.txt" +source "${DIR}/install/infer.sh" >> "$OUTPUT_LOG" 2>&1 MAVEN_OPTIONS='-B -DskipTests -DskipDocs -DskipRecon -DskipShade -Dsort.skip=true --no-transfer-progress' +MAVEN_OPTIONS+=' -Ddevelocity.cache.local.enabled=false -Ddevelocity.cache.remote.enabled=false' -mkdir -p "$REPORT_DIR" -infer run --keep-going \ +# Keep the log outside target because Maven clean removes it. +infer run --keep-going --report-console-limit 0 \ --skip-analysis-in-path '.*/src/test/.*' \ --skip-analysis-in-path '.*/target/generated-test-sources/.*' \ --skip-analysis-in-path '.*/target/generated-sources/.*' \ --skip-analysis-in-path '.*/test-utils/.*' \ - -- mvn ${MAVEN_OPTIONS} install "$@" 2>&1 | tee "${REPORT_DIR}/output.log" + -- mvn ${MAVEN_OPTIONS} clean install "$@" 2>&1 | tee -a "$OUTPUT_LOG" infer_rc=$? mkdir -p "$REPORT_DIR" -# Only copy text reports, not the multi-GB capture/results databases -if [[ -f infer-out/report.txt ]]; then - cp infer-out/report.txt "${REPORT_DIR}/" - cp infer-out/logs "${REPORT_DIR}/" 2>/dev/null || true - cp infer-out/stats "${REPORT_DIR}/" -r 2>/dev/null || true -elif [[ -d infer-out ]]; then - find infer-out -name "report.txt" -exec cp {} "${REPORT_DIR}/" \; 2>/dev/null || true -fi - -touch "$REPORT_FILE" +# Copy reports and diagnostics, not the multi-GB capture/results databases. +for report in report.txt report.json; do + if [[ -f "infer-out/${report}" ]]; then + cp "infer-out/${report}" "${REPORT_DIR}/" || exit 1 + fi +done +cp infer-out/logs "${REPORT_DIR}/" 2>/dev/null || true +cp -R infer-out/stats "${REPORT_DIR}/" 2>/dev/null || true -if [[ -f "${REPORT_DIR}/report.txt" ]]; then - issue_count=$(grep -c '\.java:' "${REPORT_DIR}/report.txt" 2>/dev/null || echo "0") - echo "Infer analysis complete. Found ${issue_count} issues." >> "$REPORT_FILE" - echo "${issue_count}" > "${REPORT_DIR}/failures" -else +if [[ ${infer_rc} -ne 0 ]]; then + echo "Infer exited with code ${infer_rc}." >> "$REPORT_FILE" + exit 1 +fi +if [[ ! -f "${REPORT_DIR}/report.txt" || ! -f "${REPORT_DIR}/report.json" ]]; then echo "Infer analysis produced no report." >> "$REPORT_FILE" - echo "0" > "${REPORT_DIR}/failures" + exit 1 fi -# Infer findings are informational, never a failure. -# Only exit non-zero if infer itself failed without producing any report. -if [[ -f "${REPORT_DIR}/report.txt" ]]; then - exit 0 -elif [[ ${infer_rc} -ne 0 ]]; then - echo "Infer exited with code ${infer_rc} and no report was produced." >> "$REPORT_FILE" +if ! infer debug --source-files > "${REPORT_DIR}/captured-sources.txt" 2>> "$OUTPUT_LOG" \ + || ! grep -q '[^[:space:]]' "${REPORT_DIR}/captured-sources.txt"; then + echo "Infer captured no source files or could not read the capture." >> "$REPORT_FILE" exit 1 -else - exit 0 fi + +issue_count=$(jq -es ' + if length == 1 and (.[0] | type) == "array" then + [.[0][] | select(.suppressed != true and .censored_reason == null)] | length + else error("Expected a single JSON report array") end +' "${REPORT_DIR}/report.json" 2>> "$OUTPUT_LOG") || { + echo "Infer produced an invalid JSON report." >> "$REPORT_FILE" + exit 1 +} +echo "Infer analysis complete. Found ${issue_count} issues." >> "$REPORT_FILE" +echo "${issue_count}" > "${REPORT_DIR}/failures" + +# Findings are informational; execution and reporting failures are not. +exit 0 From 44dcecc67d6970eac5fbdf34cc936721df9afebf Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 19 Oct 2026 13:08:13 +0800 Subject: [PATCH 29/31] HDDS-15560. Preserve Maven Java release settings during Infer capture --- dev-support/ci/infer.bats | 7 +++++++ hadoop-ozone/dev-support/checks/infer.sh | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dev-support/ci/infer.bats b/dev-support/ci/infer.bats index cb82ab9ff833..e0d5aed28c0d 100644 --- a/dev-support/ci/infer.bats +++ b/dev-support/ci/infer.bats @@ -151,6 +151,13 @@ teardown() { grep -qx -- '-Ddevelocity.cache.remote.enabled=false' infer-args } +@test "Infer preserves Maven's per-module Java release settings" { + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 0 ] + grep -qx -- '--java-version-reset' infer-args +} + @test "Infer failures are not hidden by a fresh report" { export INFER_EXIT_CODE=5 diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index bb599885c9a2..d8ed192d4705 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -76,7 +76,8 @@ MAVEN_OPTIONS='-B -DskipTests -DskipDocs -DskipRecon -DskipShade -Dsort.skip=tru MAVEN_OPTIONS+=' -Ddevelocity.cache.local.enabled=false -Ddevelocity.cache.remote.enabled=false' # Keep the log outside target because Maven clean removes it. -infer run --keep-going --report-console-limit 0 \ +# Do not let Infer's Maven profile override each module's maven.compiler.release. +infer run --java-version-reset --keep-going --report-console-limit 0 \ --skip-analysis-in-path '.*/src/test/.*' \ --skip-analysis-in-path '.*/target/generated-test-sources/.*' \ --skip-analysis-in-path '.*/target/generated-sources/.*' \ From a29d1c47ef67fe95ab93c0e9310e1c0eca36f6a6 Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Mon, 26 Oct 2026 17:28:43 +0800 Subject: [PATCH 30/31] HDDS-15560. Use self-repository syntax for Infer workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb491d1832ca..299158425259 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -227,7 +227,7 @@ jobs: needs: - build-info if: needs.build-info.outputs.needs-infer == 'true' - uses: ./.github/workflows/check.yml + uses: $/.github/workflows/check.yml with: java-version: ${{ needs.build-info.outputs.java-version }} needs-maven-cache: true From 733348c2551f092094469d68c7e7ee12d2dd244b Mon Sep 17 00:00:00 2001 From: Ivan Andika Date: Thu, 29 Oct 2026 09:27:21 +0800 Subject: [PATCH 31/31] HDDS-15560. Allow Infer checks without Git metadata --- dev-support/ci/infer.bats | 14 ++++++++++++++ hadoop-ozone/dev-support/checks/infer.sh | 5 ++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/dev-support/ci/infer.bats b/dev-support/ci/infer.bats index e0d5aed28c0d..d1cb351864a3 100644 --- a/dev-support/ci/infer.bats +++ b/dev-support/ci/infer.bats @@ -108,6 +108,20 @@ teardown() { cmp expected hadoop-ozone/dev-support/checks/_lib.sh } +@test "Infer runs without Git metadata and restores both POMs" { + mv .git saved-git + cp "${POMS[0]}" expected0 + cp "${POMS[1]}" expected1 + + run bash hadoop-ozone/dev-support/checks/infer.sh + + [ "${status}" -eq 0 ] + [ -e infer-called ] + [ -f target/infer/report.txt ] + cmp expected0 "${POMS[0]}" + cmp expected1 "${POMS[1]}" +} + @test "Infer restores POMs after an analysis failure" { export INFER_EXIT_CODE=2 diff --git a/hadoop-ozone/dev-support/checks/infer.sh b/hadoop-ozone/dev-support/checks/infer.sh index d8ed192d4705..86b598e18bea 100755 --- a/hadoop-ozone/dev-support/checks/infer.sh +++ b/hadoop-ozone/dev-support/checks/infer.sh @@ -20,7 +20,10 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" cd "$DIR/../../.." || exit 1 POM_FILES=(hadoop-ozone/ozonefs-hadoop2/pom.xml hadoop-ozone/ozonefs-shaded/pom.xml) -if ! git diff --quiet -- "${POM_FILES[@]}" || ! git diff --cached --quiet -- "${POM_FILES[@]}"; then +# CI removes .git before running checks; only check local edits in Git checkouts. +if [[ -e .git ]] && { + ! git diff --quiet -- "${POM_FILES[@]}" || ! git diff --cached --quiet -- "${POM_FILES[@]}" +}; then echo "Infer requires clean ozonefs POMs. Commit or stash changes to these files first:" >&2 printf ' %s\n' "${POM_FILES[@]}" >&2 exit 1