Skip to content

Commit 87f2762

Browse files
authored
Ignore non regressive changes (#104)
* Add an option to ignore stable changes * Add an option to allow missing baseline
1 parent a88b257 commit 87f2762

6 files changed

Lines changed: 430 additions & 28 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,12 @@ composeStabilityAnalyzer {
787787

788788
// Control build failure behavior on stability changes (default: true)
789789
failOnStabilityChange.set(true)
790+
791+
// Do not report any stable changes from the baseline (default: false)
792+
ignoreNonRegressiveChanges.set(false)
793+
794+
// Allow the check to run, even if the baseline does not exist (default: false)
795+
allowMissingBaseline.set(false)
790796
}
791797
}
792798
```

stability-gradle/api/stability-gradle.api

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ public final class com/skydoves/compose/stability/gradle/StabilityAnalyzerGradle
2323
public abstract class com/skydoves/compose/stability/gradle/StabilityCheckTask : org/gradle/api/DefaultTask {
2424
public fun <init> ()V
2525
public final fun check ()V
26+
public abstract fun getAllowMissingBaseline ()Lorg/gradle/api/provider/Property;
2627
public abstract fun getFailOnStabilityChange ()Lorg/gradle/api/provider/Property;
28+
public abstract fun getIgnoreNonRegressiveChanges ()Lorg/gradle/api/provider/Property;
2729
public abstract fun getIgnoredClasses ()Lorg/gradle/api/provider/ListProperty;
2830
public abstract fun getIgnoredPackages ()Lorg/gradle/api/provider/ListProperty;
2931
public abstract fun getProjectName ()Lorg/gradle/api/provider/Property;
@@ -46,8 +48,10 @@ public abstract class com/skydoves/compose/stability/gradle/StabilityDumpTask :
4648

4749
public abstract class com/skydoves/compose/stability/gradle/StabilityValidationConfig {
4850
public fun <init> (Lorg/gradle/api/file/ProjectLayout;Lorg/gradle/api/model/ObjectFactory;)V
51+
public final fun getAllowMissingBaseline ()Lorg/gradle/api/provider/Property;
4952
public final fun getEnabled ()Lorg/gradle/api/provider/Property;
5053
public final fun getFailOnStabilityChange ()Lorg/gradle/api/provider/Property;
54+
public final fun getIgnoreNonRegressiveChanges ()Lorg/gradle/api/provider/Property;
5155
public final fun getIgnoredClasses ()Lorg/gradle/api/provider/ListProperty;
5256
public final fun getIgnoredPackages ()Lorg/gradle/api/provider/ListProperty;
5357
public final fun getIgnoredProjects ()Lorg/gradle/api/provider/ListProperty;

stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerExtension.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,24 @@ public abstract class StabilityValidationConfig @Inject constructor(
160160
*/
161161
public val quietCheck: Property<Boolean> =
162162
objects.property(Boolean::class.javaObjectType).convention(false)
163+
164+
/**
165+
* Whether to ignore any stable changes from the baseline.
166+
*
167+
* When set to true, any new stable parameters, stable functions or removed parameters/functions
168+
* will not be reported.
169+
*
170+
* Default: false
171+
*/
172+
public val ignoreNonRegressiveChanges: Property<Boolean> =
173+
objects.property(Boolean::class.javaObjectType).convention(false)
174+
175+
/**
176+
* When true, plugin will still run normally even with the baseline missing, treating it
177+
* as an empty baseline
178+
*
179+
* Default: false
180+
*/
181+
public val allowMissingBaseline: Property<Boolean> =
182+
objects.property(Boolean::class.javaObjectType).convention(false)
163183
}

stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ public class StabilityAnalyzerGradlePlugin : KotlinCompilerPluginSupportPlugin {
112112
ignoredClasses.set(extension.stabilityValidation.ignoredClasses)
113113
failOnStabilityChange.set(extension.stabilityValidation.failOnStabilityChange)
114114
quietCheck.set(extension.stabilityValidation.quietCheck)
115+
ignoreNonRegressiveChanges.set(extension.stabilityValidation.ignoreNonRegressiveChanges)
116+
allowMissingBaseline.set(extension.stabilityValidation.allowMissingBaseline)
115117
}
116118

117119
// Make check task depend on stabilityCheck if enabled (only if check task exists)
@@ -176,6 +178,8 @@ public class StabilityAnalyzerGradlePlugin : KotlinCompilerPluginSupportPlugin {
176178
failOnStabilityChange.set(extension.stabilityValidation.failOnStabilityChange)
177179
quietCheck.set(extension.stabilityValidation.quietCheck)
178180
stabilityFileSuffix.set(variant.name)
181+
ignoreNonRegressiveChanges.set(extension.stabilityValidation.ignoreNonRegressiveChanges)
182+
allowMissingBaseline.set(extension.stabilityValidation.allowMissingBaseline)
179183
}
180184

181185
aggregateDumpTask.configure {

stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityCheckTask.kt

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ public abstract class StabilityCheckTask : DefaultTask() {
8181
@get:Optional
8282
public abstract val stabilityFileSuffix: Property<String>
8383

84+
/**
85+
* Whether to only report regressive changes
86+
*/
87+
@get:Input
88+
public abstract val ignoreNonRegressiveChanges: Property<Boolean>
89+
90+
@get:Input
91+
public abstract val allowMissingBaseline: Property<Boolean>
92+
8493
init {
8594
group = "verification"
8695
description = "Check composable stability against reference file"
@@ -99,7 +108,7 @@ public abstract class StabilityCheckTask : DefaultTask() {
99108
}
100109

101110
val stabilityReferenceFiles = stabilityReferenceFiles.asFileTree.files
102-
if (stabilityReferenceFiles.isEmpty()) {
111+
if (!allowMissingBaseline.get() && stabilityReferenceFiles.isEmpty()) {
103112
// Directory doesn't exist - no baseline has been created yet
104113
// This is expected for new modules or before the first stabilityDump
105114
logger.lifecycle(
@@ -120,7 +129,7 @@ public abstract class StabilityCheckTask : DefaultTask() {
120129
val referenceFile = stabilityReferenceFiles.firstOrNull {
121130
it.endsWith("$stabilityFileName.stability")
122131
}
123-
if (referenceFile?.exists() != true) {
132+
if (!allowMissingBaseline.get() && referenceFile?.exists() != true) {
124133
// Directory exists but file doesn't - unusual but handle gracefully
125134
logger.lifecycle(
126135
"ℹ️ No stability baseline found for :$stabilityFileName, skipping stability check",
@@ -133,7 +142,8 @@ public abstract class StabilityCheckTask : DefaultTask() {
133142

134143
val currentStability = parseStabilityFromCompiler(inputFile)
135144
val referenceStability = parseStabilityFile(referenceFile)
136-
val differences = compareStability(currentStability, referenceStability)
145+
val differences =
146+
compareStability(currentStability, referenceStability, ignoreNonRegressiveChanges.get())
137147

138148
if (differences.isNotEmpty()) {
139149
val message = buildString {
@@ -324,7 +334,11 @@ public abstract class StabilityCheckTask : DefaultTask() {
324334
.replace("\\t", "\t")
325335
}
326336

327-
private fun parseStabilityFile(file: java.io.File): Map<String, StabilityEntry> {
337+
private fun parseStabilityFile(file: java.io.File?): Map<String, StabilityEntry> {
338+
if (file?.exists() != true) {
339+
return emptyMap()
340+
}
341+
328342
val entries = mutableMapOf<String, StabilityEntry>()
329343

330344
var currentQualifiedName: String? = null
@@ -455,22 +469,34 @@ public abstract class StabilityCheckTask : DefaultTask() {
455469
private fun compareStability(
456470
current: Map<String, StabilityEntry>,
457471
reference: Map<String, StabilityEntry>,
472+
ignoreNonRegressiveChanges: Boolean = false,
473+
458474
): List<StabilityDifference> {
459475
val differences = mutableListOf<StabilityDifference>()
460476

477+
// Check for new functions
461478
current.keys.subtract(reference.keys).forEach { functionName ->
462-
differences.add(StabilityDifference.NewFunction(functionName))
479+
if (!ignoreNonRegressiveChanges || !current.getValue(functionName).skippable) {
480+
differences.add(StabilityDifference.NewFunction(functionName))
481+
}
463482
}
464483

465-
reference.keys.subtract(current.keys).forEach { functionName ->
466-
differences.add(StabilityDifference.RemovedFunction(functionName))
484+
// Check for removed functions
485+
if (!ignoreNonRegressiveChanges) {
486+
reference.keys.subtract(current.keys).forEach { functionName ->
487+
differences.add(StabilityDifference.RemovedFunction(functionName))
488+
}
467489
}
468490

491+
// Check for changed stability
469492
current.keys.intersect(reference.keys).forEach { functionName ->
470493
val currentEntry = current[functionName]!!
471494
val referenceEntry = reference[functionName]!!
472495

473-
if (currentEntry.skippable != referenceEntry.skippable) {
496+
// Check skippability change
497+
if (currentEntry.skippable != referenceEntry.skippable &&
498+
(!ignoreNonRegressiveChanges || !currentEntry.skippable)
499+
) {
474500
differences.add(
475501
StabilityDifference.SkippabilityChanged(
476502
functionName,
@@ -482,17 +508,24 @@ public abstract class StabilityCheckTask : DefaultTask() {
482508

483509
// Check if parameter count changed
484510
if (currentEntry.parameters.size != referenceEntry.parameters.size) {
485-
differences.add(
486-
StabilityDifference.ParameterCountChanged(
487-
functionName,
488-
referenceEntry.parameters.size,
489-
currentEntry.parameters.size,
490-
),
491-
)
511+
if (
512+
!ignoreNonRegressiveChanges ||
513+
currentEntry.parameters.any { it.stability != "STABLE" }
514+
) {
515+
differences.add(
516+
StabilityDifference.ParameterCountChanged(
517+
functionName,
518+
referenceEntry.parameters.size,
519+
currentEntry.parameters.size,
520+
),
521+
)
522+
}
492523
} else {
493524
// Check parameter stability changes (only if count is the same)
494525
currentEntry.parameters.zip(referenceEntry.parameters).forEach { (current, ref) ->
495-
if (current.stability != ref.stability) {
526+
if (current.stability != ref.stability &&
527+
(!ignoreNonRegressiveChanges || current.stability != "STABLE")
528+
) {
496529
differences.add(
497530
StabilityDifference.ParameterStabilityChanged(
498531
functionName,

0 commit comments

Comments
 (0)