Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
829b7ea
HDDS-15560. Integrate Infer static analysis into CI
ivandika3 Jun 22, 2026
44b27df
HDDS-15560. Fix infer CI: compile scope for infer-annotation and robu…
ivandika3 Jun 22, 2026
15e8e9d
HDDS-15560. Fix sortpom ordering and infer epilogue exit code
ivandika3 Jun 22, 2026
dca8491
HDDS-15560. Fix infer CI: REPORT_FILE, bats tests, and dependency scope
ivandika3 Jun 22, 2026
bf83eb9
HDDS-15560. Fix sortpom ordering: move infer-annotation to provided-s…
ivandika3 Jun 22, 2026
61c4872
HDDS-15560. Skip sortpom during infer capture to prevent sortpom:veri…
ivandika3 Jun 22, 2026
f4184a4
HDDS-15560. Change infer-annotation scope to compile (default) so @Th…
ivandika3 Jun 22, 2026
f48e97d
HDDS-15560. Use 'mvn install' instead of 'mvn compile' for infer capt…
ivandika3 Jun 22, 2026
9077ba9
HDDS-15560. Remove stray empty <dependency> tags from server-scm and …
ivandika3 Jun 25, 2026
bd891a8
HDDS-15560. Fix infer CI: provided scope for infer-annotation, shrink…
ivandika3 Jun 26, 2026
1e320e8
HDDS-15560. Move infer-annotation to provided-scope section for sortp…
ivandika3 Jun 26, 2026
d1d75df
HDDS-15560. Change infer-annotation to compile scope so @ThreadSafe i…
ivandika3 Jun 26, 2026
071b577
HDDS-15560. Exclude infer-annotation transitive deps from distributio…
ivandika3 Jun 29, 2026
73782a8
HDDS-15560. Skip Infer analysis in test and generated source paths
ivandika3 Jun 29, 2026
8b75a23
HDDS-15560. Exclude infer-annotation from dist module copy-dependencies
ivandika3 Jun 29, 2026
264fdc2
HDDS-15560. Skip Infer analysis in test-utils module
ivandika3 Aug 4, 2026
122d83b
HDDS-15560. Gate Infer CI on relevant source changes via paths filter…
ivandika3 Aug 4, 2026
9ba48ee
HDDS-15560. Annotate ContainerData as @ThreadSafe for RacerD analysis
ivandika3 Aug 4, 2026
9860c9b
HDDS-15560. Switch Infer CI from JDK 21 to JDK 25 for consistency wit…
ivandika3 Aug 4, 2026
d2427e5
HDDS-15560. Fix Infer CI timeout and selective checks
ivandika3 Aug 18, 2026
95a41f6
HDDS-15560. Fix reusable Infer CI workflow syntax
ivandika3 Aug 18, 2026
2c96cb7
HDDS-15560. Run Infer with approval-gated CI
ivandika3 Aug 18, 2026
a1373ab
HDDS-15560. Run Infer outside basic CI checks
ivandika3 Aug 18, 2026
6a4705d
Merge remote-tracking branch 'origin/master' into HDDS-15560
ivandika3 Aug 19, 2026
d0d8b13
HDDS-15560. Exclude test sources from Infer analysis
ivandika3 Aug 19, 2026
27c4f80
HDDS-15560. Validate GDPR key metadata in RpcClient
ivandika3 Aug 19, 2026
a100798
HDDS-15560. Exclude Infer annotation from classpath descriptors
ivandika3 Aug 19, 2026
e74ea41
Merge remote-tracking branch 'origin/master' into HDDS-15560
ivandika3 Oct 19, 2026
b198707
HDDS-15560. Preserve local ozonefs POM changes when running Infer
ivandika3 Oct 19, 2026
8aca455
HDDS-15560. Fix Infer capture, reporting and selective CI checks
ivandika3 Oct 19, 2026
44dcecc
HDDS-15560. Preserve Maven Java release settings during Infer capture
ivandika3 Oct 19, 2026
69c6860
Merge remote-tracking branch 'origin/master' into HDDS-15560
ivandika3 Oct 26, 2026
a29d1c4
HDDS-15560. Use self-repository syntax for Infer workflow
ivandika3 Oct 26, 2026
733348c
HDDS-15560. Allow Infer checks without Git metadata
ivandika3 Oct 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,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 }}
Expand Down Expand Up @@ -222,6 +223,21 @@ jobs:
- check: errorprone
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 }}

errorprone:
needs:
- build-info
Expand Down
15 changes: 15 additions & 0 deletions .inferconfig
Original file line number Diff line number Diff line change
@@ -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"
]
}
259 changes: 259 additions & 0 deletions dev-support/ci/infer.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
#!/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' '<project/>' > "${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' \
'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' \
'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 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

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[@]}"
}

@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 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

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[@]}"
}
Loading