Skip to content

fix: restore cached Gradle frontend build outputs#24314

Open
benstpierre wants to merge 2 commits into
vaadin:mainfrom
benstpierre:fix-gradle-buildfrontend-cache-outputs
Open

fix: restore cached Gradle frontend build outputs#24314
benstpierre wants to merge 2 commits into
vaadin:mainfrom
benstpierre:fix-gradle-buildfrontend-cache-outputs

Conversation

@benstpierre

Copy link
Copy Markdown

Summary

Fixes #24012.

vaadinBuildFrontend previously wrote production servlet resources into build/resources/<sourceSet>/META-INF/VAADIN, which overlaps with Gradle's processResources output. That makes the task's production bundle hard to restore safely from Gradle's build cache: a cache hit can restore the task marker/token while leaving the archive without META-INF/VAADIN/webapp assets.

This change moves the default frontendOutputDirectory for Gradle production builds to a dedicated task-owned directory:

build/vaadin-build-frontend/META-INF/VAADIN/webapp

and declares the corresponding servlet resource directory as an @OutputDirectory of vaadinBuildFrontend.

Packaging tasks now explicitly consume that task-owned output directory:

  • plain Jar: packaged at archive root
  • War: packaged under WEB-INF/classes
  • Spring Boot BootJar: packaged under BOOT-INF/classes

This keeps the archive layout unchanged while allowing Gradle to restore the production frontend bundle from the build cache.

Tests

Run from flow-plugins/flow-gradle-plugin with Java 21:

JAVA_HOME=$HOME/.sdkman/candidates/java/21.0.7-amzn ./gradlew compileKotlin compileTestKotlin
JAVA_HOME=$HOME/.sdkman/candidates/java/21.0.7-amzn ./gradlew test
JAVA_HOME=$HOME/.sdkman/candidates/java/21.0.7-amzn ./gradlew functionalTest -x test --tests 'com.vaadin.flow.gradle.VaadinSmokeTest.testBuildFrontendInProductionMode' --tests 'com.vaadin.flow.gradle.MiscSingleModuleTest.buildFrontendIncrementalBuilds_featureEnabled' --tests 'com.vaadin.flow.gradle.MiscSingleModuleTest.testBuildFrontendPropagatesBuildInfo' --tests 'com.vaadin.flow.gradle.MiscSingleModuleTest.buildFrontendBuildCacheRestoresProductionBundleForWar'
JAVA_HOME=$HOME/.sdkman/candidates/java/21.0.7-amzn ./gradlew functionalTest -x test --tests 'com.vaadin.flow.gradle.MiscSingleModuleTest.buildFrontendBuildCacheRestoresProductionBundleForWar' --tests 'com.vaadin.flow.gradle.MiscSingleModuleTest.testSpringProjectProductionMode' --tests 'com.vaadin.flow.gradle.MiscSingleModuleTest.testSpringProjectAutoProductionMode'

The new regression test verifies that after deleting the local production frontend output, cached token, and WAR, the next --build-cache -Pvaadin.productionMode build restores :vaadinBuildFrontend FROM-CACHE and the rebuilt WAR still contains the Vaadin production bundle.

@cla-assistant

cla-assistant Bot commented May 10, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@mshabarov mshabarov added the Contribution PRs coming from the community or external to the team label May 11, 2026
@mshabarov

Copy link
Copy Markdown
Contributor

Thanks for your contribution @benstpierre ! I've started the blocked validation, let's see how gradle tests end up.

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

Test Results

 1 405 files  ±0   1 405 suites  ±0   1h 20m 40s ⏱️ - 2m 33s
10 137 tests ±0  10 067 ✅ ±0  70 💤 ±0  0 ❌ ±0 
10 612 runs  ±0  10 533 ✅ ±0  79 💤 ±0  0 ❌ ±0 

Results for commit 62c56fe. ± Comparison against base commit ba7d3cb.

@mshabarov mshabarov requested review from mcollovati and mshabarov May 11, 2026 11:36
@benstpierre

Copy link
Copy Markdown
Author

So anything left for me to do? This case has been insanely aggravating for years.

@mcollovati

Copy link
Copy Markdown
Collaborator

@benstpierre I'm going to review and test this PR today. I'll keep you posted.

@mcollovati

Copy link
Copy Markdown
Collaborator

@benstpierre could you please sign the CLA? Otherwise we cannot merge your patch as is, but we need to make a new PR on our own based on this one.

@mcollovati mcollovati left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Added a first bunch of comments; in the meanwhile I'll make some manual test

  • BuildFrontendTokenService still references build/resources/main/, but since this PR changes servletResourceOutputDirectory it should most likely mention build/vaadin-build-frontend/.

val svc = (project.tasks.getByName("vaadinBuildFrontend")
as VaadinBuildFrontendTask).getTokenService().orNull
svc?.ensureToken()
if (task.isVaadinApplicationArchiveTask()) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why is this condition needed? Is this to avoid the configuration for sources and Javadoc JARs?
Wouldn't this check potentially break projects that define custom-named JAR tasks or using shadow/fatJAR plugins?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not applying the following instruction to all JAR tasks is fundamental to make the plugin work in a backward-compatible way, but the current filter is too restrictive and might break some existing builds.
We should at least add an option to let developers opt-in if they have custom tasks. Or the other way around, apply restrictions for well-know JAR tasks and let the user opt-out custom definitions.

Some options:

  • add a includeFrontendResourceIn(taskName) helper in the Vaadin plugin to opt-in (potentially also add a parameter to specify the target path inside the JAR)
  • filter Jar tasks by classifier (e.g. exlude javadoc, sources, test-sources, etc)
  • add a excludeFrontendResourceIn(taskName) helper

"Skipping task ':vaadinBuildFrontend' as it is up-to-date") }
}

@Test

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should probably add a test for a plain JAR, since the plugin code allows it.

@mcollovati

Copy link
Copy Markdown
Collaborator

I set up a repository as an attempt to have some tests for the Gradle build cache. I hope this correctly covers the usage of build cache.

Here are two builds:

I plan to add a scenario also for the custom frontendOutputDirectory.

@benstpierre

Copy link
Copy Markdown
Author

@mcollovati
I think I signed the CLA. When I go to it again to confirm though it just freezes.
image

@mcollovati

Copy link
Copy Markdown
Collaborator

@benstpierre yep, CLA is check is green now, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Contribution PRs coming from the community or external to the team +0.0.1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

vaadinBuildFrontend: redirect frontendOutputDirectory to enable Gradle build cache restore

3 participants