Skip to content

Fix DevCenter cards silently emitting zero rows#77

Merged
jkschneider merged 3 commits into
mainfrom
fix-cycle-gated-data-table-watcher
May 12, 2026
Merged

Fix DevCenter cards silently emitting zero rows#77
jkschneider merged 3 commits into
mainfrom
fix-cycle-gated-data-table-watcher

Conversation

@jkschneider
Copy link
Copy Markdown
Member

@jkschneider jkschneider commented May 12, 2026

Summary

The five dependency-version DevCenter cards — LibraryUpgrade, GroovyVersionUpgrade, KotlinVersionUpgrade, ScalaVersionUpgrade, ParentPomUpgrade — currently produce zero rows. The "Move to Spring Boot 4.0" card, for example, shows no data even on repos that clearly use Spring Boot.

Root cause

These cards read dependency versions by running a sibling recipe (DependencyInsight / ParentPomInsight) for the side effect of writing a row to its data table, then capturing the row via DataTableRowWatcher. DependencyInsight.MarkIndividualDependency adds SearchResult markers to the LST, which forces OpenRewrite to schedule a cycle 2. On cycle 2, DataTable#insertRow silently drops every write whose DataTable doesn't override allowWritingInThisCycle — and DependenciesInUse / ParentPomsInUse don't. The watcher's getRows() snapshot diff comes back zero, and the cards emit nothing.

  • This worked before Adapt to new DataTables interface #61 ("Adapt to new Data Table interface") — the previous DataTableRowWatcher read from the ungated org.openrewrite.dataTables context-message bag rather than the cycle-gated DataTableStore.

Fix

Replace the sibling-recipe + watcher pattern with direct reads from LST markers via MavenResolutionResult.findDependencies(...) and ResolvedDependency.findDependencies(...). A shared helper ResolvedDependencyVersions.findVersions(SourceFile, groupIdPattern, artifactIdPattern) walks both Maven and Gradle markers; the four version-card recipes route through it.

  • ParentPomUpgrade reads Pom.getParent() for immediate-parent matching only — drops the recursive ancestor walk that ParentPomInsight did via MavenPomDownloader. DevCenter cards typically want to track the closest parent (e.g. a corporate parent that extends spring-boot-starter-parent); if there's a use case for transitive grand-parent matching, happy to add it back.
  • DependencyVulnerabilityCheck uses the new DependencyVulnerabilityCheckBase#vulnerabilities(Accumulator) accessor (companion PR moderneinc/rewrite-java-security#354) to avoid the same coupling.
  • DataTableRowWatcher is deleted; no remaining consumers.

Tests previously asserted on <!--~~(...)~~>-- SearchResult markers from the now-removed sub-recipe runs; those assertions are dropped.

Test plan

  • ./gradlew :test --tests 'LibraryUpgradeTest' --tests 'GroovyVersionUpgradeTest' --tests 'KotlinVersionUpgradeTest' --tests 'ScalaVersionUpgradeTest' --tests 'ParentPomUpgradeTest' — all pass locally
  • End-to-end: mod git sync moderne ./working-set Default && mod run --recipe=io.moderne.devcenter.DevCenterStarter && mod devcenter produces non-empty "Move to Spring Boot 4.0" rows
  • CI green

Dependency

  • Requires moderneinc/rewrite-java-security#354 to be released before this can compile against the published artifact (CI will fail until then).

The five dependency-version cards — LibraryUpgrade, GroovyVersionUpgrade,
KotlinVersionUpgrade, ScalaVersionUpgrade, ParentPomUpgrade — read
dependency versions by running a sibling recipe (DependencyInsight /
ParentPomInsight) for the side effect of writing a data-table row, then
fishing the row back out via DataTableRowWatcher.

DependencyInsight's MarkIndividualDependency sub-visitor adds SearchResult
markers, forcing OpenRewrite to schedule cycle 2. On cycle 2,
`DataTable#insertRow` silently drops every write that doesn't override
`allowWritingInThisCycle`. `DependenciesInUse` / `ParentPomsInUse` don't
override it, so the watcher's getRows() snapshot diff is always zero and
the cards emit no rows.

This worked before the DataTableRowWatcher was retrofitted (#61) to read
from the new `DataTableStore` interface — the previous implementation
read from the ungated `org.openrewrite.dataTables` context-message bag.

Replace the sibling-recipe + watcher pattern with direct reads from LST
markers: `MavenResolutionResult.findDependencies(...)` and
`ResolvedDependency.findDependencies(...)`. ParentPomUpgrade reads
`Pom.getParent()` for immediate-parent matching (drops the recursive
ancestor walk that ParentPomInsight did via MavenPomDownloader — DevCenter
cards typically want immediate-parent matches anyway).

DependencyVulnerabilityCheck uses the new
`DependencyVulnerabilityCheckBase#vulnerabilities(Accumulator)` accessor
exposed in rewrite-java-security to avoid the same coupling. The
DataTableRowWatcher class itself is deleted.

Tests were asserting on the `<!--~~(...)~~>--` SearchResult markers from
the now-removed sub-recipe runs; those assertions are dropped.

Requires rewrite-java-security to publish a release containing the new
`DependencyVulnerabilityCheckBase#vulnerabilities(Accumulator)` method
before this can compile against the published artifact.
Resolves conflict with #74 (DataTableRowWatcher fix for CsvDataTableStore).
This branch's refactor removes all callers of DataTableRowWatcher, so the
file is dropped entirely rather than fixed.

Adapts the new `libraryUpgradeUnderCsvDataTableStore` regression test in
DevCenterTest to the new mechanism: drops the `<!--~~(...)~~>` SearchResult
marker assertions (DependencyInsight is no longer invoked) and the
DependenciesInUse.csv assertions (LibraryUpgrade no longer populates that
table as a side effect). The UpgradesAndMigrations.csv assertion remains
and is the right regression test for this bug.
Same pattern as the per-card test fixes earlier on this branch:
`UpgradesAndMigrationsTest` and `DevCenterTest#devcenterWithMultipleLibraryUpgradeRecipesHasCorrectData`
were asserting on the `<!--~~>-->` / `~~(...)~~` `SearchResult` markers
that `ParentPomInsight` and `DependencyInsight.MarkIndividualDependency`
previously left on the source — those sub-recipes are no longer invoked
after the cycle-gate refactor, so the markers don't get added and the
expected-after assertions fail with "Recipe was expected to make a
change but made no changes".

Convert two-arg `pomXml(before, after, ...)` / `buildGradle(before, after)`
to the single-arg `pomXml(before, ...)` / `buildGradle(before)` form;
the `UpgradesAndMigrations.Row` data-table assertions remain and are
the right test for the bug.
@jkschneider jkschneider merged commit 3f0077e into main May 12, 2026
1 check passed
@jkschneider jkschneider deleted the fix-cycle-gated-data-table-watcher branch May 12, 2026 17:54
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.

1 participant