null-markeder

Make sure every package in your Java project is annotated @NullMarked — no package left behind, enforced by an ArchUnit test.

CI status Maven Central version Javadoc Coverage Status License

The problem

JSpecify's @NullMarked only protects a package if its package-info.java actually carries the annotation. It's easy to add a new package, forget the file, and quietly lose null-safety coverage for everything in it — no compiler error, no warning, just a gap.

What it does

Detects

An ArchUnit test fails the build the moment a package is missing @NullMarked.

Fixes

PackageInfoGenerator generates or updates every missing package-info.java for you.

Stays out of the way

Test-scoped dependency, no runtime footprint, no annotation processor.

Usage

Add the dependency (see latest release for the version):

<dependency>
    <groupId>li.selman</groupId>
    <artifactId>null-markeder</artifactId>
    <version>VERSION</version>
    <scope>test</scope>
</dependency>

Then add an ArchUnit test that fails (and self-heals) when a package is missing the annotation:

@ArchTest
void packagesShouldBeAnnotated(JavaClasses classes) throws IOException {
    var rootPackage = classes.getPackage(ROOT_PACKAGE);
    List<String> violations = rootPackage.getSubpackagesInTree().stream()
            .filter(pkg -> !pkg.isAnnotatedWith(NullMarked.class))
            .map(pkg -> pkg.getDescription() + " is not annotated with @NullMarked")
            .toList();

    if (!violations.isEmpty()) {
        PackageInfoGenerator.main(ROOT_PACKAGE);
    }

    assertThat(violations).isEmpty();
}

Full walkthrough, including the required test dependencies, is in the README.