Skip to content

Restore lenient config handling#15949

Merged
jdaugherty merged 3 commits into
7.0.xfrom
worktree-fix-15818-conversion-service
Jul 10, 2026
Merged

Restore lenient config handling#15949
jdaugherty merged 3 commits into
7.0.xfrom
worktree-fix-15818-conversion-service

Conversation

@jdaugherty

@jdaugherty jdaugherty commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes #15818

Grails applications reject lenient configuration values (e.g. lowercase enum names) that work in plain Spring Boot applications. For example, this standard actuator configuration causes the application to fail on startup:

management:
  endpoint:
    heapdump:
      access: unrestricted

Invalid value 'unrestricted' for configuration property 'management.endpoint.heapdump.access' ...
Failed to convert to type org.springframework.boot.actuate.endpoint.Access

Root cause: GrailsApp has overridden SpringApplication.configureEnvironment() since Grails 3 without delegating to super. Spring Boot's implementation installs the ApplicationConversionService on the environment, which provides the lenient converters (lowercase/hyphenated enum names, durations, etc.). Because Grails skipped that step, the environment fell back to a plain DefaultConversionService, whose string-to-enum conversion is case-sensitive. This only affects properties resolved directly through environment.getProperty(name, type) — such as the actuator's PropertiesEndpointAccessResolver — since @ConfigurationProperties binding supplies its own conversion service.affects properties resolved directly through environment.getProperty(name, type) — such as the actuator's PropertiesEndpointAccessResolver — since @ConfigurationProperties binding supplies its own conversion service.

Change: GrailsApp.configureEnvironment() now delegates to super.configureEnvironment(environment, args) in place of the direct configurePropertySources() call. This installs the conversion service while preserving identical behavior otherwise (configureProfiles() is an empty method in Spring Boot 3.x, and the Grails-specific profile handling is unchanged).

Tests:

  • GrailsAppEnvironmentConversionSpec (grails-core) boots a minimal GrailsApp and verifies lowercase (unrestricted) and hyphenated (read-only) enum values resolve via environment.getProperty(). This spec fails without the fix.
  • The grails-test-examples/external-configuration functional app now declares the exact configuration from the issue (management.endpoint.heapdump.access: unrestricted, threaddump.access: read-only), which previously prevented startup, and the new RelaxedPropertyResolutionSpec verifies both values resolve to the Access enum through the Spring environment.

Contributor Checklist

Issue and Scope

  • This PR is linked to an existing issue that has been acknowledged or approved by the project team.
  • This PR addresses the complete scope of the linked issue.
  • This PR contains a single, focused change.
  • This PR targets the correct branch for the type of change (bug fix targeting 7.0.x).

Code Quality

  • I have added or updated tests that cover the changes introduced in this PR.
  • I have verified that all existing tests pass by running ./gradlew build --rerun-tasks.
  • My code follows the project's code style guidelines.
  • This PR does not include mass reformatting, style-only changes, or large-scale refactoring.
  • If generative AI tooling was used in preparing this contribution, a quality model was used to ensure contributions are consistent with the project's quality standards.

Licensing and Attribution

  • All contributed code is provided under the Apache License 2.0, and new source files include the appropriate Apache license header.
  • I have the necessary rights to submit this contribution and confirm it is my own original work.
  • If generative AI tooling was used in preparing this contribution, I have followed the ASF's policy on generative tooling and have properly attributed its use.

Documentation

  • If this PR introduces user-facing changes, I have included or updated the relevant documentation. (No doc change needed — this restores documented Spring Boot behavior; no new or changed public API.)
  • If this PR adds a new feature, I have updated the What's New section. (N/A — bug fix.)
  • If this PR introduces breaking changes... (N/A — no user action required.)
  • The PR description clearly explains what was changed and why.

@jdaugherty jdaugherty changed the base branch from 8.0.x to 7.0.x July 9, 2026 20:33
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (7.0.x@34bc9e9). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff            @@
##             7.0.x   #15949   +/-   ##
========================================
  Coverage         ?   49.36%           
  Complexity       ?    16777           
========================================
  Files            ?     1986           
  Lines            ?    93336           
  Branches         ?    16337           
========================================
  Hits             ?    46071           
  Misses           ?    40139           
  Partials         ?     7126           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@testlens-app

This comment has been minimized.

@jdaugherty jdaugherty marked this pull request as ready for review July 9, 2026 22:35
@jdaugherty jdaugherty requested review from codeconsole and Copilot July 9, 2026 22:36

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

This PR restores Spring Boot’s lenient/relaxed type conversion for values resolved via environment.getProperty(name, type) in Grails applications by ensuring the Spring Boot ApplicationConversionService is installed on the Environment during startup (fixing actuator management.endpoint.*.access enum conversion such as unrestricted / read-only).

Changes:

  • Delegate GrailsApp.configureEnvironment(...) to SpringApplication’s implementation to install the ApplicationConversionService.
  • Add a new core unit test verifying relaxed enum conversion via environment.getProperty(..., Enum) in a minimal GrailsApp boot.
  • Update the external-configuration functional example to include the problematic actuator config and add an integration spec asserting relaxed enum conversion against the running app’s Environment.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
grails-core/src/main/groovy/grails/boot/GrailsApp.groovy Delegates configureEnvironment to Spring Boot to restore application-level conversion service installation.
grails-core/src/test/groovy/grails/boot/GrailsAppEnvironmentConversionSpec.groovy Regression test for relaxed enum conversion through environment.getProperty.
grails-test-examples/external-configuration/grails-app/conf/application.yml Adds actuator endpoint access values that previously broke startup to reproduce the issue in the example app.
grails-test-examples/external-configuration/src/integration-test/groovy/test/app/RelaxedPropertyResolutionSpec.groovy Integration regression tests validating conversion service behavior in the example app environment.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@bito-code-review

Copy link
Copy Markdown

The suggestion to scope the management.endpoint.heapdump.access: unrestricted configuration to a specific profile is appropriate. Currently, this configuration is applied globally in grails-test-examples/external-configuration/grails-app/conf/application.yml, which could inadvertently expose the heapdump endpoint in non-test environments. Scoping it to the test profile would ensure this relaxed security setting is only active during testing.

grails-test-examples/external-configuration/grails-app/conf/application.yml

--- 
# Lenient enum values resolved via environment.getProperty(name, Access) rather than
# relaxed configuration-property binding - regression test for issue #15818
spring:
  profiles: test
management:
  endpoint:
    heapdump:
      access: unrestricted
    threaddump:
      access: read-only

@codeconsole codeconsole 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.

LGTM. Verified the core claim against the Spring Boot 3.5.16 bytecode the build resolves: SpringApplication.configureEnvironment() does exactly three things — installs a new ApplicationConversionService (guarded by addConversionService, default true), calls configurePropertySources() (the same call the old code made directly, so no double invocation and no ordering change for the profile logic that follows), and calls configureProfiles(), which has an empty body in Boot 3.5. Delegating to super is a clean restoration of the Boot contract.

Also confirmed no other entry point needs the same fix: GrailsSwingConsole, GrailsShell, and DevelopmentGrailsApplication all extend GrailsApp without overriding configureEnvironment, so they inherit it, and no other override exists in the repo.

Tests are well-placed: the unit spec follows the existing GrailsAppPidFileSpec pattern and fails without the fix, and since the pre-fix failure mode was startup failure, booting the external-configuration app with the issue's exact yml is itself the regression test. Nice that setAddConversionService(false) remains as an escape hatch if the new leniency ever causes trouble for an app.

A couple of non-blocking observations inline.

Comment thread grails-core/src/main/groovy/grails/boot/GrailsApp.groovy
@jdaugherty jdaugherty changed the title Restore lenient config handling for Spring configuration Restore lenient config handling Jul 10, 2026
@jdaugherty jdaugherty merged commit 105ab33 into 7.0.x Jul 10, 2026
10 of 45 checks passed
@jdaugherty jdaugherty deleted the worktree-fix-15818-conversion-service branch July 10, 2026 11:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

relaxed/lenient config property resolution not working

3 participants