Skip to content

chore(server): fix maven caching for graphql autogenerated classes - #948

Merged
FelixTJDietrich merged 2 commits into
mainfrom
chore/server-fix-maven-caching
Mar 26, 2026
Merged

chore(server): fix maven caching for graphql autogenerated classes#948
FelixTJDietrich merged 2 commits into
mainfrom
chore/server-fix-maven-caching

Conversation

@FelixTJDietrich

@FelixTJDietrich FelixTJDietrich commented Mar 26, 2026

Copy link
Copy Markdown
Collaborator

Description

Fixes #945

On every spring-boot:run / mvn compile, Maven recompiled all ~3700 autogenerated GraphQL Java classes even when schemas hadn't changed. Root cause: the quick profile skipped GraphQL codegen but not the clean-stale-classes execution, which deleted all .class files in target/classes every build — forcing full recompilation (~30s penalty).

Changes:

Change Why
Add skipCleanStaleClasses=true to quick profile Core fix. Preserves .class files so useIncrementalCompilation=false (MCOMPILER-209 workaround) can actually skip unchanged files
Change profile activation from target/classes to target/generated-sources/graphql-github <exists> matches empty directories; clean-stale-classes can leave target/classes empty, causing false activation
Upgrade maven-compiler-plugin 3.14.1 → 3.15.0 Fixes MCOMPILER-540: generated sources incorrectly added to compilation with useIncrementalCompilation=false
Include .graphql/.ftl in CI Maven cache key Prevents stale generated sources when schemas change but pom.xml doesn't
Update stale comments in ci-tests.yml and pom.xml Align documentation with new activation trigger

Not changed (intentionally):

  • skipCleanStaleClasses default stays false (CI safety)
  • useIncrementalCompilation=false stays (MCOMPILER-209 still open)
  • Maven Build Cache Extension stays disabled for local dev
  • Generated sources stay in target/ (not committed to git)

Net result: 3 files changed, +22/−38 lines (net −16)

How to test

  1. cd server/application-server
  2. ./mvnw clean compile — full build (~40s)
  3. ./mvnw compile — should complete in ~3-5s (not recompiling generated files)
  4. rm -rf target && ./mvnw compile — full rebuild (quick profile not active)
  5. CI should pass with the new cache key

Summary by CodeRabbit

  • Chores
    • Optimized build caching and compilation configuration to improve CI/CD pipeline performance for incremental builds.

The quick profile skipped GraphQL codegen but not the clean-stale-classes
execution, which deleted all .class files every build — forcing full
recompilation of thousands of generated files (~30s penalty).

Changes:
- Add skipCleanStaleClasses=true to quick profile for incremental builds
- Change activation from target/classes to target/generated-sources
  (target/classes can be an empty dir after cleanup, causing false activation)
- Upgrade maven-compiler-plugin 3.14.1→3.15.0 (fixes MCOMPILER-540)
- Include .graphql/.ftl files in CI cache key for correctness

Closes #945

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FelixTJDietrich
FelixTJDietrich requested a review from a team as a code owner March 26, 2026 14:04
Copilot AI review requested due to automatic review settings March 26, 2026 14:04
@dosubot dosubot Bot added the bug Something isn't working label Mar 26, 2026
@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Maven caching for GraphQL autogenerated classes is fixed by updating cache keys and profile activation conditions across GitHub Actions workflows and project configuration to use target/generated-sources instead of target/classes as the trigger point, and explicitly pinning the Maven compiler plugin to version 3.15.0.

Changes

Cohort / File(s) Summary
GitHub Actions Caching Configuration
.github/actions/setup-caches/action.yml, .github/workflows/ci-tests.yml
Updated Maven cache invalidation logic to include GraphQL (*.graphql) and FTL template (*.ftl) file hashes, and corrected comment reference from target/classes to target/generated-sources for cache activation trigger.
Maven Build Configuration
server/application-server/pom.xml
Modified quick profile activation to trigger on target/generated-sources/graphql-github presence; added skipCleanStaleClasses property; pinned Maven compiler plugin to version 3.15.0 to address compilation input-source handling; revised comments describing profile behavior around schema changes and stale-class cleanup.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 Cache keys aligned with sources true,
No more recompiling what's already new,
GraphQL schemas rest without churn,
Incremental builds now swiftly return!
A hop, skip, and optimization spree!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing Maven caching for GraphQL autogenerated classes, which directly aligns with the changeset modifications to build configuration.
Linked Issues check ✅ Passed The pull request addresses issue #945 by preventing Maven from recompiling autogenerated GraphQL classes and enabling incremental builds through cache key updates and stale-class cleanup configuration.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing Maven caching behavior: cache key updates, profile activation conditions, compiler version, and stale-class cleanup configuration. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/server-fix-maven-caching

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added application-server Spring Boot server: APIs, business logic, database maintenance Chores, cleanup, non-functional improvements ci GitHub Actions, workflows, build pipeline changes dependencies Package updates, version bumps, lock file changes size:M This PR changes 30-99 lines, ignoring generated files. labels Mar 26, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes incremental build performance for the application server by preventing unnecessary recompilation of thousands of GraphQL-generated Java classes, and by improving CI cache correctness.

Changes:

  • Adjust the quick Maven profile to skip stale-class cleanup (in addition to skipping GraphQL codegen) to preserve compiled .class files for incremental builds.
  • Change quick profile auto-activation to use target/generated-sources/graphql-github rather than target/classes.
  • Update CI Maven cache key to include GraphQL schema/template inputs and bump maven-compiler-plugin to address generated-sources behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
server/application-server/pom.xml Updates quick profile behavior/activation and overrides compiler plugin version; refreshes related comments.
.github/workflows/ci-tests.yml Updates documentation comment about when quick profile auto-activates in CI.
.github/actions/setup-caches/action.yml Expands Maven cache key inputs to include .graphql and .ftl files to avoid stale generated sources.

<failsafe.includedGroups>integration</failsafe.includedGroups>

<!-- GraphQL Codegen Skip (auto-set by 'quick' profile when target/classes exists) -->
<!-- GraphQL Codegen Skip (auto-set by 'quick' profile when target/generated-sources exists) -->

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

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

The comment says the quick profile auto-activates when target/generated-sources exists, but the actual activation checks for target/generated-sources/graphql-github. Please update this comment to match the real activation trigger (or broaden the activation to match the comment) so developers don’t get misled when debugging profile activation.

Suggested change
<!-- GraphQL Codegen Skip (auto-set by 'quick' profile when target/generated-sources exists) -->
<!-- GraphQL Codegen Skip (auto-set by 'quick' profile when target/generated-sources/graphql-github exists) -->

Copilot uses AI. Check for mistakes.
Comment on lines 1089 to 1101
<activation>
<!-- Auto-activate when compiled classes exist -->
<!-- Uses generated-sources dir, not target/classes, because <exists> matches
empty dirs and clean-stale-classes can leave target/classes empty. -->
<file>
<exists>${basedir}/target/classes</exists>
<exists>${basedir}/target/generated-sources/graphql-github</exists>
</file>
</activation>
<properties>
<!-- Skip GraphQL codegen - regenerated sources already exist
⚠️ Run 'mvn clean' when schema files change! -->
<graphql.codegen.skip>true</graphql.codegen.skip>
<!-- Keep Java compilation enabled for incremental development -->
<!-- maven.main.skip deliberately NOT set - we want incremental compile -->
<!-- Skip test compilation for faster spring-boot:run -->
<!-- Preserve .class files for incremental compilation -->
<skipCleanStaleClasses>true</skipCleanStaleClasses>
<!-- maven.main.skip deliberately NOT set — we want incremental compile -->
<maven.test.skip>true</maven.test.skip>

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

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

With activation now based on target/generated-sources/graphql-github, the quick profile will also auto-activate in CI whenever the workflow cache restores generated sources. That makes skipCleanStaleClasses=true and graphql.codegen.skip=true in CI, which can undermine the purpose of the clean-stale-classes execution (especially since the Maven Build Cache config attaches target/classes/test-classes to cached outputs). Consider preventing quick from activating on CI (e.g., add an activation guard like requiring !env.CI), or explicitly disable the profile / override skipCleanStaleClasses in CI so stale compiled classes cannot slip through.

Copilot uses AI. Check for mistakes.
Comment on lines +126 to 129
# -Dmaven.test.skip=false overrides the quick profile (auto-activated when target/generated-sources exists from cache)
case "${{ matrix.test-type }}" in
"application-server-unit-arch") ./mvnw test -Dsurefire.includedGroups="unit | architecture" -DskipAfterFailureCount=10 -Dmaven.test.skip=false -T 2C --batch-mode -Dmaven.build.cache.enabled=true ;;
"application-server-integration") ./mvnw test -Dsurefire.includedGroups="integration" -Dparallel=none -Dhephaestus.surefire.timeout=600 -Dmaven.test.skip=false -T 2C --batch-mode -Dmaven.build.cache.enabled=true ;;

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

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

This comment is inaccurate: -Dmaven.test.skip=false does not “override the quick profile”; it only overrides the maven.test.skip property value. If the quick profile activates (which it will when the cached generated-sources directory is restored), its other effects still apply (e.g., skipping GraphQL codegen / stale-class cleanup). Please reword the comment to reflect what is actually being overridden, and consider explicitly disabling the quick profile in CI if those other effects are not desired.

Suggested change
# -Dmaven.test.skip=false overrides the quick profile (auto-activated when target/generated-sources exists from cache)
case "${{ matrix.test-type }}" in
"application-server-unit-arch") ./mvnw test -Dsurefire.includedGroups="unit | architecture" -DskipAfterFailureCount=10 -Dmaven.test.skip=false -T 2C --batch-mode -Dmaven.build.cache.enabled=true ;;
"application-server-integration") ./mvnw test -Dsurefire.includedGroups="integration" -Dparallel=none -Dhephaestus.surefire.timeout=600 -Dmaven.test.skip=false -T 2C --batch-mode -Dmaven.build.cache.enabled=true ;;
# -Dmaven.test.skip=false overrides the maven.test.skip property so tests run even if the quick profile would skip them
# The quick profile itself is explicitly disabled in CI via -P '!quick' to avoid its other effects when target/generated-sources exists from cache
case "${{ matrix.test-type }}" in
"application-server-unit-arch") ./mvnw test -Dsurefire.includedGroups="unit | architecture" -DskipAfterFailureCount=10 -Dmaven.test.skip=false -P '!quick' -T 2C --batch-mode -Dmaven.build.cache.enabled=true ;;
"application-server-integration") ./mvnw test -Dsurefire.includedGroups="integration" -Dparallel=none -Dhephaestus.surefire.timeout=600 -Dmaven.test.skip=false -P '!quick' -T 2C --batch-mode -Dmaven.build.cache.enabled=true ;;

Copilot uses AI. Check for mistakes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/actions/setup-caches/action.yml:
- Around line 55-57: The fallback cache allows restoring stale generated sources
because restore-keys on the maven cache can bring back
server/application-server/target/generated-sources/graphql-github and cause the
"quick" profile in server/application-server/pom.xml to skip regeneration; to
fix, remove the non-exact restore-keys for the maven key (the "key:" and
"restore-keys:" entries) so the cache is exact-only, or split caches so that
target/** (generated-sources) is cached only with exact keys while
~/.m2/repository is a separate cache with restore-keys — adjust the action.yml
entries accordingly to ensure only ~/.m2/repository is eligible for fallback and
target/generated-sources is exact-match only.

In `@server/application-server/pom.xml`:
- Around line 1084-1085: The profile activation sentinel currently uses
target/generated-sources/graphql-github which is evaluated only at Maven startup
and can remain true after a subsequent clean, so update the pom/documentation:
change the activation sentinel for the quick profile from
target/generated-sources/graphql-github to a stable file outside target (for
example ${basedir}/.maven-generated-graphql) or revise the recovery guidance to
use a guaranteed-profile-disable or full target removal (e.g., replace the "Run
'mvn clean compile'..." guidance with "rm -rf target && mvn compile" or "mvn
clean compile -P!quick") and apply the same change to the other occurrence noted
in the same profile block.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 95e46f88-c273-44f3-9060-2e8b9146da59

📥 Commits

Reviewing files that changed from the base of the PR and between 8da9a41 and 3174250.

📒 Files selected for processing (3)
  • .github/actions/setup-caches/action.yml
  • .github/workflows/ci-tests.yml
  • server/application-server/pom.xml

Comment on lines +55 to 57
key: ${{ inputs.os }}-maven-${{ hashFiles('server/application-server/pom.xml', 'server/application-server/src/main/resources/graphql/**/*.graphql', 'server/application-server/src/main/resources/graphql/**/*.ftl') || 'no-pom' }}
restore-keys: |
${{ inputs.os }}-maven-

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

How does GitHub Actions actions/cachebehave when the primarykeymisses but arestore-keysprefix matches? Can it restore an older cache while reportingcache-hit as false?

💡 Result:

Yes, GitHub Actions actions/cache restores an older cache when the primary key does not exactly match any existing cache but a restore-keys prefix matches an existing cache (selecting the most recent one). In this case, it reports cache-hit as false (specifically, the output is the string 'false' or empty on miss, but 'false' when restored via restore-keys). The matching process is: 1. Search for exact match on key: if found, restore and cache-hit: 'true'. 2. Search for prefix matches on key: if found (most recent), restore but cache-hit not 'true' (treated as miss for exactness). 3. If no match for key, sequentially search restore-keys for prefix matches: if found (most recent for that prefix), restore but cache-hit: 'false'. This behavior is confirmed across official documentation, action READMEs, and GitHub issues/discussions as of 2026. Workflows typically check steps.cache.outputs.cache-hit != 'true' to run dependency installation even on partial/restore restores, as the restored cache may be stale.

Citations:


🏁 Script executed:

# Find the pom.xml file
fd pom.xml server/application-server

Repository: ls1intum/Hephaestus

Length of output: 96


🏁 Script executed:

# Check the size of pom.xml to decide how to read it
wc -l server/application-server/pom.xml

Repository: ls1intum/Hephaestus

Length of output: 101


🏁 Script executed:

# Search for the `quick` profile in pom.xml
rg -A 5 -B 2 "quick" server/application-server/pom.xml

Repository: ls1intum/Hephaestus

Length of output: 1928


🏁 Script executed:

# Verify the full action.yml to see the cache configuration context
cat -n .github/actions/setup-caches/action.yml

Repository: ls1intum/Hephaestus

Length of output: 5882


The fallback cache can still rehydrate stale generated sources.

Line 55 protects exact cache hits only. When a .graphql or .ftl file changes, the primary key misses, but lines 56-57 can restore an older ${{ inputs.os }}-maven-... entry containing stale server/application-server/target/generated-sources/graphql-github. This triggers the quick profile in server/application-server/pom.xml (which activates on directory existence), skipping GraphQL code generation even though the schema changed. Either make this cache exact-key only by removing restore-keys, or split ~/.m2/repository into a separate fallback cache while keeping target/** on exact matches only.

Proposed fix
       with:
         path: |
           ~/.m2/repository
           server/application-server/target/generated-sources
           server/application-server/target/maven-status
         key: ${{ inputs.os }}-maven-${{ hashFiles('server/application-server/pom.xml', 'server/application-server/src/main/resources/graphql/**/*.graphql', 'server/application-server/src/main/resources/graphql/**/*.ftl') || 'no-pom' }}
-        restore-keys: |
-          ${{ inputs.os }}-maven-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
key: ${{ inputs.os }}-maven-${{ hashFiles('server/application-server/pom.xml', 'server/application-server/src/main/resources/graphql/**/*.graphql', 'server/application-server/src/main/resources/graphql/**/*.ftl') || 'no-pom' }}
restore-keys: |
${{ inputs.os }}-maven-
key: ${{ inputs.os }}-maven-${{ hashFiles('server/application-server/pom.xml', 'server/application-server/src/main/resources/graphql/**/*.graphql', 'server/application-server/src/main/resources/graphql/**/*.ftl') || 'no-pom' }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/actions/setup-caches/action.yml around lines 55 - 57, The fallback
cache allows restoring stale generated sources because restore-keys on the maven
cache can bring back
server/application-server/target/generated-sources/graphql-github and cause the
"quick" profile in server/application-server/pom.xml to skip regeneration; to
fix, remove the non-exact restore-keys for the maven key (the "key:" and
"restore-keys:" entries) so the cache is exact-only, or split caches so that
target/** (generated-sources) is cached only with exact keys while
~/.m2/repository is a separate cache with restore-keys — adjust the action.yml
entries accordingly to ensure only ~/.m2/repository is eligible for fallback and
target/generated-sources is exact-match only.

Comment on lines +1084 to +1085
Run 'mvn clean compile' after modifying .graphql schemas or deleting source files.
Override: mvn compile -Dgraphql.codegen.skip=false | mvn compile -P!quick

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Maven, when are profile activation <file><exists>...</exists></file> checks evaluated relative to a mvn clean compileinvocation? If the file exists before the build starts, does the profile remain active even though thecleanphase later deletestarget/?

💡 Result:

Maven profile activation using is evaluated very early in the build process, during the Maven Model Builder phase before any lifecycle phases (like clean or compile) are executed. Specifically, it occurs during profile activation in phase 1 of model building, prior to model interpolation, inheritance assembly, and execution of any plugins or lifecycle goals. For a mvn clean compile invocation, which executes the clean lifecycle (deleting target/) followed by default lifecycle up to compile: - Profiles are activated based on the filesystem state at Maven startup, before clean runs. - If the file (e.g., in target/) exists before the build starts, the profile activates and remains active throughout the entire build, including after clean deletes target/. The effective POM, incorporating active profiles, is computed once at the beginning and used for all subsequent lifecycle execution. Profile activation is not re-evaluated during lifecycle phases.

Citations:


mvn clean compile is an unsafe recovery path with the current profile activation sentinel.

Maven profile activation checks ... only once at startup, before any lifecycle phases execute. With target/generated-sources/graphql-github as the activation sentinel on Line 1093, if this file exists before the build starts, the quick profile activates and remains active throughout the entire build—including after the clean phase deletes target/. This means the GraphQL codegen skip directive on Lines 1097–1099 stays in effect for the compile phase, defeating the recovery guidance on Lines 1084–1085.

Recommend either: move the sentinel outside target/ (e.g., ${basedir}/.maven-generated-graphql with manual cleanup), or update the documented recovery path to rm -rf target && mvn compile or mvn clean compile -P!quick (force-disable the quick profile).

Also applies to: 1090–1100

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/application-server/pom.xml` around lines 1084 - 1085, The profile
activation sentinel currently uses target/generated-sources/graphql-github which
is evaluated only at Maven startup and can remain true after a subsequent clean,
so update the pom/documentation: change the activation sentinel for the quick
profile from target/generated-sources/graphql-github to a stable file outside
target (for example ${basedir}/.maven-generated-graphql) or revise the recovery
guidance to use a guaranteed-profile-disable or full target removal (e.g.,
replace the "Run 'mvn clean compile'..." guidance with "rm -rf target && mvn
compile" or "mvn clean compile -P!quick") and apply the same change to the other
occurrence noted in the same profile block.

@FelixTJDietrich
FelixTJDietrich merged commit ecfb078 into main Mar 26, 2026
48 checks passed
@FelixTJDietrich
FelixTJDietrich deleted the chore/server-fix-maven-caching branch March 26, 2026 16:30
@github-actions

Copy link
Copy Markdown
Contributor

📚 Documentation Preview

Preview has been removed (PR closed)

@FelixTJDietrich

Copy link
Copy Markdown
Collaborator Author

🎉 This PR is included in version 0.52.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@FelixTJDietrich FelixTJDietrich added the released Included in a published release label Mar 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

application-server Spring Boot server: APIs, business logic, database bug Something isn't working ci GitHub Actions, workflows, build pipeline changes dependencies Package updates, version bumps, lock file changes maintenance Chores, cleanup, non-functional improvements released Included in a published release size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(server): fix maven caching for graphql autogenerated classes

2 participants