mirror of
https://github.com/bitsycore/bitsykonfig-gradle-plugin.git
synced 2026-10-07 04:27:27 +00:00
Compare commits
1
Commits
0.6.0
..
6dbdc93122
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dbdc93122 |
@@ -82,7 +82,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
if gh release view "$VERSION" --repo "$GITHUB_REPOSITORY" > /dev/null 2>&1; then
|
if gh release view "$VERSION" --repo "$GITHUB_REPOSITORY" > /dev/null 2>&1; then
|
||||||
echo "Release $VERSION already exists — skipping."
|
echo "Release $VERSION already exists - skipping."
|
||||||
else
|
else
|
||||||
gh release create "$VERSION" \
|
gh release create "$VERSION" \
|
||||||
--repo "$GITHUB_REPOSITORY" \
|
--repo "$GITHUB_REPOSITORY" \
|
||||||
|
|||||||
+1
-1
@@ -5,6 +5,6 @@
|
|||||||
local.properties
|
local.properties
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# Local credential overrides — never commit tokens
|
# Local credential overrides - never commit tokens
|
||||||
gradle.properties.local
|
gradle.properties.local
|
||||||
secrets.properties
|
secrets.properties
|
||||||
@@ -11,7 +11,7 @@ This file provides guidance to AI agents (Claude, Copilot, Codex, etc.) working
|
|||||||
# Run unit tests only
|
# Run unit tests only
|
||||||
./gradlew test
|
./gradlew test
|
||||||
|
|
||||||
# Run functional tests (Gradle TestKit — starts real Gradle builds)
|
# Run functional tests (Gradle TestKit - starts real Gradle builds)
|
||||||
./gradlew functionalTest
|
./gradlew functionalTest
|
||||||
|
|
||||||
# Run a single functional test
|
# Run a single functional test
|
||||||
@@ -51,20 +51,20 @@ region) each with their own variants.
|
|||||||
|
|
||||||
**Configuration cache compatibility** is a hard requirement throughout. This means:
|
**Configuration cache compatibility** is a hard requirement throughout. This means:
|
||||||
- Never capture `project` inside a `Provider.map {}` or `Provider.zip {}` lambda, and **never
|
- Never capture `project` inside a `Provider.map {}` or `Provider.zip {}` lambda, and **never
|
||||||
access `project` inside a `@TaskAction`** — both break caching. Use only declared
|
access `project` inside a `@TaskAction`** - both break caching. Use only declared
|
||||||
`@Input`/`@OutputDirectory` properties inside task actions.
|
`@Input`/`@OutputDirectory` properties inside task actions.
|
||||||
- Use `Class<T>` (`.javaObjectType`) instead of `KClass<T>` — Kotlin's `KClass` uses
|
- Use `Class<T>` (`.javaObjectType`) instead of `KClass<T>` - Kotlin's `KClass` uses
|
||||||
`SoftReference` internally which Gradle can't serialize.
|
`SoftReference` internally which Gradle can't serialize.
|
||||||
- Use `gradlePropertiesPrefixedBy()` to read groups of properties — **note:** in Gradle 9.x this
|
- Use `gradlePropertiesPrefixedBy()` to read groups of properties - **note:** in Gradle 9.x this
|
||||||
returns full property names as keys (prefix is NOT stripped), so always check both
|
returns full property names as keys (prefix is NOT stripped), so always check both
|
||||||
`dimProps["env"]` and `dimProps["konfig.dimension.env"]`.
|
`dimProps["env"]` and `dimProps["konfig.dimension.env"]`.
|
||||||
- All DSL field values are wrapped in `Provider<T>` from the start — literals via
|
- All DSL field values are wrapped in `Provider<T>` from the start - literals via
|
||||||
`constantProvider(value)` (a hand-written `ConstantProvider<T>`), external values via
|
`constantProvider(value)` (a hand-written `ConstantProvider<T>`), external values via
|
||||||
`providers.gradleProperty()` / `providers.environmentVariable()` etc. **Never store
|
`providers.gradleProperty()` / `providers.environmentVariable()` etc. **Never store
|
||||||
`ProviderFactory` anywhere in the DSL object graph** — it is not config-cache serializable.
|
`ProviderFactory` anywhere in the DSL object graph** - it is not config-cache serializable.
|
||||||
- `forceRegen` (`konfig.force` property) is evaluated eagerly at configuration time as a plain
|
- `forceRegen` (`konfig.force` property) is evaluated eagerly at configuration time as a plain
|
||||||
`Boolean` via `providers.gradleProperty("konfig.force").isPresent` — not inside a provider
|
`Boolean` via `providers.gradleProperty("konfig.force").isPresent` - not inside a provider
|
||||||
lambda — so the value is captured by value and is config-cache safe.
|
lambda - so the value is captured by value and is config-cache safe.
|
||||||
|
|
||||||
**Dimension data in task inputs** uses flat-map encoding (`"<dimName>|<fieldName>"` as map keys)
|
**Dimension data in task inputs** uses flat-map encoding (`"<dimName>|<fieldName>"` as map keys)
|
||||||
in `MapProperty<String, String>` rather than a managed-type `ListProperty`. Values are
|
in `MapProperty<String, String>` rather than a managed-type `ListProperty`. Values are
|
||||||
@@ -74,14 +74,14 @@ per-type maps down to one per scope and avoids Gradle's `@Nested` managed-type r
|
|||||||
### DSL design
|
### DSL design
|
||||||
|
|
||||||
- **`@KonfigDsl` / `@DslMarker`** is applied to all DSL scope classes to prevent accidental scope leakage.
|
- **`@KonfigDsl` / `@DslMarker`** is applied to all DSL scope classes to prevent accidental scope leakage.
|
||||||
- **`ConstantProvider<T>`** wraps literal values — no `ProviderFactory` anywhere in the DSL object graph.
|
- **`ConstantProvider<T>`** wraps literal values - no `ProviderFactory` anywhere in the DSL object graph.
|
||||||
- **`field()` at the top level** returns `FieldHandle<T>` which exposes `.debug(value)` and
|
- **`field()` at the top level** returns `FieldHandle<T>` which exposes `.debug(value)` and
|
||||||
`.release(value)`, both returning `Unit` — chaining beyond the first call is intentionally impossible.
|
`.release(value)`, both returning `Unit` - chaining beyond the first call is intentionally impossible.
|
||||||
- **`debug {}` / `release {}` scope blocks** use `BuildTypedFieldDeclScope` as receiver —
|
- **`debug {}` / `release {}` scope blocks** use `BuildTypedFieldDeclScope` as receiver -
|
||||||
`field()` inside these returns `Unit`, since the build type is already fixed by the enclosing scope.
|
`field()` inside these returns `Unit`, since the build type is already fixed by the enclosing scope.
|
||||||
- **`common {}` block in `DimensionConfig`** — shared fallback fields for all variants; merged in
|
- **`common {}` block in `DimensionConfig`** - shared fallback fields for all variants; merged in
|
||||||
plugin with variant fields taking precedence.
|
plugin with variant fields taking precedence.
|
||||||
- **Plain `var` properties on `KonfigExtension`** — `objectPackage`, `objectName`,
|
- **Plain `var` properties on `KonfigExtension`** - `objectPackage`, `objectName`,
|
||||||
`objectVisibility` are user-facing `var` properties backed by internal `Property<T>`
|
`objectVisibility` are user-facing `var` properties backed by internal `Property<T>`
|
||||||
(`objectPackageProp`, `objectNameProp`, `objectVisibilityProp`) used for lazy task wiring.
|
(`objectPackageProp`, `objectNameProp`, `objectVisibilityProp`) used for lazy task wiring.
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ per-type maps down to one per scope and avoids Gradle's `@Nested` managed-type r
|
|||||||
2. `konfig.properties` file in the project directory: `konfig.dimension.<name>=<variant>`
|
2. `konfig.properties` file in the project directory: `konfig.dimension.<name>=<variant>`
|
||||||
3. Task-name detection (variant name substring match, if not disabled by `konfig.android.flavordetection=false`)
|
3. Task-name detection (variant name substring match, if not disabled by `konfig.android.flavordetection=false`)
|
||||||
4. `defaultTo` fallback declared in DSL
|
4. `defaultTo` fallback declared in DSL
|
||||||
5. **Omitted silently** if none of the above — no crash, dimension object not generated
|
5. **Omitted silently** if none of the above - no crash, dimension object not generated
|
||||||
|
|
||||||
`resolveWithSource()` in `KonfigPlugin` returns a tab-separated `"<TAG>\t<variant>\t<reason>"`
|
`resolveWithSource()` in `KonfigPlugin` returns a tab-separated `"<TAG>\t<variant>\t<reason>"`
|
||||||
string for every dimension. This is stored as a task input (`dimensionResolutionLog`) so the task
|
string for every dimension. This is stored as a task input (`dimensionResolutionLog`) so the task
|
||||||
@@ -103,7 +103,7 @@ action can emit structured lifecycle/warning/error log messages without re-runni
|
|||||||
|---------------------------------------------|---------------------------------------------------------------------------------|
|
|---------------------------------------------|---------------------------------------------------------------------------------|
|
||||||
| `-Pkonfig.buildtype=DEBUG\|RELEASE` | Forces build type; falls back to task-name detection then RELEASE |
|
| `-Pkonfig.buildtype=DEBUG\|RELEASE` | Forces build type; falls back to task-name detection then RELEASE |
|
||||||
| `-Pkonfig.dimension.<name>=<variant>` | Selects a dimension variant explicitly |
|
| `-Pkonfig.dimension.<name>=<variant>` | Selects a dimension variant explicitly |
|
||||||
| `-Pkonfig.force` | Disables UP-TO-DATE checks — task always re-runs (any value or bare flag works) |
|
| `-Pkonfig.force` | Disables UP-TO-DATE checks - task always re-runs (any value or bare flag works) |
|
||||||
| `-Pkonfig.android.buildtypedetection=false` | Disables task-name build-type detection |
|
| `-Pkonfig.android.buildtypedetection=false` | Disables task-name build-type detection |
|
||||||
| `-Pkonfig.android.flavordetection=false` | Disables task-name dimension variant detection |
|
| `-Pkonfig.android.flavordetection=false` | Disables task-name dimension variant detection |
|
||||||
|
|
||||||
@@ -111,12 +111,12 @@ action can emit structured lifecycle/warning/error log messages without re-runni
|
|||||||
|
|
||||||
| File | Role |
|
| File | Role |
|
||||||
|-------------------------------|----------------------------------------------------------------------------------------------------------------|
|
|-------------------------------|----------------------------------------------------------------------------------------------------------------|
|
||||||
| `KonfigPlugin.kt` | Entry point — wires providers, registers task, auto-wires source sets, hooks compile tasks |
|
| `KonfigPlugin.kt` | Entry point - wires providers, registers task, auto-wires source sets, hooks compile tasks |
|
||||||
| `KonfigExtension.kt` | DSL (`konfig { }`) — top-level `field()`, `debug {}`, `release {}`, and `dimension()` functions |
|
| `KonfigExtension.kt` | DSL (`konfig { }`) - top-level `field()`, `debug {}`, `release {}`, and `dimension()` functions |
|
||||||
| `DimensionConfig.kt` | DSL node for a dimension — holds variants, `common {}` block, `objectNameOverride`, `defaultVariant` |
|
| `DimensionConfig.kt` | DSL node for a dimension - holds variants, `common {}` block, `objectNameOverride`, `defaultVariant` |
|
||||||
| `VariantConfig.kt` | DSL node for a variant or common block — `field()` returns `FieldHandle<T>`; `debug {}`/`release {}` supported |
|
| `VariantConfig.kt` | DSL node for a variant or common block - `field()` returns `FieldHandle<T>`; `debug {}`/`release {}` supported |
|
||||||
| `FieldConfig.kt` | Single typed field — holds default `Provider<T>?` and per-`BuildType` overrides; `resolve()` returns `Provider<T>?` |
|
| `FieldConfig.kt` | Single typed field - holds default `Provider<T>?` and per-`BuildType` overrides; `resolve()` returns `Provider<T>?` |
|
||||||
| `GenerateKonfigTask.kt` | `@CacheableTask` — validates inputs, logs detection results, writes the `.kt` file |
|
| `GenerateKonfigTask.kt` | `@CacheableTask` - validates inputs, logs detection results, writes the `.kt` file |
|
||||||
| `BuildType.kt` | `enum` with regex-based task-name detection |
|
| `BuildType.kt` | `enum` with regex-based task-name detection |
|
||||||
| `Visibility.kt` | `PUBLIC` / `INTERNAL` enum |
|
| `Visibility.kt` | `PUBLIC` / `INTERNAL` enum |
|
||||||
| `KonfigDsl.kt` | `@DslMarker` annotation applied to all DSL scope classes |
|
| `KonfigDsl.kt` | `@DslMarker` annotation applied to all DSL scope classes |
|
||||||
@@ -132,8 +132,8 @@ action can emit structured lifecycle/warning/error log messages without re-runni
|
|||||||
- **Repositories:** `https://maven.bitsycore.com/releases` (primary, no auth) and
|
- **Repositories:** `https://maven.bitsycore.com/releases` (primary, no auth) and
|
||||||
`https://maven.pkg.github.com/bitsycore/bitsykonfig-gradle-plugin` (fallback, needs PAT)
|
`https://maven.pkg.github.com/bitsycore/bitsykonfig-gradle-plugin` (fallback, needs PAT)
|
||||||
- **JVM target:** 17 (set via `sourceCompatibility` + `KotlinCompile.compilerOptions.jvmTarget`,
|
- **JVM target:** 17 (set via `sourceCompatibility` + `KotlinCompile.compilerOptions.jvmTarget`,
|
||||||
no toolchain — avoids requiring a specific JDK installation)
|
no toolchain - avoids requiring a specific JDK installation)
|
||||||
- **AGP dependency:** `compileOnly("com.android.tools.build:gradle:8.7.3")` — never leaked to consumers
|
- **AGP dependency:** `compileOnly("com.android.tools.build:gradle:8.7.3")` - never leaked to consumers
|
||||||
|
|
||||||
### Publishing
|
### Publishing
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ Two Maven publications are created automatically by the `kotlin-dsl` + `gradlePl
|
|||||||
| `pluginMaven` | `konfig-gradle-plugin` | Implementation jar + sources + POM |
|
| `pluginMaven` | `konfig-gradle-plugin` | Implementation jar + sources + POM |
|
||||||
| `konfigPluginMarkerMaven` | `com.bitsycore.konfig.gradle.plugin` | Marker POM that points to the impl |
|
| `konfigPluginMarkerMaven` | `com.bitsycore.konfig.gradle.plugin` | Marker POM that points to the impl |
|
||||||
|
|
||||||
**Never** use `publications { create<MavenPublication>("pluginMaven") { ... } }` — this replaces
|
**Never** use `publications { create<MavenPublication>("pluginMaven") { ... } }` - this replaces
|
||||||
the auto-wired publication and breaks the marker. Always configure existing publications via
|
the auto-wired publication and breaks the marker. Always configure existing publications via
|
||||||
`publications.withType<MavenPublication>().configureEach { ... }`.
|
`publications.withType<MavenPublication>().configureEach { ... }`.
|
||||||
|
|
||||||
@@ -175,9 +175,9 @@ Fast, no Gradle processes. Cover DSL model classes and type resolution in isolat
|
|||||||
|
|
||||||
| File | What it covers |
|
| File | What it covers |
|
||||||
|-------------------------------|------------------------------------------------------------------------|
|
|-------------------------------|------------------------------------------------------------------------|
|
||||||
| `BuildTypeExtendedTest.kt` | `BuildType.resolve()` — all regex edge cases, task names, enum values |
|
| `BuildTypeExtendedTest.kt` | `BuildType.resolve()` - all regex edge cases, task names, enum values |
|
||||||
| `VisibilityTest.kt` | `Visibility` enum entries, ordinals, valueOf |
|
| `VisibilityTest.kt` | `Visibility` enum entries, ordinals, valueOf |
|
||||||
| `ConstantProviderTest.kt` | `constantProvider()` / `ConstantProvider` — all Provider API methods |
|
| `ConstantProviderTest.kt` | `constantProvider()` / `ConstantProvider` - all Provider API methods |
|
||||||
| `FieldConfigTest.kt` | `FieldConfig` construction, default resolution, build-type overrides |
|
| `FieldConfigTest.kt` | `FieldConfig` construction, default resolution, build-type overrides |
|
||||||
| `VariantConfigTest.kt` | `VariantConfig` field declarations, duplicate guard, scope blocks |
|
| `VariantConfigTest.kt` | `VariantConfig` field declarations, duplicate guard, scope blocks |
|
||||||
| `DimensionConfigTest.kt` | `DimensionConfig` objectName derivation, variants, common block |
|
| `DimensionConfigTest.kt` | `DimensionConfig` objectName derivation, variants, common block |
|
||||||
@@ -186,7 +186,7 @@ Fast, no Gradle processes. Cover DSL model classes and type resolution in isolat
|
|||||||
|
|
||||||
### Functional tests (`src/functionalTest/`)
|
### Functional tests (`src/functionalTest/`)
|
||||||
|
|
||||||
Full Gradle TestKit builds — each test spins up a real Gradle project in a temp directory.
|
Full Gradle TestKit builds - each test spins up a real Gradle project in a temp directory.
|
||||||
`FunctionalTestBase` provides shared helpers (`withProject`, `withFailingProject`, `generatedFile()`, `writeBuildGradle()`).
|
`FunctionalTestBase` provides shared helpers (`withProject`, `withFailingProject`, `generatedFile()`, `writeBuildGradle()`).
|
||||||
|
|
||||||
| File | What it covers |
|
| File | What it covers |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# buildkonfig-gradle-plugin
|
# buildkonfig-gradle-plugin
|
||||||
|
|
||||||
A Gradle plugin that generates a `BuildKonfig` Kotlin object at build time — like Android's `BuildConfig`, but for any Kotlin project (JVM, Multiplatform, Android).
|
A Gradle plugin that generates a `BuildKonfig` Kotlin object at build time - like Android's `BuildConfig`, but for any Kotlin project (JVM, Multiplatform, Android).
|
||||||
|
|
||||||
Fields can be constant, overridden per build type (debug/release), or scoped to named **dimensions** (e.g. environment, region) with their own variants.
|
Fields can be constant, overridden per build type (debug/release), or scoped to named **dimensions** (e.g. environment, region) with their own variants.
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ pluginManagement {
|
|||||||
<details>
|
<details>
|
||||||
<summary>GitHub Packages fallback (authenticated)</summary>
|
<summary>GitHub Packages fallback (authenticated)</summary>
|
||||||
|
|
||||||
Store credentials in `~/.gradle/gradle.properties` — never commit them:
|
Store credentials in `~/.gradle/gradle.properties` - never commit them:
|
||||||
|
|
||||||
```properties
|
```properties
|
||||||
gpr.user=YOUR_GITHUB_USERNAME
|
gpr.user=YOUR_GITHUB_USERNAME
|
||||||
@@ -146,16 +146,16 @@ Three equivalent forms:
|
|||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
konfig {
|
konfig {
|
||||||
// Fluent handle — default + one or both overrides
|
// Fluent handle - default + one or both overrides
|
||||||
field("BASE_URL", "https://prod.example.com").debug("https://dev.example.com")
|
field("BASE_URL", "https://prod.example.com").debug("https://dev.example.com")
|
||||||
|
|
||||||
// Scope blocks — build type is fixed, field() returns Unit, no chaining
|
// Scope blocks - build type is fixed, field() returns Unit, no chaining
|
||||||
debug { field("MOCK_API", true) }
|
debug { field("MOCK_API", true) }
|
||||||
release { field("MOCK_API", false) }
|
release { field("MOCK_API", false) }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> `field()` inside `debug {}` / `release {}` blocks intentionally returns `Unit` — the build type is already fixed by the enclosing scope, so `.debug()` / `.release()` chaining is impossible by design.
|
> `field()` inside `debug {}` / `release {}` blocks intentionally returns `Unit` - the build type is already fixed by the enclosing scope, so `.debug()` / `.release()` chaining is impossible by design.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -239,7 +239,7 @@ public object BuildKonfig {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Root-level name collisions **fail the build** — a flat field may not shadow a
|
Root-level name collisions **fail the build** - a flat field may not shadow a
|
||||||
built-in constant (`BUILD_TYPE`, `MODULE_NAME`, `IS_DEBUG`), a global field, or
|
built-in constant (`BUILD_TYPE`, `MODULE_NAME`, `IS_DEBUG`), a global field, or
|
||||||
a field from another flat dimension.
|
a field from another flat dimension.
|
||||||
|
|
||||||
@@ -255,7 +255,7 @@ Variants are resolved in priority order:
|
|||||||
| 2 | `konfig.properties` file | `konfig.dimension.env=dev` |
|
| 2 | `konfig.properties` file | `konfig.dimension.env=dev` |
|
||||||
| 3 | Task-name detection | Running `assembleDevDebug` matches `dev` |
|
| 3 | Task-name detection | Running `assembleDevDebug` matches `dev` |
|
||||||
| 4 | `defaultTo` in DSL | `dimension("env", defaultTo = "prod")` |
|
| 4 | `defaultTo` in DSL | `dimension("env", defaultTo = "prod")` |
|
||||||
| — | Omitted silently | No variant → no nested object generated |
|
| - | Omitted silently | No variant → no nested object generated |
|
||||||
|
|
||||||
### `konfig.properties` file
|
### `konfig.properties` file
|
||||||
|
|
||||||
@@ -265,11 +265,11 @@ Place a `konfig.properties` file in your project directory:
|
|||||||
konfig.dimension.env=dev
|
konfig.dimension.env=dev
|
||||||
```
|
```
|
||||||
|
|
||||||
This file is tracked as a task input — changing it invalidates the build cache.
|
This file is tracked as a task input - changing it invalidates the build cache.
|
||||||
|
|
||||||
### Task-name matching rules
|
### Task-name matching rules
|
||||||
|
|
||||||
Variant detection respects **camelCase word boundaries** — a variant only
|
Variant detection respects **camelCase word boundaries** - a variant only
|
||||||
matches a whole segment of the task name, never a plain substring:
|
matches a whole segment of the task name, never a plain substring:
|
||||||
|
|
||||||
- `assemblePreprodRelease` matches variant `preprod`, **not** `prod`
|
- `assemblePreprodRelease` matches variant `preprod`, **not** `prod`
|
||||||
@@ -283,7 +283,7 @@ Genuinely ambiguous matches are skipped with a warning.
|
|||||||
### Selection logging
|
### Selection logging
|
||||||
|
|
||||||
The resolved build type and every dimension decision are printed by the
|
The resolved build type and every dimension decision are printed by the
|
||||||
`konfigInfo` task on **every** build — including fully cached / UP-TO-DATE
|
`konfigInfo` task on **every** build - including fully cached / UP-TO-DATE
|
||||||
builds with the configuration cache enabled:
|
builds with the configuration cache enabled:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -301,7 +301,7 @@ Build type is resolved in priority order:
|
|||||||
|----------|---------------------|-----------------------------------|
|
|----------|---------------------|-----------------------------------|
|
||||||
| 1 | Explicit property | `-Pkonfig.buildtype=DEBUG` |
|
| 1 | Explicit property | `-Pkonfig.buildtype=DEBUG` |
|
||||||
| 2 | Task-name detection | Running `assembleDebug` → `DEBUG` |
|
| 2 | Task-name detection | Running `assembleDebug` → `DEBUG` |
|
||||||
| — | Default | `RELEASE` |
|
| - | Default | `RELEASE` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -311,7 +311,7 @@ Build type is resolved in priority order:
|
|||||||
|---------------------------------------------|--------------------------------------------------|
|
|---------------------------------------------|--------------------------------------------------|
|
||||||
| `-Pkonfig.buildtype=DEBUG\|RELEASE` | Forces build type |
|
| `-Pkonfig.buildtype=DEBUG\|RELEASE` | Forces build type |
|
||||||
| `-Pkonfig.dimension.<name>=<variant>` | Selects a dimension variant |
|
| `-Pkonfig.dimension.<name>=<variant>` | Selects a dimension variant |
|
||||||
| `-Pkonfig.force` | Disables UP-TO-DATE checks — task always re-runs |
|
| `-Pkonfig.force` | Disables UP-TO-DATE checks - task always re-runs |
|
||||||
| `-Pkonfig.android.buildtypedetection=false` | Disables task-name build-type detection |
|
| `-Pkonfig.android.buildtypedetection=false` | Disables task-name build-type detection |
|
||||||
| `-Pkonfig.android.flavordetection=false` | Disables task-name dimension-variant detection |
|
| `-Pkonfig.android.flavordetection=false` | Disables task-name dimension-variant detection |
|
||||||
|
|
||||||
@@ -324,7 +324,7 @@ Forces `generateKonfig` to re-run on every build, bypassing Gradle's UP-TO-DATE
|
|||||||
./gradlew assembleRelease -Pkonfig.force
|
./gradlew assembleRelease -Pkonfig.force
|
||||||
```
|
```
|
||||||
|
|
||||||
The flag is presence-based — any value (or no value) enables it.
|
The flag is presence-based - any value (or no value) enables it.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -344,7 +344,7 @@ println(BuildKonfig.Env.VARIANT) // "dev" or "prod"
|
|||||||
## Build-script queries (`konfig.isDebug`, `konfig.getCurrentDimension`)
|
## Build-script queries (`konfig.isDebug`, `konfig.getCurrentDimension`)
|
||||||
|
|
||||||
The same recognition logic that drives generation is queryable from build
|
The same recognition logic that drives generation is queryable from build
|
||||||
scripts — useful for wiring per-build-type dependencies in KMP projects:
|
scripts - useful for wiring per-build-type dependencies in KMP projects:
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
konfig {
|
konfig {
|
||||||
@@ -371,7 +371,7 @@ val activeEnv: String? = konfig.getCurrentDimension("env") // "prod", or null
|
|||||||
|
|
||||||
## Using Gradle providers as field values
|
## Using Gradle providers as field values
|
||||||
|
|
||||||
Lazy `Provider<T>` values are supported — useful for reading Gradle properties or environment variables:
|
Lazy `Provider<T>` values are supported - useful for reading Gradle properties or environment variables:
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
konfig {
|
konfig {
|
||||||
@@ -380,7 +380,7 @@ konfig {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> Do not call `System.getenv()` or `project.findProperty()` directly inside `field()` — these bypass the Provider API and break configuration cache.
|
> Do not call `System.getenv()` or `project.findProperty()` directly inside `field()` - these bypass the Provider API and break configuration cache.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -427,7 +427,7 @@ The `generateKonfig` task is automatically wired as a dependency of all `compile
|
|||||||
# Run unit tests only
|
# Run unit tests only
|
||||||
./gradlew test
|
./gradlew test
|
||||||
|
|
||||||
# Run functional tests (Gradle TestKit — starts real Gradle builds)
|
# Run functional tests (Gradle TestKit - starts real Gradle builds)
|
||||||
./gradlew functionalTest
|
./gradlew functionalTest
|
||||||
|
|
||||||
# Run a specific functional test
|
# Run a specific functional test
|
||||||
|
|||||||
+1
-1
@@ -75,7 +75,7 @@ publishing {
|
|||||||
// - "pluginMaven" → the real implementation jar (groupId:artifactId:version)
|
// - "pluginMaven" → the real implementation jar (groupId:artifactId:version)
|
||||||
// - "konfigPluginMarkerMaven" → the plugin marker (pluginId:pluginId.gradle.plugin:version)
|
// - "konfigPluginMarkerMaven" → the plugin marker (pluginId:pluginId.gradle.plugin:version)
|
||||||
//
|
//
|
||||||
// We must NOT create a third "pluginMaven" manually — that breaks the marker.
|
// We must NOT create a third "pluginMaven" manually - that breaks the marker.
|
||||||
// Instead we configure the existing ones via withType.
|
// Instead we configure the existing ones via withType.
|
||||||
publications.withType<MavenPublication>().configureEach {
|
publications.withType<MavenPublication>().configureEach {
|
||||||
// Only decorate the implementation publication, not the marker
|
// Only decorate the implementation publication, not the marker
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import kotlin.test.assertTrue
|
|||||||
/**
|
/**
|
||||||
* Functional tests for the `common {}` block inside a dimension.
|
* Functional tests for the `common {}` block inside a dimension.
|
||||||
*
|
*
|
||||||
* The common block provides fallback fields for all variants — a variant field
|
* The common block provides fallback fields for all variants - a variant field
|
||||||
* with the same name must take precedence over the common field.
|
* with the same name must take precedence over the common field.
|
||||||
*/
|
*/
|
||||||
class CommonBlockFunctionalTest : FunctionalTestBase() {
|
class CommonBlockFunctionalTest : FunctionalTestBase() {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class DuplicateDetectionFunctionalTest : FunctionalTestBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `same field name in different variants is allowed`() = withProject { dir, run ->
|
@Test fun `same field name in different variants is allowed`() = withProject { dir, run ->
|
||||||
// Different variants may each define the same field name — that is the whole point
|
// Different variants may each define the same field name - that is the whole point
|
||||||
dir.writeBuildGradle("""
|
dir.writeBuildGradle("""
|
||||||
plugins { id("com.bitsycore.konfig") }
|
plugins { id("com.bitsycore.konfig") }
|
||||||
group = "com.example"
|
group = "com.example"
|
||||||
@@ -106,7 +106,7 @@ class DuplicateDetectionFunctionalTest : FunctionalTestBase() {
|
|||||||
assertTrue(result.output.contains("generateKonfig"))
|
assertTrue(result.output.contains("generateKonfig"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Duplicate dimension name — error message ──────────────────────────────
|
// ── Duplicate dimension name - error message ──────────────────────────────
|
||||||
|
|
||||||
@Test fun `duplicate dimension error message contains dimension name`() = withFailingProject { dir, run ->
|
@Test fun `duplicate dimension error message contains dimension name`() = withFailingProject { dir, run ->
|
||||||
dir.writeBuildGradle("""
|
dir.writeBuildGradle("""
|
||||||
@@ -121,7 +121,7 @@ class DuplicateDetectionFunctionalTest : FunctionalTestBase() {
|
|||||||
assertTrue(result.output.contains("my-dim"))
|
assertTrue(result.output.contains("my-dim"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Duplicate global field — error message ────────────────────────────────
|
// ── Duplicate global field - error message ────────────────────────────────
|
||||||
|
|
||||||
@Test fun `duplicate global field error message contains field name`() = withFailingProject { dir, run ->
|
@Test fun `duplicate global field error message contains field name`() = withFailingProject { dir, run ->
|
||||||
dir.writeBuildGradle("""
|
dir.writeBuildGradle("""
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import kotlin.test.assertTrue
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Functional tests for every supported field type emitted by the plugin:
|
* Functional tests for every supported field type emitted by the plugin:
|
||||||
* String, Boolean, Int, Long, Float, Double — including correct Kotlin
|
* String, Boolean, Int, Long, Float, Double - including correct Kotlin
|
||||||
* literal syntax (suffixes, special Float/Double values, escaping).
|
* literal syntax (suffixes, special Float/Double values, escaping).
|
||||||
*/
|
*/
|
||||||
class FieldTypesFunctionalTest : FunctionalTestBase() {
|
class FieldTypesFunctionalTest : FunctionalTestBase() {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import kotlin.test.assertFalse
|
|||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Functional tests for `flatDimension` — fields generated at the root of the
|
* Functional tests for `flatDimension` - fields generated at the root of the
|
||||||
* konfig object, with hard failure on root-level name collisions.
|
* konfig object, with hard failure on root-level name collisions.
|
||||||
*/
|
*/
|
||||||
class FlatDimensionFunctionalTest : FunctionalTestBase() {
|
class FlatDimensionFunctionalTest : FunctionalTestBase() {
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import kotlin.io.path.createTempDirectory
|
|||||||
* Base class for all functional tests.
|
* Base class for all functional tests.
|
||||||
*
|
*
|
||||||
* Provides:
|
* Provides:
|
||||||
* - [withProject] — creates a temp Gradle project, runs it, then cleans up.
|
* - [withProject] - creates a temp Gradle project, runs it, then cleans up.
|
||||||
* - [File.generatedFile] — walks the output dir to find the generated `.kt` file.
|
* - [File.generatedFile] - walks the output dir to find the generated `.kt` file.
|
||||||
* - [File.writeBuildGradle] — shorthand for writing a `build.gradle.kts`.
|
* - [File.writeBuildGradle] - shorthand for writing a `build.gradle.kts`.
|
||||||
*/
|
*/
|
||||||
abstract class FunctionalTestBase {
|
abstract class FunctionalTestBase {
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ abstract class FunctionalTestBase {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as [withProject] but Gradle is invoked with `buildAndFail()` so a build
|
* Same as [withProject] but Gradle is invoked with `buildAndFail()` so a build
|
||||||
* failure does NOT throw — the returned [BuildResult] carries the failed output.
|
* failure does NOT throw - the returned [BuildResult] carries the failed output.
|
||||||
*/
|
*/
|
||||||
protected fun withFailingProject(block: (projectDir: File, run: (List<String>) -> BuildResult) -> Unit) {
|
protected fun withFailingProject(block: (projectDir: File, run: (List<String>) -> BuildResult) -> Unit) {
|
||||||
val projectDir = createTempDirectory("konfig-ft-fail").toFile()
|
val projectDir = createTempDirectory("konfig-ft-fail").toFile()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import kotlin.test.assertTrue
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Functional tests for top-level (global) field declarations:
|
* Functional tests for top-level (global) field declarations:
|
||||||
* String, Boolean, Int — defaults, debug overrides, release overrides,
|
* String, Boolean, Int - defaults, debug overrides, release overrides,
|
||||||
* and the debug/release scope block syntax.
|
* and the debug/release scope block syntax.
|
||||||
*/
|
*/
|
||||||
class GlobalFieldsFunctionalTest : FunctionalTestBase() {
|
class GlobalFieldsFunctionalTest : FunctionalTestBase() {
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ abstract class GenerateKonfigTask : DefaultTask() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** `"my-env"` → `"MY_ENV_VARIANT"` — root constant holding the active variant of a flat dimension. */
|
/** `"my-env"` → `"MY_ENV_VARIANT"` - root constant holding the active variant of a flat dimension. */
|
||||||
private fun String.toVariantConstName(): String =
|
private fun String.toVariantConstName(): String =
|
||||||
uppercase().replace(Regex("[^A-Z0-9]"), "_") + "_VARIANT"
|
uppercase().replace(Regex("[^A-Z0-9]"), "_") + "_VARIANT"
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ abstract class GenerateKonfigTask : DefaultTask() {
|
|||||||
"Long" -> """${indent}const val $name: Long = ${raw}L"""
|
"Long" -> """${indent}const val $name: Long = ${raw}L"""
|
||||||
"Float" -> """${indent}const val $name: Float = ${raw.toFloat().toKotlinFloat()}"""
|
"Float" -> """${indent}const val $name: Float = ${raw.toFloat().toKotlinFloat()}"""
|
||||||
"Double" -> """${indent}const val $name: Double = ${raw.toDouble().toKotlinDouble()}"""
|
"Double" -> """${indent}const val $name: Double = ${raw.toDouble().toKotlinDouble()}"""
|
||||||
else -> return@forEach // unknown type — skip
|
else -> return@forEach // unknown type - skip
|
||||||
}
|
}
|
||||||
appendLine(line)
|
appendLine(line)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ abstract class KonfigExtension @Inject constructor(
|
|||||||
get() = objectVisibilityProp.get()
|
get() = objectVisibilityProp.get()
|
||||||
set(value) = objectVisibilityProp.set(value)
|
set(value) = objectVisibilityProp.set(value)
|
||||||
|
|
||||||
/** Output directory — kept as [DirectoryProperty] for full Gradle lazy semantics. */
|
/** Output directory - kept as [DirectoryProperty] for full Gradle lazy semantics. */
|
||||||
val outputDir: DirectoryProperty = objects.directoryProperty()
|
val outputDir: DirectoryProperty = objects.directoryProperty()
|
||||||
|
|
||||||
// ==============================================================================
|
// ==============================================================================
|
||||||
@@ -55,7 +55,7 @@ abstract class KonfigExtension @Inject constructor(
|
|||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal val dimensions: MutableList<DimensionConfig> = mutableListOf()
|
internal val dimensions: MutableList<DimensionConfig> = mutableListOf()
|
||||||
|
|
||||||
/** Backing store for global fields — reuses [com.bitsycore.konfig.configs.VariantConfig] for its field/debug/release logic. */
|
/** Backing store for global fields - reuses [com.bitsycore.konfig.configs.VariantConfig] for its field/debug/release logic. */
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal val globalScope: VariantConfig = VariantConfig("\$global")
|
internal val globalScope: VariantConfig = VariantConfig("\$global")
|
||||||
|
|
||||||
@@ -117,10 +117,10 @@ abstract class KonfigExtension @Inject constructor(
|
|||||||
// MARK: Build-script queries
|
// MARK: Build-script queries
|
||||||
// ==============================================================================
|
// ==============================================================================
|
||||||
|
|
||||||
/** Wired by the plugin at apply time — same resolution chain as the generated object. */
|
/** Wired by the plugin at apply time - same resolution chain as the generated object. */
|
||||||
internal lateinit var buildTypeProviderInternal: Provider<BuildType>
|
internal lateinit var buildTypeProviderInternal: Provider<BuildType>
|
||||||
|
|
||||||
/** Wired by the plugin at apply time — resolves a dimension with the same recognition logic. */
|
/** Wired by the plugin at apply time - resolves a dimension with the same recognition logic. */
|
||||||
internal lateinit var dimensionResolverInternal: (String) -> Provider<String>
|
internal lateinit var dimensionResolverInternal: (String) -> Provider<String>
|
||||||
|
|
||||||
/** Resolved build type as a lazy provider (`"debug"` / `"release"`). */
|
/** Resolved build type as a lazy provider (`"debug"` / `"release"`). */
|
||||||
@@ -132,7 +132,7 @@ abstract class KonfigExtension @Inject constructor(
|
|||||||
get() = buildTypeProviderInternal.map { it == BuildType.DEBUG }
|
get() = buildTypeProviderInternal.map { it == BuildType.DEBUG }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True when the resolved build type is debug — uses the exact same recognition
|
* True when the resolved build type is debug - uses the exact same recognition
|
||||||
* as the generated object, so it can drive build-script decisions such as
|
* as the generated object, so it can drive build-script decisions such as
|
||||||
* KMP `debugImplementation`-style wiring:
|
* KMP `debugImplementation`-style wiring:
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import org.gradle.work.DisableCachingByDefault
|
|||||||
* [GenerateKonfigTask] only logs when it actually executes, so a cached /
|
* [GenerateKonfigTask] only logs when it actually executes, so a cached /
|
||||||
* up-to-date build would silently hide which variant is active. This task is
|
* up-to-date build would silently hide which variant is active. This task is
|
||||||
* untracked (never up-to-date, never cached) and `generateKonfig` depends on
|
* untracked (never up-to-date, never cached) and `generateKonfig` depends on
|
||||||
* it, guaranteeing the selection is always visible in the logs — even on a
|
* it, guaranteeing the selection is always visible in the logs - even on a
|
||||||
* fully cached build with the configuration cache enabled.
|
* fully cached build with the configuration cache enabled.
|
||||||
*/
|
*/
|
||||||
@DisableCachingByDefault(because = "pure logging task, must run on every build")
|
@DisableCachingByDefault(because = "pure logging task, must run on every build")
|
||||||
|
|||||||
@@ -293,11 +293,11 @@ class KonfigPlugin : Plugin<Project> {
|
|||||||
* Resolves a dimension and encodes the result as a tab-separated string for logging.
|
* Resolves a dimension and encodes the result as a tab-separated string for logging.
|
||||||
*
|
*
|
||||||
* Format: `"<TAG>\t<variant>\t<reason>"`
|
* Format: `"<TAG>\t<variant>\t<reason>"`
|
||||||
* - TAG = `OK` — active, variant resolved successfully
|
* - TAG = `OK` - active, variant resolved successfully
|
||||||
* - TAG = `WARN_UNKNOWN` — property set but value is not a known variant
|
* - TAG = `WARN_UNKNOWN` - property set but value is not a known variant
|
||||||
* - TAG = `WARN_AMBIGUOUS` — multiple variants matched task names
|
* - TAG = `WARN_AMBIGUOUS` - multiple variants matched task names
|
||||||
* - TAG = `SKIP` — no active variant could be determined
|
* - TAG = `SKIP` - no active variant could be determined
|
||||||
* - TAG = `ERROR` — configuration error (e.g. invalid defaultTo)
|
* - TAG = `ERROR` - configuration error (e.g. invalid defaultTo)
|
||||||
*/
|
*/
|
||||||
private fun resolveWithSource(
|
private fun resolveWithSource(
|
||||||
dim: DimensionConfig,
|
dim: DimensionConfig,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import java.util.function.BiFunction
|
|||||||
*
|
*
|
||||||
* Values are stored as [Provider]<T> so lazy sources (e.g. Gradle property providers)
|
* Values are stored as [Provider]<T> so lazy sources (e.g. Gradle property providers)
|
||||||
* are supported alongside plain constants. [org.gradle.api.provider.ProviderFactory]
|
* are supported alongside plain constants. [org.gradle.api.provider.ProviderFactory]
|
||||||
* is intentionally NOT stored here — it is not configuration-cache serializable and
|
* is intentionally NOT stored here - it is not configuration-cache serializable and
|
||||||
* must never flow into the object graph captured by task input providers.
|
* must never flow into the object graph captured by task input providers.
|
||||||
*/
|
*/
|
||||||
class FieldConfig<T : Any> @PublishedApi internal constructor(
|
class FieldConfig<T : Any> @PublishedApi internal constructor(
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import org.gradle.api.provider.Provider
|
|||||||
* field("TIMEOUT", 30).debug(5)
|
* field("TIMEOUT", 30).debug(5)
|
||||||
* field("URL", "https://prod.example.com").debug("https://dev.example.com").release("https://prod.example.com")
|
* field("URL", "https://prod.example.com").debug("https://dev.example.com").release("https://prod.example.com")
|
||||||
* ```
|
* ```
|
||||||
* Not available inside `debug {}` / `release {}` blocks — those return [Unit].
|
* Not available inside `debug {}` / `release {}` blocks - those return [Unit].
|
||||||
*/
|
*/
|
||||||
class FieldHandle<T : Any> @PublishedApi internal constructor(
|
class FieldHandle<T : Any> @PublishedApi internal constructor(
|
||||||
@PublishedApi internal val field: FieldConfig<T>
|
@PublishedApi internal val field: FieldConfig<T>
|
||||||
@@ -34,7 +34,7 @@ class FieldHandle<T : Any> @PublishedApi internal constructor(
|
|||||||
/**
|
/**
|
||||||
* Receiver of `debug { ... }` and `release { ... }` blocks inside [VariantConfig].
|
* Receiver of `debug { ... }` and `release { ... }` blocks inside [VariantConfig].
|
||||||
*
|
*
|
||||||
* `field()` here returns [Unit] — no `.debug()`/`.release()` chaining is possible
|
* `field()` here returns [Unit] - no `.debug()`/`.release()` chaining is possible
|
||||||
* because the build type is already fixed by the enclosing scope.
|
* because the build type is already fixed by the enclosing scope.
|
||||||
*/
|
*/
|
||||||
@KonfigDsl
|
@KonfigDsl
|
||||||
|
|||||||
@@ -128,20 +128,20 @@ class BuildTypeExtendedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `prereleased does not resolve RELEASE`() {
|
@Test fun `prereleased does not resolve RELEASE`() {
|
||||||
// "release" is preceded by lowercase 'e' in "prere[lease]" — but "release"
|
// "release" is preceded by lowercase 'e' in "prere[lease]" - but "release"
|
||||||
// starts after "prere", let's verify actual behaviour via the regex:
|
// starts after "prere", let's verify actual behaviour via the regex:
|
||||||
// lookbehind (?<![a-z]) fails because char before 'r' of "release" is 'e'
|
// lookbehind (?<![a-z]) fails because char before 'r' of "release" is 'e'
|
||||||
assertNull(BuildType.resolve("prereleased"))
|
assertNull(BuildType.resolve("prereleased"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `released does not resolve RELEASE`() {
|
@Test fun `released does not resolve RELEASE`() {
|
||||||
// "released" — 'd' after "release" is NOT a lowercase letter... wait,
|
// "released" - 'd' after "release" is NOT a lowercase letter... wait,
|
||||||
// actually the regex checks (?![a-z]) so 'd' fails the lookahead.
|
// actually the regex checks (?![a-z]) so 'd' fails the lookahead.
|
||||||
assertNull(BuildType.resolve("released"))
|
assertNull(BuildType.resolve("released"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `debugMode resolves DEBUG`() {
|
@Test fun `debugMode resolves DEBUG`() {
|
||||||
// 'M' after "debug" — uppercase, not [a-z], so lookahead passes
|
// 'M' after "debug" - uppercase, not [a-z], so lookahead passes
|
||||||
assertEquals(BuildType.DEBUG, BuildType.resolve("debugMode"))
|
assertEquals(BuildType.DEBUG, BuildType.resolve("debugMode"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ class DimensionConfigTest {
|
|||||||
d.variant("prod") { field("URL", "https://prod.example.com") }
|
d.variant("prod") { field("URL", "https://prod.example.com") }
|
||||||
d.variant("prod") { field("KEY", "secret") }
|
d.variant("prod") { field("KEY", "secret") }
|
||||||
val v = d.variants["prod"]!!
|
val v = d.variants["prod"]!!
|
||||||
// Same VariantConfig instance reused — two fields total
|
// Same VariantConfig instance reused - two fields total
|
||||||
assertEquals(2, v.fields.size)
|
assertEquals(2, v.fields.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ class FieldHandleTest {
|
|||||||
@Test fun `handle with no debug or release set returns null for scope-only field`() {
|
@Test fun `handle with no debug or release set returns null for scope-only field`() {
|
||||||
val fc = FieldConfig<String>("F", String::class.java, null)
|
val fc = FieldConfig<String>("F", String::class.java, null)
|
||||||
@Suppress("UNUSED_VARIABLE") val handle = FieldHandle(fc)
|
@Suppress("UNUSED_VARIABLE") val handle = FieldHandle(fc)
|
||||||
// No overrides set — resolve returns null
|
// No overrides set - resolve returns null
|
||||||
assertNull(fc.resolve(BuildType.DEBUG))
|
assertNull(fc.resolve(BuildType.DEBUG))
|
||||||
assertNull(fc.resolve(BuildType.RELEASE))
|
assertNull(fc.resolve(BuildType.RELEASE))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class TaskNameMatchingTest {
|
|||||||
|
|
||||||
@Test fun `camelCase variant matches its camelCase segment`() {
|
@Test fun `camelCase variant matches its camelCase segment`() {
|
||||||
assertTrue("assemblePreProdRelease".containsWordCamelCase("preProd"))
|
assertTrue("assemblePreProdRelease".containsWordCamelCase("preProd"))
|
||||||
// "Prod" is a legitimate camelCase segment inside PreProd — the resolver's
|
// "Prod" is a legitimate camelCase segment inside PreProd - the resolver's
|
||||||
// longest-match rule (tested functionally) disambiguates this case.
|
// longest-match rule (tested functionally) disambiguates this case.
|
||||||
assertTrue("assemblePreProdRelease".containsWordCamelCase("prod"))
|
assertTrue("assemblePreProdRelease".containsWordCamelCase("prod"))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user