feat(feature-flag): add FeatureFlagPlugin with JSON schema validator - #11346
feat(feature-flag): add FeatureFlagPlugin with JSON schema validator#11346rafaeltonholo wants to merge 10 commits into
Conversation
32b6962 to
65ee9c0
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Gradle plugin in :build-plugin:plugin to validate the declarative feature flag catalog against a JSON Schema at root-project configuration time, and wires it up in the root build so the repository catalog/schema under config/featureflag/ are validated during builds.
Changes:
- Added
net.thunderbird.gradle.plugin.featureflagGradle plugin plus aSchemaValidatorbacked bycom.networknt:json-schema-validator. - Added the feature flag schema and catalog files under
config/featureflag/, and configured the root build to validate them. - Updated related RFC/technical design documentation status/links to reflect acceptance and the new schema name.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| gradle/libs.versions.toml | Adds the NetworkNT JSON schema validator dependency and registers the new build plugin alias. |
| docs/SUMMARY.md | Moves technical design 0002 into the “Accepted” section. |
| docs/engineering/technical-designs/0002-feature-flag-declarative-catalog.md | Updates technical design status to Accepted. |
| docs/engineering/rfcs/0004-feature-flag-new-architecture.md | Updates the example catalog’s $schema filename to match the shipped schema. |
| config/featureflag/thunderbird_mobile_featureflag.schema.json | Adds the JSON Schema used to validate the feature flag catalog. |
| config/featureflag/thunderbird_mobile_featureflag.catalog.json | Adds the repository’s feature flag catalog to be validated at build time. |
| build.gradle.kts | Applies and configures the feature flag Gradle plugin in the root build. |
| build-plugin/plugin/src/test/kotlin/net/thunderbird/gradle/plugin/testing/rule/ProjectTempFolderRule.kt | Adds a JUnit rule helper for writing temp project files in plugin tests. |
| build-plugin/plugin/src/test/kotlin/net/thunderbird/gradle/plugin/featureflag/schema/SchemaValidatorTest.kt | Adds unit tests for schema validation behavior (missing files, multiple violations, format assertions). |
| build-plugin/plugin/src/test/kotlin/net/thunderbird/gradle/plugin/featureflag/FeatureFlagPluginTest.kt | Adds Gradle plugin tests for extension registration and configuration-time validation failures/success. |
| build-plugin/plugin/src/test/kotlin/net/thunderbird/gradle/plugin/featureflag/fake/FakeData.kt | Provides fake schema/catalog fixtures for validator/plugin tests. |
| build-plugin/plugin/src/main/kotlin/net/thunderbird/gradle/plugin/featureflag/schema/SchemaValidator.kt | Implements JSON Schema validation using NetworkNT’s validator. |
| build-plugin/plugin/src/main/kotlin/net/thunderbird/gradle/plugin/featureflag/FeatureFlagPluginExtension.kt | Adds the root-project featureFlag extension (schema, catalog, validateFormats). |
| build-plugin/plugin/src/main/kotlin/net/thunderbird/gradle/plugin/featureflag/FeatureFlagPlugin.kt | Implements plugin application and configuration-time validation/error reporting. |
| build-plugin/plugin/build.gradle.kts | Registers the new Gradle plugin ID and adds required dependencies for implementation/tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
65ee9c0 to
9486e5b
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 18 changed files in this pull request and generated 2 comments.
Suppressed comments (2)
build-plugin/plugin/src/main/kotlin/net/thunderbird/gradle/plugin/featureflag/validator/CatalogValidator.kt:32
keyRegistryis built viacatalog.flags.associate { it.key to it.default }, which silently drops duplicate flag keys (last one wins). The JSON schema only hasuniqueItems: truefor theflagsarray, which doesn't guaranteekeyuniqueness, so a catalog with duplicate keys could pass schema validation but produce ambiguous defaults/override validation.
Consider explicitly detecting duplicate flags[].key values and failing validation with a clear error message listing the duplicates.
fun validate(catalog: FeatureFlagCatalog): Result {
val keyRegistry = catalog.flags.associate { it.key to it.default }
val tbOverrides = catalog.overrides.thunderbird
build-plugin/plugin/src/test/kotlin/net/thunderbird/gradle/plugin/featureflag/validator/SchemaValidatorTest.kt:169
- Acceptance criteria require tests for rejecting an unknown application and an unknown build type in
overrides. Current validator/plugin tests cover unknown override keys, but there isn't a test asserting schema validation fails for e.g.overrides.unknown_apporoverrides.thunderbird.unknown_build.
Add a SchemaValidator test using catalogSchemaFile() (real schema) and a catalog JSON containing both cases, then assert the returned errors mention additionalProperties/unknown property for the offending paths.
@Test
fun `validate should return Success when override keys match the flag key format`() {
// Arrange
val schemaFile = catalogSchemaFile()
val catalogAsText = FakeData.catalogWithOverrideKey("archive_marks_as_read")
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 18 changed files in this pull request and generated no new comments.
Suppressed comments (3)
build-plugin/plugin/src/test/kotlin/net/thunderbird/gradle/plugin/featureflag/fake/FakeData.kt:3
- This file imports
FakeData.SCHEMAfrom itself, but the import is unused. Because:build-plugin:pluginsetsallWarningsAsErrors = true, this will fail compilation.
import net.thunderbird.gradle.plugin.featureflag.fake.FakeData.SCHEMA
build-plugin/plugin/src/test/kotlin/net/thunderbird/gradle/plugin/featureflag/validator/SchemaValidatorTest.kt:176
- The linked acceptance criteria (#11326) explicitly call for tests covering “Unknown application” and “Unknown build type”. Current schema validation tests cover required properties, additional properties at the root, key formats, and date formats, but don’t assert that the real shipped schema rejects unexpected entries under
overrides(e.g.,overrides.unknownApporoverrides.thunderbird.unknownBuildType). Please add unit tests validating these cases againstconfig/featureflag/thunderbird_mobile_featureflag.schema.json(similar to the existing override-key-format tests).
@Test
fun `validate should return Success when override keys match the flag key format`() {
// Arrange
val schemaFile = catalogSchemaFile()
val catalogAsText = FakeData.catalogWithOverrideKey("archive_marks_as_read")
val testSubject = SchemaValidator(validateFormats = true)
// Act
val result = testSubject.validate(schemaFile = schemaFile, catalogAsText = catalogAsText)
// Assert
assertThat(result).isEqualTo(SchemaValidator.Result.Success)
build-plugin/plugin/src/main/kotlin/net/thunderbird/gradle/plugin/featureflag/validator/SchemaValidator.kt:27
- The KDoc says this checks whether “both files exist” and that
FileNotFoundis returned if either file is missing, butSchemaValidatoronly checks the schema file (the catalog is passed as text). This can mislead future callers about what is validated here.
* Checks if both files exist, loads the schema, and validates the catalog content against it.
* Format assertions are enabled or disabled based on the validator configuration.
*
* @param schemaFile The JSON schema file to validate against
* @param catalogAsText The JSON catalog as text to validate
* @return Result.Success if validation passes, Result.Error.FileNotFound if either file doesn't exist,
* or Result.Error.ValidationFailed if the catalog doesn't conform to the schema
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 18 changed files in this pull request and generated 1 comment.
Suppressed comments (3)
build-plugin/plugin/src/main/kotlin/net/thunderbird/gradle/plugin/featureflag/FeatureFlagPlugin.kt:48
- Error message contains a double space ("The feature flag schema"), which looks like a typo and will leak into build failures.
"Failed to apply feature flag plugin. Reason: The feature flag schema was not found at '${schema.path}'.",
build-plugin/plugin/src/main/kotlin/net/thunderbird/gradle/plugin/featureflag/validator/SchemaValidator.kt:23
- KDoc for validate() mentions a Result.Error.FileNotFound variant that doesn't exist in SchemaValidator.Result (and the method takes contents, not files). This is misleading documentation.
* @return Result.Success if validation passes, Result.Error.FileNotFound if either file doesn't exist,
* or Result.Error.ValidationFailed if the catalog doesn't conform to the schema
build-plugin/plugin/src/test/kotlin/net/thunderbird/gradle/plugin/featureflag/FeatureFlagPluginTest.kt:139
- The new schema enforces known applications/build types via additionalProperties=false, but the test suite here doesn't cover the failure cases required by #11326 (e.g., unknown application under overrides, unknown build type under an app). Adding these cases would ensure schema validation behavior is exercised end-to-end by the plugin tests.
@Test
06a0a53 to
bca8bbe
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.
Suppressed comments (2)
build-plugin/plugin/src/main/kotlin/net/thunderbird/gradle/plugin/featureflag/FeatureFlagPlugin.kt:44
- The catalog-missing error message here doesn't match the plugin tests (FeatureFlagPluginTest expects "Failed to apply feature flag plugin. Reason: The catalog file '' not found."). As-is, the test should fail even though the behavior is correct.
val catalog = extension.catalog.asFile.get()
val catalogContents = readTextOrNull(extension.catalog) ?: throw GradleException(
"Failed to apply feature flag plugin. Reason: The feature flag catalog was not found at '${catalog.path}'.",
)
build-plugin/plugin/src/main/kotlin/net/thunderbird/gradle/plugin/featureflag/validator/SchemaValidator.kt:24
- This KDoc mentions Result.Error.FileNotFound / Result.Error.ValidationFailed, but SchemaValidator.Result has only Success and ValidationFailed (and no nested Error type). This is misleading documentation for callers and for future maintenance.
* @param schemaContents The JSON schema file contents to validate against
* @param catalogContents The JSON catalog file contents as text to validate
* @return Result.Success if validation passes, Result.Error.FileNotFound if either file doesn't exist,
* or Result.Error.ValidationFailed if the catalog doesn't conform to the schema
*/
| @@ -16,6 +17,11 @@ kotlin { | |||
| compilerOptions { | |||
| jvmTarget = JvmTarget.JVM_21 | |||
| } | |||
| sourceSets { | |||
- Add SchemaValidator for validating JSON catalogs against JSON schemas - Add FeatureFlagPlugin to validate feature flag catalog at configuration time - Configure plugin in root build script with catalog and schema files - Add networknt json-schema-validator dependency
- Validate that all override keys reference existing flags - Validate that overrides differ from their flag defaults - Report missing keys and redundant values per override - Support both Thunderbird and K-9 Mail override structures
- Pass catalog as text instead of File to SchemaValidator - Add CatalogValidator call after schema validation in plugin - Introduce catalog() builder function in FakeData for test maintainability - Add test coverage for unknown override key validation error - Simplify test setup by reducing File I/O operations
…reading - Read files through providers.fileContents() to track as configuration cache inputs - Pass file contents as strings instead of File objects to validators - Remove FileNotFound result type from SchemaValidator as file existence is checked upfront - Improve error messages to clarify when catalog or schema files are not found
- Replace context receivers with explicit map parameter passing - No longer needed as context receivers were removed from CatalogValidator
- Remove double space in schema error message - Update test expectations to match corrected error messages
bca8bbe to
39666a6
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 13 changed files in this pull request and generated no new comments.
Suppressed comments (2)
build-plugin/plugin/src/test/kotlin/net/thunderbird/gradle/plugin/featureflag/fake/FakeData.kt:3
- This import is unused (the KDoc links to
SCHEMAin the same object without needing an import) and will fail compilation because this module setsallWarningsAsErrors = true.
import net.thunderbird.gradle.plugin.featureflag.fake.FakeData.SCHEMA
build-plugin/plugin/src/main/kotlin/net/thunderbird/gradle/plugin/featureflag/validator/SchemaValidator.kt:24
- The KDoc for
validate()mentionsResult.Error.FileNotFound/Result.Error.ValidationFailed, butResultonly definesSuccessandValidationFailed, and this method validates string contents (not files). This documentation is misleading for callers.
* Validates a JSON catalog file against a JSON schema file.
*
* @param schemaContents The JSON schema file contents to validate against
* @param catalogContents The JSON catalog file contents as text to validate
* @return Result.Success if validation passes, Result.Error.FileNotFound if either file doesn't exist,
* or Result.Error.ValidationFailed if the catalog doesn't conform to the schema
*/
Contribution Summary
Linked Issue/Ticket: Resolves #11326
RFC / Technical Design (if applicable): RFC 0004: Add a Declarative Feature Flag Catalog / Technical Design 0002: Declarative Feature Flag Catalog
Description
AI Disclosure
Select one of the following (mandatory)
Contribution Checklist
gradlew spotlessCheckto check andgradlew spotlessApplyto format your source code; will be checked by CI).gradlew testDebugUnitTest; will be checked by CI).