diff --git a/CHANGELOG.md b/CHANGELOG.md index a68d78552..d30c5b097 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Changelog **Unreleased** -------------- +- Add optional `validate(Kotlin|Java)VersionMatches` tasks to keep files like `.java_version` synced with version catalogs. + 0.24.11 ------- diff --git a/platforms/gradle/foundry-gradle-plugin/best-practices-baseline.json b/platforms/gradle/foundry-gradle-plugin/best-practices-baseline.json index 97f38115d..d9de5d6c2 100644 --- a/platforms/gradle/foundry-gradle-plugin/best-practices-baseline.json +++ b/platforms/gradle/foundry-gradle-plugin/best-practices-baseline.json @@ -1 +1 @@ -{"issues":[{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/FoundryRootPlugin$configureRootProject$5","name":"accept","descriptor":"(Ljava/lang/Object;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/FoundryRootPlugin$configureRootProject$5","name":"accept","descriptor":"(Ljava/lang/String;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/testing/EmulatorWtfTests","name":"configureRootProject","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver$default","descriptor":"(Lfoundry/gradle/artifacts/Resolver$Companion;Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;ZILjava/lang/Object;)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver","name":"addSubprojectDependencies","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}},{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/FoundryBasePlugin","name":"apply","descriptor":"(Ljava/lang/Object;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/FoundryBasePlugin","name":"apply","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/topography/ModuleTopographyTasks","name":"configureRootProject","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver$default","descriptor":"(Lfoundry/gradle/artifacts/Resolver$Companion;Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;ZILjava/lang/Object;)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver","name":"addSubprojectDependencies","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}},{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/FoundryRootPlugin","name":"apply","descriptor":"(Ljava/lang/Object;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/FoundryRootPlugin","name":"apply","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/FoundryRootPlugin","name":"configureRootProject","descriptor":"(Lorg/gradle/api/Project;Lfoundry/gradle/FoundryProperties;Lorg/gradle/api/provider/Provider;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/tasks/AndroidTestApksTask$Companion","name":"register$foundry_gradle_plugin","descriptor":"(Lorg/gradle/api/Project;)Lorg/gradle/api/tasks/TaskProvider;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver$default","descriptor":"(Lfoundry/gradle/artifacts/Resolver$Companion;Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;ZILjava/lang/Object;)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver","name":"addSubprojectDependencies","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}},{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/FoundryRootPlugin$configureRootProject$4","name":"accept","descriptor":"(Ljava/lang/Object;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/FoundryRootPlugin$configureRootProject$4","name":"accept","descriptor":"(Ljava/lang/String;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/testing/RoborazziTests","name":"configureRootProject","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver$default","descriptor":"(Lfoundry/gradle/artifacts/Resolver$Companion;Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;ZILjava/lang/Object;)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver","name":"addSubprojectDependencies","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}},{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/stats/ModuleStatsTasks$configureRoot$1$1","name":"call","descriptor":"()Ljava/lang/Object;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/stats/ModuleStatsTasks$configureRoot$1$1","name":"call","descriptor":"()Ljava/util/Map;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}},{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/FoundryRootPlugin$configureRootProject$14","name":"execute","descriptor":"(Ljava/lang/Object;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/FoundryRootPlugin$configureRootProject$14","name":"execute","descriptor":"(Lorg/gradle/api/plugins/AppliedPlugin;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/dependencyrake/MissingIdentifiersAggregatorTask$Companion","name":"register","descriptor":"(Lorg/gradle/api/Project;)Lorg/gradle/api/tasks/TaskProvider;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver$default","descriptor":"(Lfoundry/gradle/artifacts/Resolver$Companion;Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;ZILjava/lang/Object;)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver","name":"addSubprojectDependencies","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}},{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver$default","descriptor":"(Lfoundry/gradle/artifacts/Resolver$Companion;Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver","name":"addSubprojectDependencies","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}}]} \ No newline at end of file +{"issues":[{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/FoundryBasePlugin","name":"apply","descriptor":"(Ljava/lang/Object;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/FoundryBasePlugin","name":"apply","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/topography/ModuleTopographyTasks","name":"configureRootProject","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver$default","descriptor":"(Lfoundry/gradle/artifacts/Resolver$Companion;Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;ZILjava/lang/Object;)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver","name":"addSubprojectDependencies","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}},{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/FoundryRootPlugin","name":"apply","descriptor":"(Ljava/lang/Object;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/FoundryRootPlugin","name":"apply","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/FoundryRootPlugin","name":"configureRootProject","descriptor":"(Lorg/gradle/api/Project;Lfoundry/gradle/FoundryProperties;Lorg/gradle/api/provider/Provider;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/tasks/AndroidTestApksTask$Companion","name":"register$foundry_gradle_plugin","descriptor":"(Lorg/gradle/api/Project;)Lorg/gradle/api/tasks/TaskProvider;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver$default","descriptor":"(Lfoundry/gradle/artifacts/Resolver$Companion;Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;ZILjava/lang/Object;)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver","name":"addSubprojectDependencies","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}},{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/stats/ModuleStatsTasks$configureRoot$1$1","name":"call","descriptor":"()Ljava/lang/Object;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/stats/ModuleStatsTasks$configureRoot$1$1","name":"call","descriptor":"()Ljava/util/Map;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}},{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/FoundryRootPlugin$configureRootProject$6","name":"accept","descriptor":"(Ljava/lang/Object;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/FoundryRootPlugin$configureRootProject$6","name":"accept","descriptor":"(Ljava/lang/String;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/testing/RoborazziTests","name":"configureRootProject","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver$default","descriptor":"(Lfoundry/gradle/artifacts/Resolver$Companion;Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;ZILjava/lang/Object;)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver","name":"addSubprojectDependencies","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}},{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/FoundryRootPlugin$configureRootProject$16","name":"execute","descriptor":"(Ljava/lang/Object;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/FoundryRootPlugin$configureRootProject$16","name":"execute","descriptor":"(Lorg/gradle/api/plugins/AppliedPlugin;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/dependencyrake/MissingIdentifiersAggregatorTask$Companion","name":"register","descriptor":"(Lorg/gradle/api/Project;)Lorg/gradle/api/tasks/TaskProvider;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver$default","descriptor":"(Lfoundry/gradle/artifacts/Resolver$Companion;Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;ZILjava/lang/Object;)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver","name":"addSubprojectDependencies","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}},{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver$default","descriptor":"(Lfoundry/gradle/artifacts/Resolver$Companion;Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver","name":"addSubprojectDependencies","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}},{"type":"get_subprojects","name":"getSubprojects","trace":{"trace":[{"owner":"foundry/gradle/FoundryRootPlugin$configureRootProject$7","name":"accept","descriptor":"(Ljava/lang/Object;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/FoundryRootPlugin$configureRootProject$7","name":"accept","descriptor":"(Ljava/lang/String;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/testing/EmulatorWtfTests","name":"configureRootProject","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver$default","descriptor":"(Lfoundry/gradle/artifacts/Resolver$Companion;Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;ZILjava/lang/Object;)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lfoundry/gradle/artifacts/FoundryArtifact;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver$Companion","name":"interProjectResolver","descriptor":"(Lorg/gradle/api/Project;Lorg/gradle/api/attributes/Attribute;Ljava/io/Serializable;Ljava/lang/String;Ljava/lang/String;Z)Lfoundry/gradle/artifacts/Resolver;","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"foundry/gradle/artifacts/Resolver","name":"addSubprojectDependencies","descriptor":"(Lorg/gradle/api/Project;)V","metadata":{"isTaskAction":false,"isVirtual":false}},{"owner":"org/gradle/api/Project","name":"getSubprojects","descriptor":"()Ljava/util/Set;","metadata":{"isTaskAction":false,"isVirtual":false}}]}}]} \ No newline at end of file diff --git a/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/FoundryProperties.kt b/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/FoundryProperties.kt index 4580a7779..310f27d1d 100644 --- a/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/FoundryProperties.kt +++ b/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/FoundryProperties.kt @@ -779,6 +779,14 @@ internal constructor( KotlinVersion.fromVersion(it) } + /** Defines .kotlin_version. */ + public val kotlinVersionFilePath: String? + get() = optionalStringProperty("foundry.kotlin.kotlin-version-file-path", blankIsNull = true) + + /** Defines .java_version. */ + public val javaVersionFilePath: String? + get() = optionalStringProperty("foundry.jvm.java-version-file-path", blankIsNull = true) + /** Defines a required vendor for JDK toolchains. */ public val jvmVendor: Provider get() = diff --git a/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/FoundryRootPlugin.kt b/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/FoundryRootPlugin.kt index c26aeed56..0086303be 100644 --- a/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/FoundryRootPlugin.kt +++ b/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/FoundryRootPlugin.kt @@ -36,11 +36,13 @@ import foundry.gradle.properties.sneakyNull import foundry.gradle.stats.ModuleStatsTasks import foundry.gradle.tasks.AndroidTestApksTask import foundry.gradle.tasks.CoreBootstrapTask +import foundry.gradle.tasks.FoundryValidationTask import foundry.gradle.tasks.GjfDownloadTask import foundry.gradle.tasks.InstallCommitHooksTask import foundry.gradle.tasks.KtLintDownloadTask import foundry.gradle.tasks.KtfmtDownloadTask import foundry.gradle.tasks.SortDependenciesDownloadTask +import foundry.gradle.tasks.ValidateVersionsMatch import foundry.gradle.tasks.robolectric.UpdateRobolectricJarsTask import foundry.gradle.testing.EmulatorWtfTests import foundry.gradle.testing.RoborazziTests @@ -49,6 +51,7 @@ import foundry.gradle.util.Thermals import foundry.gradle.util.ThermalsData import java.util.Locale import javax.inject.Inject +import kotlin.jvm.optionals.getOrNull import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.configuration.BuildFeatures @@ -164,6 +167,31 @@ internal class FoundryRootPlugin @Inject constructor(private val buildFeatures: AndroidSourcesConfigurer.patchSdkSources(compileSdk, project, latestCompileSdkWithSources) } } + + FoundryValidationTask.registerLifecycleTask(project) + + foundryProperties.javaVersionFilePath?.let { javaVersionFilePath -> + foundryProperties.versions.jdk.getOrNull()?.let { catalogVersion -> + ValidateVersionsMatch.register( + project = project, + type = "javaVersion", + versionFilePath = javaVersionFilePath, + catalogVersion = catalogVersion.toString(), + foundryVersions = foundryProperties.versions, + ) + } + } + + foundryProperties.kotlinVersionFilePath?.let { javaVersionFilePath -> + ValidateVersionsMatch.register( + project = project, + type = "kotlinVersion", + versionFilePath = javaVersionFilePath, + catalogVersion = foundryProperties.versions.kotlin, + foundryVersions = foundryProperties.versions, + ) + } + project.configureFoundryRootBuildscript( foundryProperties.versions.jdk.asProvider(project.providers), foundryProperties.jvmVendor.map(JvmVendorSpec::matching).orNull, diff --git a/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/FoundryVersions.kt b/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/FoundryVersions.kt index 71a5359be..b30e303c5 100644 --- a/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/FoundryVersions.kt +++ b/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/FoundryVersions.kt @@ -29,6 +29,7 @@ internal class FoundryVersions( private val libResolver: (String) -> Optional, bundleResolver: (String) -> Optional>, val boms: Set>, + val catalogName: String, ) { /** @@ -48,6 +49,7 @@ internal class FoundryVersions( it.endsWith(".bom") } .mapTo(LinkedHashSet()) { catalog.findLibrary(it).get() }, + catalogName = catalog.name, ) // Have to use Optional because ConcurrentHashMap doesn't allow nulls for absence diff --git a/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/FoundryValidationTask.kt b/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/FoundryValidationTask.kt new file mode 100644 index 000000000..10721fd2d --- /dev/null +++ b/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/FoundryValidationTask.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2025 Slack Technologies, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package foundry.gradle.tasks + +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.tasks.TaskProvider + +/** Marker interface for Foundry validation tasks that can be depended on by type. */ +public interface FoundryValidationTask : Task { + public companion object { + internal fun registerLifecycleTask(project: Project): TaskProvider { + return LifecycleTask.register(project, "validateFoundryProject") { + dependsOn(project.tasks.withType(FoundryValidationTask::class.java)) + } + } + } +} diff --git a/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/LifecycleTask.kt b/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/LifecycleTask.kt new file mode 100644 index 000000000..fe104f00d --- /dev/null +++ b/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/LifecycleTask.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2025 Slack Technologies, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package foundry.gradle.tasks + +import foundry.gradle.register +import org.gradle.api.Action +import org.gradle.api.DefaultTask +import org.gradle.api.Project +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.UntrackedTask + +@UntrackedTask(because = "Just a lifecycle task") +internal abstract class LifecycleTask : DefaultTask() { + companion object { + internal fun register( + project: Project, + name: String, + group: String = "foundry", + action: Action = Action {}, + ): TaskProvider { + return project.tasks.register(name) { + this.group = group + action.execute(this) + } + } + } +} diff --git a/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/ValidateVersionsMatch.kt b/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/ValidateVersionsMatch.kt new file mode 100644 index 000000000..7eab49186 --- /dev/null +++ b/platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/ValidateVersionsMatch.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2025 Slack Technologies, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package foundry.gradle.tasks + +import foundry.gradle.FoundryVersions +import foundry.gradle.capitalizeUS +import foundry.gradle.register +import org.gradle.api.DefaultTask +import org.gradle.api.Project +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction + +/** + * A Gradle task that validates whether version specified in a given [versionFile] (for example: + * `.java_version`) matches the expected version defined in a version catalog ([catalogVersion]). If + * the versions match, an [outputFile] is generated with the status "valid". If they do not match, + * the task fails with a descriptive error message. + * + * This is useful for projects that define the version in multiple places, as some tools like + * Renovate and GitHub actions work well with a `.java_version` file and some caching mechanisms + * want a single manifest file. + */ +@CacheableTask +public abstract class ValidateVersionsMatch : DefaultTask(), FoundryValidationTask { + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + public abstract val versionFile: RegularFileProperty + + @get:Input public abstract val versionFileRelativePath: Property + @get:Input public abstract val catalogName: Property + @get:Input public abstract val catalogVersion: Property + + @get:OutputFile public abstract val outputFile: RegularFileProperty + + init { + group = "foundry" + } + + @TaskAction + internal fun validate() { + val fileVersion = versionFile.asFile.get().readText().trim() + val requiredVersion = catalogVersion.get() + + check(fileVersion == requiredVersion) { + "Version ($fileVersion) in file '${versionFileRelativePath.get()}' does not match the version in ${catalogName.get()}.versions.toml ($requiredVersion). Please ensure these are aligned" + } + + outputFile.asFile.get().writeText("valid") + } + + internal companion object { + fun register( + project: Project, + type: String, + versionFilePath: String, + catalogVersion: String, + foundryVersions: FoundryVersions, + ) { + project.tasks.register("validate${type.capitalizeUS()}Matches") { + versionFile.set(project.layout.projectDirectory.file(versionFilePath)) + versionFileRelativePath.set(versionFilePath) + this.catalogVersion.set(catalogVersion) + catalogName.set(foundryVersions.catalogName) + outputFile.set( + project.layout.buildDirectory.file("foundry/version-matches/$type/valid.txt") + ) + } + } + } +}