Skip to content

feat: Add KMP Product Flavors support#131

Closed
therajanmaurya wants to merge 2 commits intodevfrom
feature/kmp-product-flavors-integration-v2
Closed

feat: Add KMP Product Flavors support#131
therajanmaurya wants to merge 2 commits intodevfrom
feature/kmp-product-flavors-integration-v2

Conversation

@therajanmaurya
Copy link
Member

@therajanmaurya therajanmaurya commented Feb 25, 2026

Summary

Integrate kmp-product-flavors (v1.0.0) for cross-platform flavor support that aligns with existing Android application flavors.

What's Changed

Convention Plugin Integration

  • KMPLibraryConventionPlugin - Now applies org.convention.kmp.flavors automatically
  • KMPCoreBaseLibraryConventionPlugin - Now applies org.convention.kmp.flavors automatically
  • All KMP library modules automatically get cross-platform flavor support (demo/prod)

New Files

  • KMPFlavorsConventionPlugin.kt - Convention plugin wrapping kmp-product-flavors
  • org/convention/KmpFlavors.kt - Centralized flavor configuration

Version Catalog

  • Added kmpProductFlavors = "1.0.0" version
  • Added kmpProductFlavors-gradlePlugin library
  • Added kmp-flavors-convention plugin alias

How It Works

Since KMP flavors is applied in KMPLibraryConventionPlugin and KMPCoreBaseLibraryConventionPlugin, ALL existing KMP modules automatically get:

  1. Cross-platform BuildConfig - Access flavor info from shared code:

    object FlavorConfig {
        const val VARIANT_NAME: String = "demo"
        const val IS_DEMO: Boolean = true
        const val IS_PROD: Boolean = false
        const val BASE_URL: String = "https://demo-api.mifos.org"
        const val ANALYTICS_ENABLED: Boolean = false
    }
  2. Flavor-specific source sets - commonDemo, commonProd, iosDemo, etc.

  3. Aligned with Android - Same demo/prod flavors as Android application

Build Commands

# Build with demo flavor (default)
./gradlew build

# Build with prod flavor
./gradlew build -PkmpFlavor=prod

# List all variants
./gradlew listFlavors

# Initialize source directories
./gradlew kmpFlavorInit

Test Plan

  • Build succeeds with ./gradlew build
  • Build succeeds with ./gradlew build -PkmpFlavor=prod
  • ./gradlew listFlavors shows demo/prod variants
  • BuildConfig is generated in shared modules

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced Kotlin Multiplatform Product Flavors enabling multiple distinct build configurations across platforms.
  • Build Configuration

    • Added DEMO and PROD flavor variants with independent configuration properties and build settings.
    • Integrated centralized flavor management system providing support for flavor dimensions and customizable build parameters across different product variants.

therajanmaurya and others added 2 commits February 25, 2026 17:12
Integrate kmp-product-flavors (v1.0.0) for cross-platform flavor support that aligns with existing Android application flavors.

## Changes
- Add kmp-product-flavors v1.0.0 to version catalog
- Create KMPFlavorsConventionPlugin for easy integration
- Add KmpFlavors.kt with centralized flavor configuration
- Configure demo/prod flavors matching Android application
- Add mavenLocal() for local development support

## Features
- Cross-platform flavor support (Android, iOS, Desktop, Web)
- BuildConfig generation with flavor-specific constants
- Automatic source set wiring for all platforms
- Aligns with existing Android contentType dimension (demo/prod)

## Usage
Apply to any KMP module:
```kotlin
plugins {
    id("org.convention.kmp.flavors")
}
```

Build with specific flavor:
```bash
./gradlew build -PkmpFlavor=prod
```

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Apply kmp.flavors in KMPLibraryConventionPlugin (all KMP modules get flavors)
- Apply kmp.flavors in KMPCoreBaseLibraryConventionPlugin
- Add kmp-flavors-convention to version catalog for direct usage
- Remove mavenLocal (not needed for production - plugin on Maven Central)

All KMP library modules now automatically get cross-platform flavor support
(demo/prod) that aligns with Android application flavors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

This change introduces Kotlin Multiplatform (KMP) Product Flavors support by registering a new convention plugin, creating a centralized flavor configuration module, and integrating it into existing KMP library plugins to enable cross-platform flavor dimensions with DEMO and PROD variants.

Changes

Cohort / File(s) Summary
Gradle Plugin Registration & Wiring
build-logic/convention/build.gradle.kts
Added dependency on KMP product flavors Gradle plugin and registered new "kmpFlavors" convention plugin with id "org.convention.kmp.flavors".
Convention Plugin Integration
build-logic/convention/src/main/kotlin/KMPCoreBaseLibraryConventionPlugin.kt, build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt
Applied the new KMP flavors convention plugin to both base library and library convention plugins for cross-platform flavor configuration.
KMP Flavors Convention Plugin
build-logic/convention/src/main/kotlin/KMPFlavorsConventionPlugin.kt
Implemented new Gradle convention plugin that applies KMP flavor support, configures dimensions/flavors, generates BuildConfig, and infers package names.
Flavor Configuration Module
build-logic/convention/src/main/kotlin/org/convention/KmpFlavors.kt
Introduced centralized configuration object defining flavor dimensions and enums (CONTENT\_TYPE with DEMO/PROD variants), utility functions for flavor querying, data models for configuration, and a primary configurator for applying flavors with BuildConfig field generation.
Version & Library Declarations
gradle/libs.versions.toml
Added KMP product flavors version ("1.0.0"), registered external Gradle plugin dependency from mobilebytelabs, and declared convention plugin entry.

Sequence Diagram

sequenceDiagram
    participant Gradle as Gradle Build<br/>Initialization
    participant KMPLib as KMPLibraryConvention<br/>Plugin
    participant KMPFlav as KMPFlavorsConvention<br/>Plugin
    participant KmpConfig as KmpFlavors<br/>Configuration
    participant Project as Project

    Gradle->>KMPLib: Apply plugin
    KMPLib->>KMPFlav: Apply org.convention.kmp.flavors
    KMPFlav->>KMPFlav: Instantiate plugin
    KMPFlav->>KmpConfig: Call configureKmpFlavors()
    KmpConfig->>KmpConfig: Extract defaultDimensions
    KmpConfig->>KmpConfig: Extract defaultFlavors (DEMO, PROD)
    KmpConfig->>KmpConfig: Add standard BuildConfig fields<br/>(BASE_URL, ANALYTICS_ENABLED)
    KmpConfig->>Project: Configure flavor extension<br/>with dimensions & flavors
    Project-->>Gradle: Flavors configured & BuildConfig ready
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A fluffy convention takes its place,
With DEMO flavors and PROD's grace!
BuildConfig fields hop through the code,
Cross-platform paths now lighter load.
Dimensions dance, configurations bloom—
Kotlin's multiverse has more room! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Add KMP Product Flavors support' directly summarizes the main change: integrating KMP Product Flavors to enable cross-platform flavor support, which aligns with the detailed objectives and all modified files.
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 docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/kmp-product-flavors-integration-v2

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.

@therajanmaurya
Copy link
Member Author

Version Update Required

Please update the kmp-product-flavors version to 1.0.1 before merging:

File: gradle/libs.versions.toml

- kmpProductFlavors = "1.0.0"
+ kmpProductFlavors = "1.0.1"

v1.0.1 Fixes

  • Fixed "Invalid Dependency on Default Compilation Source Set" warnings
  • Platform/intermediate flavor source sets no longer incorrectly depend on compilation defaults
  • Improved documentation

The v1.0.1 release is now publishing to Maven Central and Gradle Plugin Portal.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (5)
gradle/libs.versions.toml (1)

57-57: Version entry placed under the wrong section.

kmpProductFlavors is listed under the "Static Analysis & Code Formatting" section, but it's a build/flavor plugin, not a static analysis or formatting tool. Consider moving it to a more appropriate section (e.g., near "KMP & CMP" entries or a new dedicated section) for maintainability.

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

In `@gradle/libs.versions.toml` at line 57, The version entry "kmpProductFlavors"
is placed under the "Static Analysis & Code Formatting" section but belongs with
build/flavor tooling; move the kmpProductFlavors = "1.0.0" line out of that
section and place it near the "KMP & CMP" entries (or create a new "KMP / Build
Flavors" section) so it groups logically with other KMP/build plugin versions
and retains alphabetical/organizational ordering.
build-logic/convention/src/main/kotlin/org/convention/KmpFlavors.kt (3)

139-174: Potential shadowing: flavors parameter vs flavors extension method.

On line 160, this.flavors { ... } calls the extension's flavors configuration block, while the parameter flavors: List<FlavorConfigData> on line 142 is also named flavors. Inside the lambda on line 161, flavors resolves to the parameter due to scoping, which happens to be correct here. However, this is fragile and could confuse future maintainers.

Rename parameter to avoid ambiguity
 fun configureKmpFlavors(
     extension: KmpFlavorExtension,
     dimensions: List<DimensionConfig>,
-    flavors: List<FlavorConfigData>,
+    flavorConfigs: List<FlavorConfigData>,
     generateBuildConfig: Boolean = true,
     buildConfigPackage: String? = null,
 ) {
     extension.apply {
         ...
         this.flavors {
-            flavors.forEach { flavorData ->
+            flavorConfigs.forEach { flavorData ->
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@build-logic/convention/src/main/kotlin/org/convention/KmpFlavors.kt` around
lines 139 - 174, Rename the function parameter flavors: List<FlavorConfigData>
to a non-shadowing name (e.g., flavorConfigs) in configureKmpFlavors so it
doesn’t conflict with the KmpFlavorExtension.flavors { ... } configuration
block; update all uses inside the function (change flavors.forEach { ... } to
flavorConfigs.forEach { ... } and any other references to the parameter) so the
call to this.flavors { register(...) } clearly refers to the extension method
and the loop iterates the renamed parameter.

94-97: Hardcoded API base URLs in build-logic.

getBaseUrl hardcodes https://demo-api.mifos.org and https://api.mifos.org. These are fine for this specific template, but they'll be baked into the generated BuildConfig for every module that applies the flavors plugin. If this template is forked, users may overlook updating these values.

Consider adding a comment or documenting that downstream projects should customize these URLs.

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

In `@build-logic/convention/src/main/kotlin/org/convention/KmpFlavors.kt` around
lines 94 - 97, The getBaseUrl(flavor: Flavor) function currently returns
hardcoded URLs for Flavor.DEMO and Flavor.PROD; add a brief TODO comment above
getBaseUrl calling out that these values are template defaults that must be
customized by downstream projects (or provide instructions to override), and
change implementation to prefer configurable values (e.g., read from project
properties or environment) with the existing hardcoded URLs as fallbacks;
reference the getBaseUrl function and the Flavor.DEMO / Flavor.PROD branches so
reviewers can locate and verify the TODO and the new config-first fallback
behavior.

64-69: Replace Enum.values() with Enum.entries for Kotlin 1.9+ compatibility.

Dimension.values() (line 64), Flavor.values() (line 75), and Flavor.values() again (line 183) use the synthetic values() method. Since Kotlin 1.9, entries is the recommended modern replacement, offering better performance and consistency across platforms. This project targets Kotlin 2.2.21.

Replace values() with entries
     val defaultDimensions: List<DimensionConfig>
-        get() = Dimension.values().map { dimension ->
+        get() = Dimension.entries.map { dimension ->
     val defaultFlavors: List<FlavorConfigData>
-        get() = Flavor.values().map { flavor ->
+        get() = Flavor.entries.map { flavor ->
-    val flavor = KmpFlavors.Flavor.values().find { it.flavorName == flavorData.name }
+    val flavor = KmpFlavors.Flavor.entries.find { it.flavorName == flavorData.name }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@build-logic/convention/src/main/kotlin/org/convention/KmpFlavors.kt` around
lines 64 - 69, Replace all uses of the synthetic Enum.values() with the Kotlin
1.9+ entries property: change Dimension.values() to Dimension.entries and both
Flavor.values() occurrences to Flavor.entries. Locate the mapping logic that
builds DimensionConfig (where you currently call Dimension.values()) and the
places that iterate over Flavor (including the mapping creating flavor configs
and the later occurrence around line 183) and swap .values() for .entries so the
code iterates over the enum entries collection compatible with Kotlin 2.2.21.
build-logic/convention/build.gradle.kts (1)

35-36: implementation scope is correct for class-based plugin application, but consider compileOnly for consistency.

Other Gradle plugin dependencies in this block use compileOnly (e.g., libs.android.gradlePlugin, libs.kotlin.gradlePlugin). This one uses implementation because KMPFlavorsConventionPlugin applies KmpFlavorPlugin by class reference (pluginManager.apply(KmpFlavorPlugin::class.java)), which requires the class to be on the runtime classpath.

If you'd prefer consistency, you could switch to applying the plugin by its string ID instead (which would allow compileOnly), assuming the plugin ID is declared in the consuming project's classpath. However, the current approach is functionally correct.

Alternative: apply by plugin ID with compileOnly

In build.gradle.kts:

-    implementation(libs.kmpProductFlavors.gradlePlugin)
+    compileOnly(libs.kmpProductFlavors.gradlePlugin)

In KMPFlavorsConventionPlugin.kt:

-            pluginManager.apply(KmpFlavorPlugin::class.java)
+            pluginManager.apply("io.github.mobilebytelabs.kmpflavors") // or whatever the plugin ID is

This would require consuming modules to have the plugin on their classpath (e.g., via settings.gradle.kts plugin management).

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

In `@build-logic/convention/build.gradle.kts` around lines 35 - 36, The current
dependency uses implementation(libs.kmpProductFlavors.gradlePlugin) because
KMPFlavorsConventionPlugin applies the plugin by class reference via
pluginManager.apply(KmpFlavorPlugin::class.java), which requires the plugin on
the runtime classpath; to make this consistent with other plugin deps use
compileOnly you have two options: either keep the class-based application and
leave implementation as-is, or change KMPFlavorsConventionPlugin to apply the
plugin by its string ID (pluginManager.apply("com.example.kmpflavor") /
apply(plugin = "com.example.kmpflavor") pattern) so you can switch the
dependency to compileOnly(libs.kmpProductFlavors.gradlePlugin); update the code
around KMPFlavorsConventionPlugin and the plugin application call
(KmpFlavorPlugin::class.java) accordingly and ensure the plugin ID is provided
on the consuming projects' classpath (pluginManagement).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@build-logic/convention/src/main/kotlin/KMPFlavorsConventionPlugin.kt`:
- Around line 49-67: inferBuildConfigPackage currently builds package segments
from project.name and project.path without sanitizing invalid Java/Kotlin
identifiers and uses a hardcoded "org.mifos" prefix; update
inferBuildConfigPackage to (1) read a configurable base package first (e.g.,
from a project property like "buildConfigPackageBase" or fallback to
project.group if set), (2) sanitize every segment derived from project.name and
pathParts by replacing non-alphanumeric/underscore characters with underscores,
lowercasing, and if a segment starts with a digit prefixing it with an
underscore so it becomes a valid identifier, and (3) join the sanitized segments
with dots and prepend the configurable base (instead of the hardcoded
"org.mifos") before returning the package string; refer to
inferBuildConfigPackage, project.name, pathParts, and the "org.mifos" prefix
when locating places to change.

In `@build-logic/convention/src/main/kotlin/org/convention/KmpFlavors.kt`:
- Line 66: The dimension name transform in KmpFlavors (the expression that
builds the flavor dimension name from dimension.name) lowercases and strips
underscores producing "contenttype" which mismatches Android's
FlavorDimension.contentType (camelCase); update the transformation used where
name = dimension.name.lowercase().replace("_", "") (and the analogous occurrence
later in the same file) to produce camelCase (e.g., convert tokens split by '_'
to lower-firstToken + capitalized subsequent tokens) so CONTENT_TYPE becomes
contentType and matches AppFlavor/FlavorDimension.

In `@gradle/libs.versions.toml`:
- Line 255: The dependency entry kmpProductFlavors-gradlePlugin = { group =
"io.github.mobilebytelabs.kmpflavors", name = "flavor-plugin", version.ref =
"kmpProductFlavors" } references an artifact that isn't on Maven Central; verify
the coordinates (group/name/version referenced by kmpProductFlavors) and either
correct them or add the repository that hosts the artifact to the Gradle
repositories (e.g., maven { url "..." } or a private repo with credentials) so
Gradle can resolve it; if this is an internal artifact ensure the appropriate
credentials and repository are configured in the project’s build/repository
configuration or replace version.ref with a published version available on the
configured repositories.

---

Nitpick comments:
In `@build-logic/convention/build.gradle.kts`:
- Around line 35-36: The current dependency uses
implementation(libs.kmpProductFlavors.gradlePlugin) because
KMPFlavorsConventionPlugin applies the plugin by class reference via
pluginManager.apply(KmpFlavorPlugin::class.java), which requires the plugin on
the runtime classpath; to make this consistent with other plugin deps use
compileOnly you have two options: either keep the class-based application and
leave implementation as-is, or change KMPFlavorsConventionPlugin to apply the
plugin by its string ID (pluginManager.apply("com.example.kmpflavor") /
apply(plugin = "com.example.kmpflavor") pattern) so you can switch the
dependency to compileOnly(libs.kmpProductFlavors.gradlePlugin); update the code
around KMPFlavorsConventionPlugin and the plugin application call
(KmpFlavorPlugin::class.java) accordingly and ensure the plugin ID is provided
on the consuming projects' classpath (pluginManagement).

In `@build-logic/convention/src/main/kotlin/org/convention/KmpFlavors.kt`:
- Around line 139-174: Rename the function parameter flavors:
List<FlavorConfigData> to a non-shadowing name (e.g., flavorConfigs) in
configureKmpFlavors so it doesn’t conflict with the KmpFlavorExtension.flavors {
... } configuration block; update all uses inside the function (change
flavors.forEach { ... } to flavorConfigs.forEach { ... } and any other
references to the parameter) so the call to this.flavors { register(...) }
clearly refers to the extension method and the loop iterates the renamed
parameter.
- Around line 94-97: The getBaseUrl(flavor: Flavor) function currently returns
hardcoded URLs for Flavor.DEMO and Flavor.PROD; add a brief TODO comment above
getBaseUrl calling out that these values are template defaults that must be
customized by downstream projects (or provide instructions to override), and
change implementation to prefer configurable values (e.g., read from project
properties or environment) with the existing hardcoded URLs as fallbacks;
reference the getBaseUrl function and the Flavor.DEMO / Flavor.PROD branches so
reviewers can locate and verify the TODO and the new config-first fallback
behavior.
- Around line 64-69: Replace all uses of the synthetic Enum.values() with the
Kotlin 1.9+ entries property: change Dimension.values() to Dimension.entries and
both Flavor.values() occurrences to Flavor.entries. Locate the mapping logic
that builds DimensionConfig (where you currently call Dimension.values()) and
the places that iterate over Flavor (including the mapping creating flavor
configs and the later occurrence around line 183) and swap .values() for
.entries so the code iterates over the enum entries collection compatible with
Kotlin 2.2.21.

In `@gradle/libs.versions.toml`:
- Line 57: The version entry "kmpProductFlavors" is placed under the "Static
Analysis & Code Formatting" section but belongs with build/flavor tooling; move
the kmpProductFlavors = "1.0.0" line out of that section and place it near the
"KMP & CMP" entries (or create a new "KMP / Build Flavors" section) so it groups
logically with other KMP/build plugin versions and retains
alphabetical/organizational ordering.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4a85c9 and 6c31d99.

📒 Files selected for processing (6)
  • build-logic/convention/build.gradle.kts
  • build-logic/convention/src/main/kotlin/KMPCoreBaseLibraryConventionPlugin.kt
  • build-logic/convention/src/main/kotlin/KMPFlavorsConventionPlugin.kt
  • build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt
  • build-logic/convention/src/main/kotlin/org/convention/KmpFlavors.kt
  • gradle/libs.versions.toml

Comment on lines +49 to +67
private fun inferBuildConfigPackage(project: Project): String {
// Try to use the project's group if available
val group = project.group.toString()
if (group.isNotEmpty() && group != "unspecified") {
return "$group.${project.name.replace("-", ".")}"
}

// Fall back to path-based package name
val pathParts = project.path
.removePrefix(":")
.split(":")
.filter { it.isNotEmpty() }

return if (pathParts.isNotEmpty()) {
"org.mifos.${pathParts.joinToString(".") { it.replace("-", ".") }}"
} else {
"org.mifos.${project.name.replace("-", ".")}"
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Package name inference may produce invalid Kotlin/Java packages.

A few concerns with inferBuildConfigPackage:

  1. Line 53: project.name.replace("-", ".") doesn't sanitize characters that are invalid in Java/Kotlin package identifiers (e.g., names starting with a digit, or containing other special characters). A module named 3d-renderer would produce a segment starting with 3d.

  2. Line 63: Same issue with pathParts.joinToString(".") — path segments could begin with digits or contain other invalid chars.

  3. Lines 63, 65: The "org.mifos." prefix is hardcoded. Since this is a project template intended for forking, downstream users will inherit org.mifos BuildConfig packages unless they set an explicit group. Consider deriving this from a configurable property or at minimum documenting that users should set project.group.

Suggested defensive sanitization
+    /**
+     * Sanitizes a string segment to be a valid Java/Kotlin package identifier.
+     */
+    private fun sanitizePackageSegment(segment: String): String =
+        segment.replace("-", ".")
+            .split(".")
+            .joinToString(".") { part ->
+                if (part.firstOrNull()?.isDigit() == true) "_$part" else part
+            }
+
     private fun inferBuildConfigPackage(project: Project): String {
         val group = project.group.toString()
         if (group.isNotEmpty() && group != "unspecified") {
-            return "$group.${project.name.replace("-", ".")}"
+            return "$group.${sanitizePackageSegment(project.name)}"
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@build-logic/convention/src/main/kotlin/KMPFlavorsConventionPlugin.kt` around
lines 49 - 67, inferBuildConfigPackage currently builds package segments from
project.name and project.path without sanitizing invalid Java/Kotlin identifiers
and uses a hardcoded "org.mifos" prefix; update inferBuildConfigPackage to (1)
read a configurable base package first (e.g., from a project property like
"buildConfigPackageBase" or fallback to project.group if set), (2) sanitize
every segment derived from project.name and pathParts by replacing
non-alphanumeric/underscore characters with underscores, lowercasing, and if a
segment starts with a digit prefixing it with an underscore so it becomes a
valid identifier, and (3) join the sanitized segments with dots and prepend the
configurable base (instead of the hardcoded "org.mifos") before returning the
package string; refer to inferBuildConfigPackage, project.name, pathParts, and
the "org.mifos" prefix when locating places to change.

val defaultDimensions: List<DimensionConfig>
get() = Dimension.values().map { dimension ->
DimensionConfig(
name = dimension.name.lowercase().replace("_", ""),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find and read the KmpFlavors.kt file
find . -name "KmpFlavors.kt" -type f

Repository: openMF/kmp-project-template

Length of output: 140


🏁 Script executed:

#!/bin/bash
# Read the KmpFlavors.kt file to see lines around 66 and 78
cat -n ./build-logic/convention/src/main/kotlin/org/convention/KmpFlavors.kt

Repository: openMF/kmp-project-template

Length of output: 7080


🏁 Script executed:

#!/bin/bash
# Search for contentType, contenttype, CONTENT_TYPE usage in the codebase
rg -i "contenttype|content_type" --type=kt -C 3

Repository: openMF/kmp-project-template

Length of output: 97


🏁 Script executed:

#!/bin/bash
# Search for contentType, contenttype, CONTENT_TYPE usage in the codebase
rg -i "contenttype|content_type" -C 3

Repository: openMF/kmp-project-template

Length of output: 5960


🏁 Script executed:

#!/bin/bash
# Let's look at the AppFlavor.kt file more closely to understand the dimension naming
cat -n ./build-logic/convention/src/main/kotlin/org/convention/AppFlavor.kt

Repository: openMF/kmp-project-template

Length of output: 2182


🏁 Script executed:

#!/bin/bash
# Check if there's any test or usage that validates the dimension names match
rg "contentType\|contenttype" --type-list | head -20
find . -name "*.gradle.kts" -o -name "build.gradle" | xargs grep -l "contentType\|contenttype\|kmp.*flavor" 2>/dev/null | head -10

Repository: openMF/kmp-project-template

Length of output: 519


🏁 Script executed:

#!/bin/bash
# Check the build.gradle.kts file for kmp-product-flavors configuration
cat -n ./build-logic/convention/build.gradle.kts | head -80

Repository: openMF/kmp-project-template

Length of output: 3032


🏁 Script executed:

#!/bin/bash
# Search for how kmp-product-flavors is actually used and if there's any configuration or test
find . -type f \( -name "*.kt" -o -name "*.kts" \) | xargs grep -l "kmpProductFlavors\|KmpFlavorExtension\|configureKmpFlavors" 2>/dev/null

Repository: openMF/kmp-project-template

Length of output: 253


🏁 Script executed:

#!/bin/bash
# Read the KMPFlavorsConventionPlugin.kt to see how it's being used
cat -n ./build-logic/convention/src/main/kotlin/KMPFlavorsConventionPlugin.kt

Repository: openMF/kmp-project-template

Length of output: 2902


🏁 Script executed:

#!/bin/bash
# Search for any test files that might validate dimension names
find . -type f -name "*Test*.kt" -o -name "*test*.kts" | xargs grep -l "contenttype\|contentType\|dimension" 2>/dev/null | head -5

Repository: openMF/kmp-project-template

Length of output: 53


🏁 Script executed:

#!/bin/bash
# Search for any gradle configuration files that might show how dimension names are used
find . -type f -name "*.gradle.kts" | xargs grep -A5 -B5 "dimension\|flavorDimension" 2>/dev/null | head -50

Repository: openMF/kmp-project-template

Length of output: 53


Dimension name transform produces "contenttype" instead of "contentType" — this mismatches Android configuration.

Android's FlavorDimension.contentType (line 33 of AppFlavor.kt) registers the dimension as "contentType" (camelCase). However, lines 66 and 78 of KmpFlavors.kt transform CONTENT_TYPE to "contenttype" (all lowercase), causing a mismatch.

The transform should convert CONTENT_TYPE to contentType to align with Android's expected dimension name:

Suggested fix for camelCase conversion
-                name = dimension.name.lowercase().replace("_", ""),
+                name = dimension.name.lowercase()
+                    .replace(Regex("_([a-z])")) { it.groupValues[1].uppercase() },

Apply the same to line 78.

📝 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
name = dimension.name.lowercase().replace("_", ""),
name = dimension.name.lowercase()
.replace(Regex("_([a-z])")) { it.groupValues[1].uppercase() },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@build-logic/convention/src/main/kotlin/org/convention/KmpFlavors.kt` at line
66, The dimension name transform in KmpFlavors (the expression that builds the
flavor dimension name from dimension.name) lowercases and strips underscores
producing "contenttype" which mismatches Android's FlavorDimension.contentType
(camelCase); update the transformation used where name =
dimension.name.lowercase().replace("_", "") (and the analogous occurrence later
in the same file) to produce camelCase (e.g., convert tokens split by '_' to
lower-firstToken + capitalized subsequent tokens) so CONTENT_TYPE becomes
contentType and matches AppFlavor/FlavorDimension.

kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }

ksp-gradlePlugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
kmpProductFlavors-gradlePlugin = { group = "io.github.mobilebytelabs.kmpflavors", name = "flavor-plugin", version.ref = "kmpProductFlavors" }
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the artifact exists on Maven Central
echo "Checking Maven Central for io.github.mobilebytelabs.kmpflavors:flavor-plugin:1.0.0"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://repo1.maven.org/maven2/io/github/mobilebytelabs/kmpflavors/flavor-plugin/1.0.0/flavor-plugin-1.0.0.pom")
echo "HTTP Response Code: $HTTP_CODE"

if [ "$HTTP_CODE" == "200" ]; then
    echo "✓ Artifact is available on Maven Central"
elif [ "$HTTP_CODE" == "404" ]; then
    echo "✗ Artifact NOT found on Maven Central (404)"
else
    echo "? Unexpected response code: $HTTP_CODE"
fi

Repository: openMF/kmp-project-template

Length of output: 221


The artifact io.github.mobilebytelabs.kmpflavors:flavor-plugin:1.0.0 is not available on Maven Central. The dependency cannot be resolved with this configuration and the build will fail.

Confirm the artifact group ID, name, and version are correct. If the artifact is published to an alternative repository, add that repository to the project's Gradle configuration. If this is a custom or private artifact, ensure the appropriate repository and credentials are configured.

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

In `@gradle/libs.versions.toml` at line 255, The dependency entry
kmpProductFlavors-gradlePlugin = { group =
"io.github.mobilebytelabs.kmpflavors", name = "flavor-plugin", version.ref =
"kmpProductFlavors" } references an artifact that isn't on Maven Central; verify
the coordinates (group/name/version referenced by kmpProductFlavors) and either
correct them or add the repository that hosts the artifact to the Gradle
repositories (e.g., maven { url "..." } or a private repo with credentials) so
Gradle can resolve it; if this is an internal artifact ensure the appropriate
credentials and repository are configured in the project’s build/repository
configuration or replace version.ref with a published version available on the
configured repositories.

@therajanmaurya
Copy link
Member Author

Superseded by #132 which uses kmp-product-flavors v1.0.1 with source set dependency fixes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant