Skip to content

Run checkWorkingCopyClean as a Test-task finalizer on CI#4417

Open
dsmiley wants to merge 1 commit into
apache:mainfrom
dsmiley:checkWorkingCopyCleanAfterTests
Open

Run checkWorkingCopyClean as a Test-task finalizer on CI#4417
dsmiley wants to merge 1 commit into
apache:mainfrom
dsmiley:checkWorkingCopyCleanAfterTests

Conversation

@dsmiley
Copy link
Copy Markdown
Contributor

@dsmiley dsmiley commented May 11, 2026

Previously the working-tree cleanliness check
(gradle/validation/git-status.gradle's checkWorkingCopyClean) only ran as part of precommit. CI jobs that invoke just test therefore never noticed when a test wrote files into the working tree.

Wire every Test task across all projects so that:

  • On CI (isCIBuild), each Test task is finalizedBy :checkWorkingCopyClean. The finalizer runs after every test task -- de-duped when multiple test tasks are scheduled, and still fires when the test itself fails -- but does not pull tests in when checkWorkingCopyClean is invoked standalone.
  • Locally, only a mustRunAfter is wired. Running gradlew test locally is not surprised by an auto-running clean check; but when both tasks are scheduled together (e.g. via check, which depends on both precommit and test), the clean check is guaranteed to run after the tests.

Also move the Git#status() call out of the gitStatus task and into checkWorkingCopyClean itself. Previously the working-tree snapshot was captured in gitStatus.doFirst and merely consulted later by checkWorkingCopyClean. Because gitStatus is a dependency of Jar tasks (via jar-manifest.gradle, which needs the git revision for the manifest), it always ran early in the task graph -- so the snapshot was taken before any Test task could pollute the tree. Now the status is captured at the check's own execution time, which (thanks to the finalizer/ordering) is after all Test tasks have completed. gitStatus still captures gitRev/gitRevShort for the jar manifest consumer.

Verified by adding a temporary test that writes a file into the project root, then running with -Pvalidation.git.failOnModified=true: the finalizer fires after the test and the build fails with the polluted file listed under "(untracked)". Also confirmed the finalizer runs when the test itself fails, and that the local mustRunAfter-only path correctly avoids triggering the check from a bare gradlew test.


(I reviewed the LLM generated output completely)

This is motivated by the Java SecurityManager going away which was helping us tame our tests from bad behavior. This solution seems very pragmatic and leverages something we already have. It's not as comprehensive as the JSM but I think it's adequate.

Previously the working-tree cleanliness check
(gradle/validation/git-status.gradle's checkWorkingCopyClean) only ran as
part of `precommit`. CI jobs that invoke just `test` therefore never
noticed when a test wrote files into the working tree.

Wire every Test task across all projects so that:

  - On CI (isCIBuild), each Test task is `finalizedBy
    :checkWorkingCopyClean`. The finalizer runs after every test task --
    de-duped when multiple test tasks are scheduled, and still fires when
    the test itself fails -- but does not pull tests in when
    `checkWorkingCopyClean` is invoked standalone.
  - Locally, only a `mustRunAfter` is wired. Running `gradlew test`
    locally is not surprised by an auto-running clean check; but when
    both tasks are scheduled together (e.g. via `check`, which depends
    on both `precommit` and `test`), the clean check is guaranteed to
    run after the tests.

Also move the `Git#status()` call out of the `gitStatus` task and into
`checkWorkingCopyClean` itself. Previously the working-tree snapshot was
captured in `gitStatus.doFirst` and merely consulted later by
`checkWorkingCopyClean`. Because `gitStatus` is a dependency of Jar
tasks (via jar-manifest.gradle, which needs the git revision for the
manifest), it always ran early in the task graph -- so the snapshot was
taken before any Test task could pollute the tree. Now the status is
captured at the check's own execution time, which (thanks to the
finalizer/ordering) is after all Test tasks have completed. `gitStatus`
still captures `gitRev`/`gitRevShort` for the jar manifest consumer.

Verified by adding a temporary test that writes a file into the project
root, then running with `-Pvalidation.git.failOnModified=true`: the
finalizer fires after the test and the build fails with the polluted
file listed under "(untracked)". Also confirmed the finalizer runs when
the test itself fails, and that the local mustRunAfter-only path
correctly avoids triggering the check from a bare `gradlew test`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant