Add support for source set and Spring profile specific application properties#1807
Open
denisfalqueto wants to merge 4 commits into
Open
Add support for source set and Spring profile specific application properties#1807denisfalqueto wants to merge 4 commits into
denisfalqueto wants to merge 4 commits into
Conversation
Add a SourceSet enum to the buildsystem package, modeling the main/test split of the standard directory layout shared by the supported build systems, alongside the existing DependencyScope classification. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com>
ApplicationProperties becomes a composite: the root instance keeps
representing the main source set with the default profile, and a new
section(SourceSet, String) method returns the properties of any other
(source set, profile) pair, creating it on demand. Sections expose the
exact same API as the root and cannot be nested.
The properties and YAML contributors now share a new base class,
AbstractApplicationPropertiesContributor, which writes one file per
non-empty section following the
src/{sourceSet}/resources/application[-{profile}] convention. The file
for the main source set and default profile is still always written,
preserving the existing behavior, and the public API is untouched.
Compliance tests gain the (source set, profile) pair as an additional
parameterized axis for both configuration file formats.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com>
Describe in the configuration guide how application properties can be contributed to the generated project, including how to target another source set or Spring profile through sections, with a runnable example. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com>
Declare the local variable as @nullable Object[] to match the JSpecify-annotated return type of Arguments.get() in recent JUnit versions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add support for source set and Spring profile specific application properties
Disclaimer
These commits were made with aid of Claude Code's models. But they were supervised by me, in each step. I assume responsibility for the reasoning and approval of these changes, as well as providing addition information.
Motivation
The
ApplicationPropertiesabstraction introduced ininitializr-generator-springallows contributors to add properties to the generated project's configuration file. However, it is currently a single flat key-value container, and bothApplicationPropertiesContributorandApplicationYamlPropertiesContributorwrite it to a hardcoded location:src/main/resources/application.properties(or.yaml).This means an
ApplicationPropertiesCustomizerhas no way to express two very common needs of generated projects:Test-only configuration. Properties that should only apply when running tests (for example, an in-memory datasource URL, or disabling a cloud integration during tests) belong in
src/test/resources/application.properties, not in the main configuration packaged with the application.Profile-specific configuration. Spring Boot's convention of
application-{profile}.properties/application-{profile}.yamlfiles is the idiomatic way to separate configuration per environment, yet the generation model cannot produce these files today.As a result, custom Initializr instances that need either behavior must bypass the
ApplicationPropertiesabstraction entirely and write raw files through a dedicatedProjectContributor, duplicating the properties/YAML rendering logic that already exists in this module.This PR generalizes the model so that application properties can be targeted at a (source set, Spring profile) pair, while keeping the existing single-file behavior and public API fully intact.
What this change does
ApplicationPropertiesbecomes a composite of itself. The root instance — the bean that customizers already receive — represents the main source set with the default profile, exactly as before. A newsection(SourceSet, String profile)method returns theApplicationPropertiesfor any other (source set, profile) combination, creating it on demand:The two file contributors now share a new base class,
AbstractApplicationPropertiesContributor, which iterates the root and its sections and resolves the output path following the standard convention:The file for the main source set and default profile is always written, even when empty, preserving today's behavior. Files for other sections are only written when the section actually contains properties.
The reference documentation now covers the
ApplicationPropertiesCustomizerextension point, including how to target sections, with a runnable example inProjectCustomizationExamples.Design decisions
No breaking change to the public API. The signatures of
ApplicationProperties(alladd/get/contains/removeoverloads),ApplicationPropertiesCustomizer, and both contributors' constructors are untouched. Existing customizers — including the lambda-based registrations that are the documented idiom for this extension point — keep working unmodified and keep targeting the main/default file. The feature is purely additive: one new public method (section, plus a convenience overload) and one new public enum (SourceSet).Dimensions are fixed by navigation, not by method parameters. The (source set, profile) pair is fixed once when obtaining the section, and the section then exposes the exact same API as the root, keeping every typed
addoverload untouched. This mirrors the idiom already established in this code base byMavenBuild, wherebuild.profiles().id("profile-id")navigates to aMavenProfilethat exposes the regular build-configuration API scoped to that profile.Default profile is represented by
null, not the string"default". The literal"default"has its own semantics in Spring (spring.profiles.default) and using it as a sentinel could suggest aapplication-default.propertiesfile should be generated. Anullprofile unambiguously means "the profile-lessapplication.{properties|yaml}file".Sections cannot be nested. Calling
section(...)on a section throws anIllegalStateException, andsection(SourceSet.MAIN, null)on the root returns the root itself, so there is exactly one canonical instance per (source set, profile) pair.Testing
Unit tests were added for the section semantics (root identity for main/default, idempotent section retrieval, isolation between sections, rejection of nesting and of blank profile names) and for the contributor output paths (test source set, profile-specific files, combination of both, empty sections producing no file, and the main/default file still always being written).
ApplicationPropertiesComplianceTestswas extended with the source set/profile pair as an additional parameterized axis: for both the properties and YAML formats, a customizer targeting a section is verified end-to-end to produce the expected file at the expected location, while the main/default file keeps being generated.All existing tests pass unchanged, which follows from the API being untouched.
Why
SourceSetlives inio.spring.initializr.generator.buildsystemAlthough this PR only consumes
SourceSetfrom the properties support ininitializr-generator-spring, the enum was deliberately placed in theio.spring.initializr.generator.buildsystempackage ofinitializr-generator, for three reasons.First, the concept genuinely belongs to the build-system domain, not to the properties feature. The main/test split is defined by the build systems this project abstracts over: it is Maven's Standard Directory Layout (
src/main,src/test, mirrored lifecycle phases and output directories) and it is literally Gradle's source set concept, from which the name is borrowed. A type that answers "which tree of the standard directory layout does this artifact belong to" is build-system vocabulary regardless of which feature happens to ask the question.Second, the package already contains the direct sibling of this concept:
DependencyScope. That enum models the build-system-agnostic classification of dependencies (compile, runtime, test-compile, ...) in a neutral form that eachBuildSystemimplementation translates to its own representation.SourceSetdoes exactly the same for the classification of sources and resources. Placing the two classification axes side by side keeps the build abstraction's vocabulary coherent and discoverable.Third, it enables reuse without churn. Other contributors in the code base hardcode the same layout knowledge today — for example
WebFoldersContributorresolvessrc/main/resources/templatesandsrc/main/resources/staticas string literals — and language source-code contributors are inherently organized around the main/test split. Hosting the enum in the sharedbuildsystempackage lets those spots adopt it incrementally as further consumers appear.No module dependencies change:
initializr-generator-springalready depends oninitializr-generator.Closes #1806