Skip to content

Update to Error Prone 2.45.0 - #1401

Merged
msridhar merged 4 commits into
masterfrom
ep-2.45.0
Dec 24, 2025
Merged

msridhar merged 4 commits into
masterfrom
ep-2.45.0

Conversation

@msridhar

@msridhar msridhar commented Dec 24, 2025 •

Copy link
Copy Markdown
Collaborator

Fixes #1397

Auto-fix violations of https://errorprone.info/bugpattern/FormatStringShouldUsePlaceholders. For testing on JDK 17 we still use Error Prone 2.42.0, as 2.43.0+ require JDK 21.

Summary by CodeRabbit

  • Chores
    • Updated Error Prone to 2.45.0 and added a dedicated Error Prone JDK17 version/property.
    • Added JDK 17/Error Prone test configuration and a new test task to verify compatibility.
    • Adjusted test matrix and test task wiring so the new checks run during verification.
  • Style
    • Standardized error-message formatting across modules for clearer diagnostics.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Dec 24, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

Updates the Error Prone version in gradle/libs.versions.toml (errorProne -> 2.45.0) and adds an errorProneJdk17 version entry (2.42.0). Adds a Gradle ext property and a new configuration/task (errorProneJdk17, epJdk17Test) to run tests with JDK 17 using the older Error Prone, adjusts test matrix and wiring, and marks the jmh testJdk17 task to skip. Across jar-infer and nullaway modules, multiple Preconditions/checkArgument and error messages were refactored from string concatenation to parameterized format strings; no control-flow or API changes.

Possibly related issues

Possibly related PRs

Suggested reviewers

  • yuxincs
  • lazaroclapp

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.75% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: updating Error Prone to version 2.45.0, which is the primary objective of the PR.
Linked Issues check ✅ Passed The PR successfully addresses issue #1397 by updating Error Prone from 2.42.0 to 2.45.0 across dependencies and configurations while maintaining JDK 17 compatibility.
Out of Scope Changes check ✅ Passed All changes directly support the Error Prone 2.45.0 update objective: version updates, error message formatting fixes (FormatStringShouldUsePlaceholders auto-fix), and JDK 17 compatibility infrastructure.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ep-2.45.0

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@msridhar
msridhar marked this pull request as draft December 24, 2025 04:29
@codecov

codecov Bot commented Dec 24, 2025 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 88.19%. Comparing base (3fdd52b) to head (6a574bd).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...m/uber/nullaway/handlers/LibraryModelsHandler.java 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##             master    #1401   +/-   ##
=========================================
  Coverage     88.19%   88.19%           
  Complexity     2616     2616           
=========================================
  Files            95       95           
  Lines          8675     8675           
  Branches       1743     1743           
=========================================
  Hits           7651     7651           
  Misses          510      510           
  Partials        514      514           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@msridhar
msridhar marked this pull request as ready for review December 24, 2025 17:15
@msridhar
msridhar requested a review from yuxincs December 24, 2025 17:16
@msridhar
msridhar merged commit 07fcb6f into master Dec 24, 2025
8 of 11 checks passed
@msridhar
msridhar deleted the ep-2.45.0 branch December 24, 2025 17:17

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00232cb and 6a574bd.

📒 Files selected for processing (5)
  • build.gradle
  • buildSrc/src/main/groovy/nullaway.java-test-conventions.gradle
  • gradle/libs.versions.toml
  • jmh/build.gradle
  • nullaway/build.gradle
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
Repo: uber/NullAway PR: 1245
File: guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java:101-102
Timestamp: 2025-08-14T18:50:06.159Z
Learning: In NullAway JSpecify tests, when JDK version requirements exist due to bytecode annotation reading capabilities, prefer failing tests over skipping them on unsupported versions to ensure CI catches regressions and enforces proper JDK version usage for developers.

Applied to files:

  • nullaway/build.gradle
  • jmh/build.gradle
  • buildSrc/src/main/groovy/nullaway.java-test-conventions.gradle
📚 Learning: 2025-10-09T19:59:16.543Z
Learnt from: msridhar
Repo: uber/NullAway PR: 1243
File: jdk-annotations/astubx-generator/build.gradle:22-22
Timestamp: 2025-10-09T19:59:16.543Z
Learning: When disabling testJdk17 tasks for modules requiring JDK 21, use `onlyIf { false }` to skip the task:
```gradle
tasks.named("testJdk17").configure {
    onlyIf { false }
}
```
Do not use `doFirst { throw new GradleException(...) }` as it will cause CI failures when the task is executed.

Applied to files:

  • jmh/build.gradle
  • buildSrc/src/main/groovy/nullaway.java-test-conventions.gradle
🔇 Additional comments (7)
build.gradle (1)

40-43: LGTM! Version constant properly exposed.

The new errorProneJdk17Version ext property follows the established pattern and correctly sources the version from the catalog, making it available to subprojects for JDK 17-specific Error Prone testing.

nullaway/build.gradle (1)

85-87: LGTM! Dependency properly addresses transitive conflicts.

The addition of JetBrains annotations to the errorProneJdk17 configuration is well-documented and prevents version conflicts during JDK 17 testing.

jmh/build.gradle (1)

147-151: LGTM! Task correctly disabled for JMH module.

The use of onlyIf { false } properly skips the JDK 17 test task for the JMH module, which requires the latest Error Prone version. This approach aligns with established patterns.

Based on learnings, this is the correct way to disable tasks without causing CI failures.

gradle/libs.versions.toml (1)

20-21: LGTM! Version catalog properly updated.

The version updates correctly implement the dual-version strategy: Error Prone 2.45.0 for main builds (requiring JDK 21+) and 2.42.0 for JDK 17 testing.

buildSrc/src/main/groovy/nullaway.java-test-conventions.gradle (3)

47-58: LGTM! Configuration properly set up for JDK 17 testing.

The errorProneJdk17 configuration and dependencies are correctly wired to support testing with Error Prone 2.42.0 on JDK 17.


81-81: LGTM! Loop correctly adjusted to prevent task duplication.

Removing JDK 17 from the loop prevents conflict with the dedicated testJdk17 task created below, while maintaining test coverage on JDK 25.


137-139: LGTM! Check task properly wired to include JDK 17 tests.

The dependency ensures JDK 17 tests run as part of the standard verification workflow.

Comment on lines +103 to +135
// Create a task to test with JDK 17 and the supported version of Error Prone for that JDK
def epJdk17Test = tasks.register("testJdk17", Test) {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(17)
}

description = "Runs the test suite using the oldest supported version of Error Prone"
group = LifecycleBasePlugin.VERIFICATION_GROUP

// Copy inputs from normal Test task.
def testTask = tasks.getByName("test")
// A bit of a hack: we add the dependencies of the JDK17-supporting Error Prone version to the _beginning_ of the
// classpath, so that they are used instead of the latest version. This exercises the scenario of building
// NullAway against the latest supported Error Prone version but then running on JDK 17.
classpath = configurations.errorProneJdk17 + testTask.classpath

testClassesDirs = testTask.testClassesDirs

jvmArgs += [
"--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
// Accessed by Lombok tests
"--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED",
]
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Clarify task description to reflect JDK 17 compatibility purpose.

The task description (line 109) states "oldest supported version of Error Prone" but actually uses the JDK 17-compatible version (2.42.0), not the oldest version (2.14.0, used by testErrorProneOldest). The description should clarify this distinction.

🔎 Proposed description fix
-    description = "Runs the test suite using the oldest supported version of Error Prone"
+    description = "Runs the test suite on JDK 17 using Error Prone ${errorProneJdk17Version}"
🤖 Prompt for AI Agents
In buildSrc/src/main/groovy/nullaway.java-test-conventions.gradle around lines
103 to 135, update the task description to accurately state that this task runs
the test suite with the JDK 17-compatible Error Prone version (used to exercise
running on JDK 17) rather than claiming it uses the "oldest supported version of
Error Prone"; change the description string to mention JDK 17 compatibility
(e.g., "Runs the test suite using the Error Prone version compatible with JDK
17") so it clearly distinguishes this task from the one that runs the oldest
Error Prone version.

vlsi added a commit to vlsi/NullAway that referenced this pull request Sep 8, 2026
NullAway has no automated dependency updates. Several entries in
gradle/libs.versions.toml are pinned on purpose and must not move:
errorProneOldest is the 2.36.0 minimum README.md documents,
errorProneJdk17 is the newest Error Prone that runs on JDK 17 because
2.43.0 and later require JDK 21 (uber#1401), and guava is the version
NullAway depends on while guava-latest tracks the newest release for
guava-recent-unit-tests (uber#629).

The obvious way to hold such an entry does not work. Renovate's gradle
manager declares versioning "gradle", whose ranges are [a,b), 1.2.+ and
[1.0]. There is no "<" form, so a bound of "< 2.43" in either
matchCurrentVersion or allowedVersions parses as nothing and holds
nothing back. The three Error Prone entries also resolve to the same
com.google.errorprone artifacts, so matchPackageNames cannot tell them
apart either.

Each pinned entry therefore gets a rule that matches on
sharedVariableName, the version-catalog alias Renovate records for a
dependency, and bounds it with a Gradle range: [,2.37) for
errorProneOldest, [,2.43) for errorProneJdk17, [,32) for guava. A patch
release inside the pin stays available, and a groupName keeps each entry
in a PR of its own, since group:monorepos, which config:recommended
brings in, would otherwise collect every com.google.errorprone
dependency into a single error-prone-monorepo PR. Everything else is
grouped the way Renovate already groups it, by version-catalog entry or
by upstream monorepo.

Three sets take no updates at all: the 1.0-HEAD-SNAPSHOT marker that
selects the current Error Prone snapshot, jmh/build.gradle, whose
caffeine, autodispose and nullaway versions appear as literal substrings
in its own classpath filters, and com.android.support, the legacy
Android support library.

PRs arrive every two weeks, except for Error Prone, which opens as soon
as a release lands. A major update goes to the dependency dashboard for
manual review instead of opening a PR.

The workflow runs renovate-config-validator on renovate.json, following
the model in pgjdbc. It passes --no-global, without which the validator
treats the file as a self-hosted global config and accepts options a
repository config may not carry. It carries no paths filter, because a
workflow that filters itself out reports no status and so could never be
a required check; the job runs on every pull request and every merge
queue entry instead, and skips the validation step when neither
renovate.json nor the workflow itself changed. One step carries no
condition at all: it checks that every catalog alias the rules pin is
still declared in gradle/libs.versions.toml, because a rename there
disables a rule while the config goes on validating, and the
change-detection step does not watch that file.

Checked with renovate --platform=local --dry-run=full: every pinned
entry is held, and widening each bound by hand produces the separate
branches the rules name. Renaming a catalog entry by hand fails the
alias check and names the entry.

Assisted-by: Claude Code (claude-opus-5)
vlsi added a commit to vlsi/NullAway that referenced this pull request Sep 8, 2026
NullAway has no automated dependency updates. uber#1817 proposed Dependabot
and was closed in favor of Renovate for its configuration options:
grouping every GitHub Actions bump into one pull request, and custom
regex managers for versions the standard managers do not reach.

Several versions in this build are pinned on purpose, and a bot that
bumped every one of them would break the test matrix:

* `errorProneOldest` holds the minimum Error Prone that `README.md`
  documents, 2.36.0, and `testErrorProneOldest` runs the suite against
  it. Raising that floor is a decision rather than maintenance, so the
  entry takes 2.36.x only.
* `errorProneJdk17` holds the newest Error Prone that runs on JDK 17,
  since 2.43.0 and later require JDK 21 (uber#1401), and `testJdk17` puts
  its jars in front of the test classpath. The entry takes 2.42.x only.
* `guava` is the version NullAway itself depends on, and it reaches a
  user's annotation processor path, so it stays on 31.x. The separate
  `guava-latest` entry that `guava-recent-unit-tests` uses keeps taking
  updates (uber#629).
* `errorProneLatestSnapshot` asks for the literal `1.0-HEAD-SNAPSHOT`
  marker, which Renovate reads as version 1.0 and would replace with a
  release.
* `jmh/build.gradle` pins the versions each benchmark was built with,
  and spells three of them as literal substrings in its classpath
  filters, so a bump would leave a filter matching nothing.
* `com.android.support` is the superseded Android support library, used
  only by `sample-app`.

The three Error Prone catalog entries resolve to the same
`com.google.errorprone` artifacts, and `guava` and `guava-latest` both
resolve to `com.google.guava:guava`, so `matchPackageNames` cannot tell
them apart. Those rules match on `sharedVariableName`, the catalog alias
Renovate reports for a dependency, under both spellings Renovate uses:
the bare alias, and the `libs.versions.` prefixed form it keeps for an
entry that `build.gradle` reads through an `ext` property. Each bound is
a Gradle range such as `[,2.37)`, because that versioning accepts
`[a,b)`, `1.2.+` and `[1.0]` and has no `<` form, so a bound written as
`< 2.37` would parse as nothing and hold nothing back. Each pinned entry
also carries a `groupName`, because otherwise `group:monorepos`, which
`config:recommended` brings in, would collect a pinned entry and an
unpinned one into one monorepo pull request. Everything else is grouped
the way Renovate already groups it, by version-catalog entry or by
upstream monorepo.

The rest is policy. Updates arrive every two weeks, labeled
`dependencies`, with no semantic-commit prefix, since this repository
writes plain subjects. A major update goes to the dependency dashboard
for manual approval instead of opening a pull request. Error Prone is
exempt from the schedule and opens as soon as a release is published:
NullAway compiles against Error Prone internals such as `ASTHelpers`,
and `testErrorProneLatestSnapshot` exists to catch such a change while
it is still a snapshot (uber#1555).

`renovate-config-validator` catches a malformed config and nothing else,
so the workflow also checks that every alias a rule pins is still
declared in `gradle/libs.versions.toml`; a rename there would leave the
rule matching nothing while the config still validated. That step
carries no `if:` condition, because the catalog is not one of the two
files the workflow watches for changes. The workflow itself carries no
`paths:` filter, so that its status is always reported and it can be
made a required check; the validation step is skipped instead when
neither `renovate.json` nor the workflow changed. The validator runs
with `--no-global`, without which it accepts self-hosted global options
such as `onboarding` that a repository config may not carry. The `on:`,
`concurrency:` and `permissions:` blocks are the ones uber#1818 settled on
for `continuous-integration.yml`.

This is configuration, so there are no unit tests. Checked with
`renovate --platform=local --dry-run=full` over this worktree, which
matched every rule against the real dependency set: each pinned entry is
held, and widening a bound by hand produces the separate branch its rule
names. Renaming a catalog entry by hand fails the alias check and names
the entry.

Left out, following the plan in uber#1792 to start with an initial version
and tune the rules later: automerge stays off, no custom regex manager
is configured yet, and the versions `jmh/build.gradle` and `sample-app`
pin are disabled outright rather than bounded. Merging this file does
not start Renovate; the app still has to be enabled for `uber/NullAway`.

Fixes uber#1792

Assisted-by: Claude Code (claude-opus-5)
vlsi added a commit to vlsi/NullAway that referenced this pull request Sep 8, 2026
NullAway has no automated dependency updates. uber#1817 proposed Dependabot
and was closed in favor of Renovate for its configuration options:
grouping every GitHub Actions bump into one pull request, and custom
regex managers for versions the standard managers do not reach.

Several versions in this build are pinned on purpose, and a bot that
bumped every one of them would break the test matrix:

* `errorProneOldest` holds the minimum Error Prone that `README.md`
  documents, 2.36.0, and `testErrorProneOldest` runs the suite against
  it. Raising that floor is a decision rather than maintenance, so the
  entry takes 2.36.x only.
* `errorProneJdk17` holds the newest Error Prone that runs on JDK 17,
  since 2.43.0 and later require JDK 21 (uber#1401), and `testJdk17` puts
  its jars in front of the test classpath. The entry takes 2.42.x only.
* `guava` is the version NullAway itself depends on, and it reaches a
  user's annotation processor path, so it stays on 31.x. The separate
  `guava-latest` entry that `guava-recent-unit-tests` uses keeps taking
  updates (uber#629).
* `errorProneLatestSnapshot` asks for the literal `1.0-HEAD-SNAPSHOT`
  marker, which Renovate reads as version 1.0 and would replace with a
  release.
* `jmh/build.gradle` pins every version it names, and spells three of
  them as literal substrings in its classpath filters, so a bump would
  leave a filter matching nothing.
* `com.android.support` is the superseded Android support library, used
  only by `sample-app`.

The three Error Prone catalog entries resolve to the same
`com.google.errorprone` artifacts, and `guava` and `guava-latest` both
resolve to `com.google.guava:guava`, so `matchPackageNames` cannot tell
them apart. Those rules match on `sharedVariableName`, the catalog alias
Renovate reports for a dependency, under both spellings Renovate uses:
the bare alias, and the `libs.versions.` prefixed form it keeps for an
entry that `build.gradle` reads through an `ext` property. Each bound is
a Gradle range such as `[,2.37)`, because that versioning accepts
`[a,b)`, `1.2.+` and `[1.0]` and has no `<` form, so a bound written as
`< 2.37` would parse as nothing and hold nothing back. Each pinned entry
also carries a `groupName`, because otherwise `group:monorepos`, which
`config:recommended` brings in, would collect a pinned entry and an
unpinned one into one monorepo pull request. Everything else is grouped
the way Renovate already groups it, by version-catalog entry or by
upstream monorepo.

The rest is policy. Updates arrive every two weeks, labeled
`dependencies`, with no semantic-commit prefix, since this repository
writes plain subjects. A major update goes to the dependency dashboard
for manual approval instead of opening a pull request. Error Prone is
exempt from the schedule and opens as soon as a release is published:
NullAway compiles against Error Prone internals such as `ASTHelpers`,
and `testErrorProneLatestSnapshot` (uber#1555) runs the suite against Error
Prone snapshots, so such a change shows up before the release does.

`renovate-config-validator` checks the config's shape, not whether a
rule still matches anything, so the workflow also checks that every
alias a rule pins is still declared in `gradle/libs.versions.toml`; a
rename there would leave the rule matching nothing while the config
still validated. That step carries no `if:` condition, because the
catalog is not one of the two files the workflow watches for changes.
The workflow itself carries no `paths:` filter, so that its status is
always reported and it can be made a required check; the validation step
is skipped instead when neither `renovate.json` nor the workflow
changed. The validator runs with `--no-global`, without which it accepts
self-hosted global options such as `onboarding` that a repository config
may not carry. The `merge_group` trigger and the `concurrency` group are
the ones uber#1818 settled on for `continuous-integration.yml`.

This is configuration, so there are no unit tests. Checked with
`renovate --platform=local --dry-run=full` over this worktree, which
matched every rule against the real dependency set: each pinned entry is
held, and widening a bound by hand produces the separate branch its rule
names. Renaming a catalog entry by hand fails the alias check and names
the entry.

Left out, following the plan in uber#1792 to start with an initial version
and tune the rules later: automerge stays off, no custom regex manager
is configured yet, and `jmh/build.gradle` and `com.android.support` are
disabled outright rather than bounded. Merging this file does not start
Renovate; the app still has to be enabled for `uber/NullAway`.

Fixes uber#1792

Assisted-by: Claude Code (claude-opus-5)
vlsi added a commit to vlsi/NullAway that referenced this pull request Sep 8, 2026
NullAway has no automated dependency updates. uber#1817 proposed Dependabot
and was closed in favor of Renovate for its configuration options:
grouping every GitHub Actions bump into one pull request, and custom
regex managers for versions the standard managers do not reach.

Several versions in this build are pinned on purpose, and a bot that
bumped every one of them would break the test matrix:

* `errorProneOldest` holds the minimum Error Prone that `README.md`
  documents, 2.36.0, and `testErrorProneOldest` runs the suite against
  it. Raising that floor is a decision rather than maintenance, so the
  entry takes 2.36.x only.
* `errorProneJdk17` holds the newest Error Prone that runs on JDK 17,
  since 2.43.0 and later require JDK 21 (uber#1401), and `testJdk17` puts
  its jars in front of the test classpath. The entry takes 2.42.x only.
* `guava` is the version NullAway itself depends on, and it reaches a
  user's annotation processor path, so it stays on 31.x. The separate
  `guava-latest` entry that `guava-recent-unit-tests` uses keeps taking
  updates (uber#629).
* `errorProneLatestSnapshot` asks for the literal `1.0-HEAD-SNAPSHOT`
  marker, which Renovate reads as version 1.0 and would replace with a
  release.
* `jmh/build.gradle` pins every version it names, and spells three of
  them as literal substrings in its classpath filters, so a bump would
  leave a filter matching nothing.
* `com.android.support` is the superseded Android support library, used
  only by `sample-app`.

The three Error Prone catalog entries resolve to the same
`com.google.errorprone` artifacts, and `guava` and `guava-latest` both
resolve to `com.google.guava:guava`, so `matchPackageNames` cannot tell
them apart. Those rules match on `sharedVariableName`, the catalog alias
Renovate reports for a dependency, under two spellings. Renovate reports
the bare alias for every entry in this repository; the `libs.versions.`
prefixed form is insurance, for an entry that `build.gradle` reads
through an `ext` property and that a refactor leaves with a single
dependency. Each bound is a Gradle range such as `[,2.37)`, because that
versioning accepts `[a,b)`, `1.2.+` and `[1.0]` and has no `<` form, so
a bound written as `< 2.37` would parse as nothing and hold nothing
back. Each pinned entry also carries a `groupName`, because otherwise
`group:monorepos`, which `config:best-practices` pulls in through
`config:recommended`, would collect a pinned entry and an unpinned one
into one monorepo pull request. Everything else is grouped the way
Renovate already groups it, by version-catalog entry or by upstream
monorepo.

The rest is policy. Updates arrive every two weeks, labeled
`dependencies`, with no semantic-commit prefix, since this repository
writes plain subjects. A major update goes to the dependency dashboard
for manual approval instead of opening a pull request. Error Prone is
exempt from the schedule and opens as soon as a release is published:
NullAway compiles against Error Prone internals such as `ASTHelpers`,
and `testErrorProneLatestSnapshot` (uber#1555) runs the suite against Error
Prone snapshots, so such a change shows up before the release does.

`renovate-config-validator` checks the config's shape, not whether a
rule still matches anything, so the workflow also checks that every
alias a rule pins is still declared in `gradle/libs.versions.toml`; a
rename there would leave the rule matching nothing while the config
still validated. That step carries no `if:` condition, because the
catalog is not one of the two files the workflow watches for changes.
The workflow itself carries no `paths:` filter, so that its status is
always reported and it can be made a required check; the validation step
is skipped instead when neither `renovate.json` nor the workflow
changed. The validator runs with `--no-global`, without which it accepts
self-hosted global options such as `onboarding` that a repository config
may not carry. The `merge_group` trigger and the `concurrency` group are
the ones uber#1818 settled on for `continuous-integration.yml`.

This is configuration, so there are no unit tests. Checked with
`renovate --platform=local --dry-run=full` over this worktree, which
matched every rule against the real dependency set: each pinned entry is
held, and widening a bound by hand produces the separate branch its rule
names. Renaming a catalog entry by hand fails the alias check and names
the entry.

Left out, following the plan in uber#1792 to start with an initial version
and tune the rules later. Automerge stays off and no custom regex
manager is configured yet. The GitHub Actions grouping covers
`actions/*` and `github/*`, not the other publishers this repository
uses, so a `codecov`, `gradle/actions` or `zizmorcore` bump still
arrives on its own. The older test fixtures in
`gradle/libs.versions.toml` are not excluded either: nothing in the
repository records which of them are held on purpose, so their majors
need dashboard approval and their minors will open pull requests until
someone names the ones to pin. `jmh/build.gradle` and
`com.android.support` are disabled outright rather than bounded. Merging
this file does not start Renovate; the app still has to be enabled for
`uber/NullAway`.

Fixes uber#1792

Assisted-by: Claude Code (claude-opus-5)
vlsi added a commit to vlsi/NullAway that referenced this pull request Sep 8, 2026
NullAway has no automated dependency updates. uber#1817 proposed Dependabot
and was closed in favor of Renovate for its configuration options:
grouping every GitHub Actions bump into one pull request, and custom
regex managers for versions the standard managers do not reach.

Several versions in this build are pinned on purpose, and a bot that
bumped every one of them would break the test matrix:

* `errorProneOldest` holds the minimum Error Prone that `README.md`
  documents, 2.36.0, and `testErrorProneOldest` runs the suite against
  it. Raising that floor is a decision rather than maintenance, so the
  entry takes 2.36.x only.
* `errorProneJdk17` holds the newest Error Prone that runs on JDK 17,
  since 2.43.0 and later require JDK 21 (uber#1401), and `testJdk17` puts
  its jars in front of the test classpath. The entry takes 2.42.x only.
* `guava` is the version NullAway itself depends on, and it reaches a
  user's annotation processor path, so it stays on 31.x. The separate
  `guava-latest` entry that `guava-recent-unit-tests` uses keeps taking
  updates (uber#629).
* `errorProneLatestSnapshot` asks for the literal `1.0-HEAD-SNAPSHOT`
  marker, which Renovate reads as version 1.0 and would replace with a
  release.
* `jmh/build.gradle` pins every version it names, and spells three of
  them as literal substrings in its classpath filters, so a bump would
  leave a filter matching nothing.
* `com.android.support` is the superseded Android support library, used
  only by `sample-app`.

The three Error Prone catalog entries resolve to the same
`com.google.errorprone` artifacts, and `guava` and `guava-latest` both
resolve to `com.google.guava:guava`, so `matchPackageNames` cannot tell
them apart. Those rules match on `sharedVariableName`, the catalog alias
Renovate reports for a dependency, under two spellings. Renovate reports
the bare alias for every entry in this repository; the `libs.versions.`
prefixed form is insurance, for an entry that `build.gradle` reads
through an `ext` property and that a refactor leaves with a single
dependency. Each bound is a Gradle range such as `[,2.37)`, because that
versioning accepts `[a,b)`, `1.2.+` and `[1.0]` and has no `<` form, so
a bound written as `< 2.37` would parse as nothing and hold nothing
back. Each pinned entry also carries a `groupName`, because otherwise
`group:monorepos`, which `config:best-practices` pulls in through
`config:recommended`, would collect a pinned entry and an unpinned one
into one monorepo pull request. Everything else is grouped the way
Renovate already groups it, by version-catalog entry or by upstream
monorepo.

The rest is policy. Updates arrive every two weeks, labeled
`dependencies`, with no semantic-commit prefix, since this repository
writes plain subjects. A major update goes to the dependency dashboard
for manual approval instead of opening a pull request, Error Prone
included; only the schedule and the cooldown below treat Error Prone
differently. It is exempt from the schedule and opens as soon as a
release is published: NullAway compiles against Error Prone internals
such as `ASTHelpers`, and `testErrorProneLatestSnapshot` (uber#1555) runs
the suite against Error Prone snapshots, so such a change shows up
before the release does.

A release less than seven days old does not reach a pull request:
`minimumReleaseAge` holds it back, so that a broken or compromised
publish has time to be found and pulled. Where an older release above
the current version is itself old enough, Renovate targets that one
rather than waiting. Error Prone and the actions GitHub publishes are
exempt, because a broken Error Prone release breaks this build rather
than reaching users and `testErrorProneLatestSnapshot` has already
exercised it as a snapshot, and because an action GitHub publishes is
not the supply-chain risk a third-party one is. A security update
bypasses the cooldown through Renovate's own `vulnerabilityAlerts`
defaults.

`renovate-config-validator` checks the config's shape, not whether a
rule still matches anything, so the workflow also checks that every
alias a rule pins is still declared in `gradle/libs.versions.toml`; a
rename there would leave the rule matching nothing while the config
still validated. That step carries no `if:` condition, because the
catalog is not one of the two files the workflow watches for changes.
The workflow itself carries no `paths:` filter, so that its status is
always reported and it can be made a required check; the validation step
is skipped instead when neither `renovate.json` nor the workflow
changed. The validator runs with `--no-global`, without which it accepts
self-hosted global options such as `onboarding` that a repository config
may not carry. The `merge_group` trigger and the `concurrency` group are
the ones uber#1818 settled on for `continuous-integration.yml`.

This is configuration, so there are no unit tests. Checked with
`renovate --platform=local --dry-run=full` over this worktree, which
matched every rule against the real dependency set: each pinned entry is
held, and widening a bound by hand produces the separate branch its rule
names. Renaming a catalog entry by hand fails the alias check and names
the entry. The cooldown was measured the same way: at seven days two
updates are held as pending and three retarget to an older release that
is old enough, and raising it to 365 days holds twenty while the exempt
Error Prone update still comes through. No run covers GitHub Actions: a
local Renovate has no token, so all 21 action dependencies come back
`github-token-required`.

Left out, following the plan in uber#1792 to start with an initial version
and tune the rules later. Automerge stays off and no custom regex
manager is configured yet. The GitHub Actions grouping covers
`actions/*` and `github/*`, not the other publishers this repository
uses, so a `codecov`, `gradle/actions` or `zizmorcore` bump still
arrives on its own. The older test fixtures in
`gradle/libs.versions.toml` are not excluded either: nothing in the
repository records which of them are held on purpose, so their majors
need dashboard approval and their minors will open pull requests until
someone names the ones to pin. `jmh/build.gradle` and
`com.android.support` are disabled outright rather than bounded. Merging
this file does not start Renovate; the app still has to be enabled for
`uber/NullAway`.

Fixes uber#1792

Assisted-by: Claude Code (claude-opus-5)
msridhar pushed a commit that referenced this pull request Sep 8, 2026
NullAway has no automated dependency updates. #1817 proposed Dependabot
and was closed in favor of Renovate for its configuration options:
grouping every GitHub Actions bump into one pull request, and custom
regex managers for versions the standard managers do not reach.

Several versions in this build are pinned on purpose, and a bot that
bumped every one of them would break the test matrix:

* `errorProneOldest` holds the minimum Error Prone that `README.md`
documents, 2.36.0, and `testErrorProneOldest` runs the suite against it.
Raising that floor is a decision rather than maintenance, so the entry
takes 2.36.x only.
* `errorProneJdk17` holds the newest Error Prone that runs on JDK 17,
since 2.43.0 and later require JDK 21 (#1401), and `testJdk17` puts its
jars in front of the test classpath. The entry takes 2.42.x only.
* `guava` is the version NullAway itself depends on, and it reaches a
user's annotation processor path, so it stays on 31.x. The separate
`guava-latest` entry that `guava-recent-unit-tests` uses keeps taking
updates (#629).
* `errorProneLatestSnapshot` asks for the literal `1.0-HEAD-SNAPSHOT`
marker, which Renovate reads as version 1.0 and would replace with a
release.
* `jmh/build.gradle` pins every version it names, and spells three of
them as literal substrings in its classpath filters, so a bump would
leave a filter matching nothing.
* `com.android.support` is the superseded Android support library, used
only by `sample-app`.

The three Error Prone catalog entries resolve to the same
`com.google.errorprone` artifacts, and `guava` and `guava-latest` both
resolve to `com.google.guava:guava`, so `matchPackageNames` cannot tell
them apart. Those rules match on `sharedVariableName`, the catalog alias
Renovate reports for a dependency, under two spellings. Renovate reports
the bare alias for every entry in this repository; the `libs.versions.`
prefixed form is insurance, for an entry that `build.gradle` reads
through an `ext` property and that a refactor leaves with a single
dependency. Each bound is a Gradle range such as `[,2.37)`, because that
versioning accepts `[a,b)`, `1.2.+` and `[1.0]` and has no `<` form, so
a bound written as `< 2.37` would parse as nothing and hold nothing
back. Each pinned entry also carries a `groupName`, because otherwise
`group:monorepos`, which `config:best-practices` pulls in through
`config:recommended`, would collect a pinned entry and an unpinned one
into one monorepo pull request. Everything else is grouped the way
Renovate already groups it, by version-catalog entry or by upstream
monorepo.

The rest is policy. Updates arrive every two weeks, labeled
`dependencies`, with no semantic-commit prefix, since this repository
writes plain subjects. A major update goes to the dependency dashboard
for manual approval instead of opening a pull request, Error Prone
included; only the schedule and the cooldown below treat Error Prone
differently. It is exempt from the schedule and opens as soon as a
release is published: NullAway compiles against Error Prone internals
such as `ASTHelpers`, and `testErrorProneLatestSnapshot` (#1555) runs
the suite against Error Prone snapshots, so such a change shows up
before the release does.

A release less than seven days old does not reach a pull request:
`minimumReleaseAge` holds it back, so that a broken or compromised
publish has time to be found and pulled. Where an older release above
the current version is itself old enough, Renovate targets that one
rather than waiting. Error Prone and the actions GitHub publishes are
exempt, because a broken Error Prone release breaks this build rather
than reaching users and `testErrorProneLatestSnapshot` has already
exercised it as a snapshot, and because an action GitHub publishes is
not the supply-chain risk a third-party one is. A security update
bypasses the cooldown through Renovate's own `vulnerabilityAlerts`
defaults.

`renovate-config-validator` checks the config's shape, not whether a
rule still matches anything, so the workflow also checks that every
alias a rule pins is still declared in `gradle/libs.versions.toml`; a
rename there would leave the rule matching nothing while the config
still validated. That step carries no `if:` condition, because the
catalog is not one of the two files the workflow watches for changes.
The workflow itself carries no `paths:` filter, so that its status is
always reported and it can be made a required check; the validation step
is skipped instead when neither `renovate.json` nor the workflow
changed. The validator runs with `--no-global`, without which it accepts
self-hosted global options such as `onboarding` that a repository config
may not carry. The `merge_group` trigger and the `concurrency` group are
the ones #1818 settled on for `continuous-integration.yml`.

This is configuration, so there are no unit tests. Checked with
`renovate --platform=local --dry-run=full` over this worktree, which
matched every rule against the real dependency set: each pinned entry is
held, and widening a bound by hand produces the separate branch its rule
names. Renaming a catalog entry by hand fails the alias check and names
the entry. The cooldown was measured the same way: at seven days two
updates are held as pending and three retarget to an older release that
is old enough, and raising it to 365 days holds twenty while the exempt
Error Prone update still comes through. No run covers GitHub Actions: a
local Renovate has no token, so all 21 action dependencies come back
`github-token-required`.

Left out, following the plan in #1792 to start with an initial version
and tune the rules later. Automerge stays off and no custom regex
manager is configured yet. The GitHub Actions grouping covers
`actions/*` and `github/*`, not the other publishers this repository
uses, so a `codecov`, `gradle/actions` or `zizmorcore` bump still
arrives on its own. The older test fixtures in
`gradle/libs.versions.toml` are not excluded either: nothing in the
repository records which of them are held on purpose, so their majors
need dashboard approval and their minors will open pull requests until
someone names the ones to pin. `jmh/build.gradle` and
`com.android.support` are disabled outright rather than bounded. Merging
this file does not start Renovate; the app still has to be enabled for
`uber/NullAway`.

Fixes #1792

Assisted-by: Claude Code (claude-opus-5)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Chores**
- Added automated Renovate configuration to regularly identify and group
dependency updates.
- Dependency updates now follow release-age safeguards, version
constraints, and approval requirements for major changes.
- Added validation checks to ensure Renovate configuration remains
consistent and valid across pull requests, merge queues, and pushes.
  - GitHub Actions updates are grouped together to simplify maintenance.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update our latest Error Prone version to 2.45.0

2 participants