Skip to content

Commit 37051f5

Browse files
authored
Use the problems API in more places (#1266)
<!-- ⬆ Put your description above this! ⬆ Please be descriptive and detailed. Please read our [Contributing Guidelines](https://github.com/tinyspeck/foundry/blob/main/.github/CONTRIBUTING.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct). Don't worry about deleting this, it's not visible in the PR! -->
1 parent 4cf60a9 commit 37051f5

File tree

10 files changed

+117
-35
lines changed

10 files changed

+117
-35
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright (C) 2025 Slack Technologies, LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package foundry.gradle
17+
18+
import org.gradle.api.problems.ProblemGroup
19+
20+
internal object FoundryShared {
21+
val PROBLEM_GROUP = ProblemGroup.create("foundry-group", "Foundry Problems")
22+
const val FOUNDRY_TASK_GROUP = "foundry"
23+
}

platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/avoidance/ComputeAffectedProjectsTask.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package foundry.gradle.avoidance
1818
import com.jraska.module.graph.DependencyGraph
1919
import foundry.common.FoundryLogger
2020
import foundry.gradle.FoundryProperties
21+
import foundry.gradle.FoundryShared
2122
import foundry.gradle.properties.setDisallowChanges
2223
import foundry.gradle.property
2324
import foundry.gradle.util.gradle
@@ -105,7 +106,7 @@ public abstract class ComputeAffectedProjectsTask : DefaultTask() {
105106
@get:Internal internal abstract val rootDir: DirectoryProperty
106107

107108
init {
108-
group = "foundry"
109+
group = FoundryShared.FOUNDRY_TASK_GROUP
109110
description = "Computes affected projects and writes output files to an output directory."
110111
}
111112

platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/stats/ModuleStats.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import com.squareup.moshi.JsonClass
2323
import com.squareup.wire.gradle.WireTask
2424
import foundry.common.json.JsonTools
2525
import foundry.gradle.FoundryProperties
26+
import foundry.gradle.FoundryShared
2627
import foundry.gradle.artifacts.FoundryArtifact
2728
import foundry.gradle.artifacts.Publisher
2829
import foundry.gradle.artifacts.Resolver
@@ -262,7 +263,7 @@ public abstract class ModuleStatsAggregatorTask : DefaultTask() {
262263
@get:OutputFile public abstract val outputFile: RegularFileProperty
263264

264265
init {
265-
group = "foundry"
266+
group = FoundryShared.FOUNDRY_TASK_GROUP
266267
}
267268

268269
@TaskAction
@@ -365,7 +366,7 @@ internal abstract class ModuleStatsCollectorTask @Inject constructor(objects: Ob
365366
@get:OutputFile abstract val outputFile: RegularFileProperty
366367

367368
init {
368-
group = "foundry"
369+
group = FoundryShared.FOUNDRY_TASK_GROUP
369370
}
370371

371372
@TaskAction

platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/AndroidTestApksTask.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package foundry.gradle.tasks
1717

18+
import foundry.gradle.FoundryShared
1819
import foundry.gradle.artifacts.FoundryArtifact
1920
import foundry.gradle.artifacts.Resolver
2021
import foundry.gradle.properties.setDisallowChanges
@@ -51,7 +52,7 @@ public abstract class AndroidTestApksTask : DefaultTask() {
5152
@get:OutputFile public abstract val outputFile: RegularFileProperty
5253

5354
init {
54-
group = "foundry"
55+
group = FoundryShared.FOUNDRY_TASK_GROUP
5556
}
5657

5758
@OptIn(ExperimentalPathApi::class)

platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/CheckManifestPermissionsTask.kt

+39-17
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,19 @@
1515
*/
1616
package foundry.gradle.tasks
1717

18+
import foundry.gradle.FoundryShared
1819
import java.io.File
20+
import javax.inject.Inject
1921
import javax.xml.parsers.DocumentBuilderFactory
2022
import kotlin.system.measureTimeMillis
2123
import okio.buffer
2224
import okio.source
2325
import org.gradle.api.DefaultTask
26+
import org.gradle.api.GradleException
2427
import org.gradle.api.file.RegularFileProperty
28+
import org.gradle.api.problems.ProblemId
29+
import org.gradle.api.problems.Problems
30+
import org.gradle.api.problems.Severity
2531
import org.gradle.api.provider.SetProperty
2632
import org.gradle.api.tasks.CacheableTask
2733
import org.gradle.api.tasks.Input
@@ -32,9 +38,9 @@ import org.gradle.api.tasks.PathSensitivity
3238
import org.gradle.api.tasks.TaskAction
3339
import org.w3c.dom.NodeList
3440

35-
@Suppress("UnstableApiUsage")
3641
@CacheableTask
37-
internal abstract class CheckManifestPermissionsTask : DefaultTask() {
42+
internal abstract class CheckManifestPermissionsTask @Inject constructor(problems: Problems) :
43+
DefaultTask() {
3844

3945
@get:PathSensitive(PathSensitivity.NONE)
4046
@get:InputFile
@@ -52,6 +58,8 @@ internal abstract class CheckManifestPermissionsTask : DefaultTask() {
5258

5359
@get:Input abstract val permissionAllowlist: SetProperty<String>
5460

61+
private val problemReporter = problems.reporter
62+
5563
@TaskAction
5664
fun check() {
5765
measureTimeMillis {
@@ -68,24 +76,38 @@ internal abstract class CheckManifestPermissionsTask : DefaultTask() {
6876
logger.debug("$LOG ${permissions.size} parsed permissions: $permissions")
6977

7078
val added = permissions - allowlist
79+
val removed = allowlist - permissions
80+
var exception: PermissionAllowlistException? = null
81+
var solution = ""
7182
if (added.isNotEmpty()) {
72-
throw PermissionAllowlistException(
73-
"New permission(s) detected! If this is intentional, " +
74-
"please add them to $allowlistFile and update your PR (a code owners group will be " +
75-
"added for review). Added permissions: $added"
76-
)
83+
exception = PermissionAllowlistException("New permission(s) detected!")
84+
solution =
85+
"If this is intentional, please add them to $allowlistFile and " +
86+
"update your PR (a code owners group will be added for review)." +
87+
"Added permissions: $added"
88+
} else if (removed.isNotEmpty()) {
89+
exception = PermissionAllowlistException("Removed permission(s) detected!")
90+
solution =
91+
"If this is intentional, please remove them to $allowlistFile and update " +
92+
"your PR (a code owners group will be added for review)." +
93+
"Removed permissions: $removed"
7794
}
7895

79-
val removed = allowlist - permissions
80-
if (removed.isNotEmpty()) {
81-
throw PermissionAllowlistException(
82-
"Removed permission(s) detected! If this is " +
83-
"intentional, please remove them to $allowlistFile and update your PR (a code owners " +
84-
"group will be added for review). Removed permissions: $removed"
85-
)
96+
if (exception == null) {
97+
manifestFile.copyTo(outputFile.asFile.get(), overwrite = true)
98+
} else {
99+
val problemId =
100+
ProblemId.create(
101+
"permission-allowlist",
102+
"Manifest permission check failure",
103+
FoundryShared.PROBLEM_GROUP,
104+
)
105+
problemReporter.throwing(exception, problemId) {
106+
fileLocation(manifestFile.absolutePath)
107+
solution(solution)
108+
severity(Severity.ERROR)
109+
}
86110
}
87-
88-
manifestFile.copyTo(outputFile.asFile.get(), overwrite = true)
89111
}
90112
.let { logger.debug("$LOG Manifest perm checks took $it ms") }
91113
}
@@ -117,4 +139,4 @@ internal abstract class CheckManifestPermissionsTask : DefaultTask() {
117139
}
118140
}
119141

120-
internal class PermissionAllowlistException(message: String) : RuntimeException(message)
142+
internal class PermissionAllowlistException(message: String) : GradleException(message)

platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/InstallCommitHooksTask.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package foundry.gradle.tasks
1717

1818
import com.google.common.io.Resources
19+
import foundry.gradle.FoundryShared
1920
import foundry.gradle.setProperty
2021
import java.io.File
2122
import javax.inject.Inject
@@ -46,7 +47,7 @@ constructor(layout: ProjectLayout, objects: ObjectFactory) : DefaultTask() {
4647
objects.directoryProperty().convention(layout.projectDirectory.dir("config/git/hooks"))
4748

4849
init {
49-
group = "foundry"
50+
group = FoundryShared.FOUNDRY_TASK_GROUP
5051
description = "Installs basic git hook files for formatting to a given output directory."
5152
}
5253

platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/PrintFossaDependencies.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package foundry.gradle.tasks
1717

18+
import foundry.gradle.FoundryShared
1819
import foundry.gradle.capitalizeUS
1920
import foundry.gradle.properties.setDisallowChanges
2021
import foundry.gradle.register
@@ -46,7 +47,7 @@ public abstract class PrintFossaDependencies : BaseDependencyCheckTask() {
4647
@get:OutputFile public abstract val outputFile: RegularFileProperty
4748

4849
init {
49-
group = "foundry"
50+
group = FoundryShared.FOUNDRY_TASK_GROUP
5051
}
5152

5253
override fun handleDependencies(identifiersToVersions: Map<String, String>) {

platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/ValidateVersionsMatch.kt

+34-6
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,20 @@
1515
*/
1616
package foundry.gradle.tasks
1717

18+
import foundry.gradle.FoundryShared
1819
import foundry.gradle.FoundryVersions
1920
import foundry.gradle.capitalizeUS
2021
import foundry.gradle.register
22+
import javax.inject.Inject
23+
import kotlin.io.path.absolutePathString
24+
import kotlin.io.path.readText
2125
import org.gradle.api.DefaultTask
26+
import org.gradle.api.GradleException
2227
import org.gradle.api.Project
2328
import org.gradle.api.file.RegularFileProperty
29+
import org.gradle.api.problems.ProblemId
30+
import org.gradle.api.problems.Problems
31+
import org.gradle.api.problems.Severity
2432
import org.gradle.api.provider.Property
2533
import org.gradle.api.tasks.CacheableTask
2634
import org.gradle.api.tasks.Input
@@ -41,7 +49,8 @@ import org.gradle.api.tasks.TaskAction
4149
* want a single manifest file.
4250
*/
4351
@CacheableTask
44-
public abstract class ValidateVersionsMatch : DefaultTask(), FoundryValidationTask {
52+
public abstract class ValidateVersionsMatch @Inject constructor(problems: Problems) :
53+
DefaultTask(), FoundryValidationTask {
4554
@get:InputFile
4655
@get:PathSensitive(PathSensitivity.NONE)
4756
public abstract val versionFile: RegularFileProperty
@@ -52,20 +61,39 @@ public abstract class ValidateVersionsMatch : DefaultTask(), FoundryValidationTa
5261

5362
@get:OutputFile public abstract val outputFile: RegularFileProperty
5463

64+
private val problemReporter = problems.reporter
65+
5566
init {
56-
group = "foundry"
67+
group = FoundryShared.FOUNDRY_TASK_GROUP
5768
}
5869

5970
@TaskAction
6071
internal fun validate() {
61-
val fileVersion = versionFile.asFile.get().readText().trim()
72+
val versionFilePath = versionFile.asFile.get().toPath()
73+
val fileVersion = versionFilePath.readText().trim()
6274
val requiredVersion = catalogVersion.get()
6375

64-
check(fileVersion == requiredVersion) {
65-
"Version ($fileVersion) in file '${versionFileRelativePath.get()}' does not match the version in ${catalogName.get()}.versions.toml ($requiredVersion). Please ensure these are aligned"
76+
val valid = fileVersion == requiredVersion
77+
if (!valid) {
78+
val problemId =
79+
ProblemId.create(
80+
"validate-versions-match",
81+
"Versions mismatch",
82+
FoundryShared.PROBLEM_GROUP,
83+
)
84+
problemReporter.throwing(
85+
GradleException(
86+
"Version ($fileVersion) in file '${versionFileRelativePath.get()}' does not match the version in ${catalogName.get()}.versions.toml ($requiredVersion)."
87+
),
88+
problemId,
89+
) {
90+
fileLocation(versionFilePath.absolutePathString())
91+
solution("Ensure these versions are aligned.")
92+
severity(Severity.ERROR)
93+
}
6694
}
6795

68-
outputFile.asFile.get().writeText("valid")
96+
outputFile.asFile.get().writeText(valid.toString())
6997
}
7098

7199
internal companion object {

platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/tasks/robolectric/UpdateRobolectricJarsTask.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package foundry.gradle.tasks.robolectric
1717

1818
import foundry.gradle.FoundryProperties
19+
import foundry.gradle.FoundryShared
1920
import foundry.gradle.properties.setDisallowChanges
2021
import foundry.gradle.register
2122
import foundry.gradle.robolectricJars
@@ -61,7 +62,7 @@ internal abstract class UpdateRobolectricJarsTask : DefaultTask(), BootstrapTask
6162
@get:Internal abstract val outputDir: DirectoryProperty
6263

6364
init {
64-
group = "foundry"
65+
group = FoundryShared.FOUNDRY_TASK_GROUP
6566
description = "Downloads the Robolectric android-all jars."
6667
}
6768

platforms/gradle/foundry-gradle-plugin/src/main/kotlin/foundry/gradle/topography/ModuleTopographyTask.kt

+8-5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import foundry.cli.walkEachFile
1919
import foundry.common.json.JsonTools
2020
import foundry.gradle.FoundryExtension
2121
import foundry.gradle.FoundryProperties
22+
import foundry.gradle.FoundryShared
2223
import foundry.gradle.artifacts.FoundryArtifact
2324
import foundry.gradle.artifacts.Publisher
2425
import foundry.gradle.artifacts.Resolver
@@ -47,7 +48,6 @@ import org.gradle.api.Project
4748
import org.gradle.api.file.DirectoryProperty
4849
import org.gradle.api.file.RegularFileProperty
4950
import org.gradle.api.internal.plugins.PluginRegistry
50-
import org.gradle.api.problems.ProblemGroup
5151
import org.gradle.api.problems.ProblemId
5252
import org.gradle.api.problems.Problems
5353
import org.gradle.api.problems.Severity
@@ -173,7 +173,7 @@ public abstract class ModuleTopographyTask : DefaultTask() {
173173
@get:OutputFile public abstract val topographyOutputFile: RegularFileProperty
174174

175175
init {
176-
group = "foundry"
176+
group = FoundryShared.FOUNDRY_TASK_GROUP
177177
}
178178

179179
@TaskAction
@@ -219,7 +219,7 @@ public abstract class ValidateModuleTopographyTask @Inject constructor(problems:
219219
private val problemReporter = problems.reporter
220220

221221
init {
222-
group = "foundry"
222+
group = FoundryShared.FOUNDRY_TASK_GROUP
223223
@Suppress("LeakingThis")
224224
notCompatibleWithConfigurationCache("This task modified build files in place")
225225
@Suppress("LeakingThis") doNotTrackState("This task modified build files in place")
@@ -337,7 +337,11 @@ public abstract class ValidateModuleTopographyTask @Inject constructor(problems:
337337
exception: GradleException = GradleException(),
338338
) {
339339
val problemId =
340-
ProblemId.create("module-topography-validation", "Module validation failed!", PROBLEM_GROUP)
340+
ProblemId.create(
341+
"module-topography-validation",
342+
"Module validation failed!",
343+
FoundryShared.PROBLEM_GROUP,
344+
)
341345
problemReporter.throwing(exception, problemId) {
342346
fileLocation(buildFile.relativeTo(rootDirProperty.asFile.get().toPath()).toString())
343347
solution(solution)
@@ -374,7 +378,6 @@ public abstract class ValidateModuleTopographyTask @Inject constructor(problems:
374378
private const val NAME = "validateModuleTopography"
375379
private val CI_NAME = "ci${NAME.capitalizeUS()}"
376380
internal val GLOBAL_CI_NAME = "global${CI_NAME.capitalizeUS()}"
377-
val PROBLEM_GROUP = ProblemGroup.create("sample-group", "Sample Group")
378381

379382
fun register(
380383
project: Project,

0 commit comments

Comments
 (0)