Skip to content

vaadinBuildFrontend: redirect frontendOutputDirectory to enable Gradle build cache restore #24012

@benstpierre

Description

@benstpierre

Problem

vaadinBuildFrontend writes Vite output to build/resources/main/META-INF/VAADIN/webapp/ — the same directory owned by Gradle's processResources. Gradle's overlapping-outputs validation prevents declaring this as a task @OutputDirectory. Without declared outputs:

  • Local up-to-date check works (with @CacheableTask + @Input from PR feat: BuildFrontend Incremental build #23884)
  • Build cache restore does not — Gradle skips the task on a cache hit but doesn't restore the frontend files. The jar ships without index.html and the app crashes at runtime.

This is the CI use case — the one that matters most for build performance.

Discussed in #17354@mcollovati suggested opening a dedicated ticket.

Root cause

servletResourceOutputDirectory() in GradlePluginAdapter is hardcoded to build/resources/main/META-INF/VAADIN/ (when isBeforeProcessResources=false), and frontendOutputDirectory in VaadinFlowPluginExtension defaults to a subdirectory of it. This puts both inside processResources territory.

ProdBundleUtils.compressBundle() reads from servletResourceOutputDirectory, so redirecting frontendOutputDirectory alone breaks the bundle — both need to move together.

Proposed fix

  1. Change the default frontendOutputDirectory to a non-conflicting path — e.g. build/vaadin-frontend/META-INF/VAADIN/webapp/
  2. Move servletResourceOutputDirectory alongside it (or derive it from the same base)
  3. Declare frontendOutputDirectory as @OutputDirectory on VaadinBuildFrontendTask
  4. Wire the plugin to automatically add a from(frontendOutputDirectory) to the jar task so the files end up on the classpath

This preserves the existing behavior (files end up at META-INF/VAADIN/webapp/ in the jar) while letting Gradle's build cache do its job.

Impact

We measured the difference on our project (Vaadin 24.9.12, ~42 JS chunks):

Scenario Time
Cache miss (Vite runs) ~26s
Cache hit (task skipped) ~5s
Saved ~21s per build

On GitHub Actions with persistent runners: build step dropped from ~116s to ~54s.

Our workaround (for anyone stuck on 24.x)

We declare src/main/bundles/ as the task output (contains prod.bundle), then zipTree() it in the jar task into META-INF/VAADIN/. We're unzipping the zip that compressBundle() created from the files we can't cache directly. Details: https://github.com/galateatech/waste-coordinator/pull/10283

Happy to contribute a PR if the team can confirm the approach direction.

Environment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions