Skip to content

Commit 5b09d9a

Browse files
authored
SONARGO-72 Add Gradle module for rule-api update (#11)
1 parent 9243aed commit 5b09d9a

5 files changed

Lines changed: 198 additions & 29 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* SonarSource Cloud Native Gradle Modules
3+
* Copyright (C) 2024-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
import org.sonarsource.cloudnative.gradle.RuleApiExtension
18+
import org.sonarsource.cloudnative.gradle.registerRuleApiGenerateTask
19+
import org.sonarsource.cloudnative.gradle.registerRuleApiUpdateTask
20+
import org.sonarsource.cloudnative.gradle.repox
21+
22+
val ruleApi: Configuration = configurations.create("ruleApi")
23+
val ruleApiExtension = extensions.create<RuleApiExtension>("ruleApi")
24+
25+
repositories {
26+
repox("sonarsource-private-releases", providers, ruleApiExtension.fileOperations)
27+
mavenCentral()
28+
}
29+
30+
dependencies {
31+
ruleApi("com.sonarsource.rule-api:rule-api:2.9.0.4061")
32+
ruleApi("org.slf4j:slf4j-nop:1.7.36") {
33+
because(
34+
"To get rid of a warning. A logging backend is not needed, because the rule API logs everything important to stdout. " +
35+
"Slf4j logs contain only debug information"
36+
)
37+
}
38+
}
39+
40+
project.afterEvaluate {
41+
val languageToSonarpediaDirectory = ruleApiExtension.languageToSonarpediaDirectory.get()
42+
val ruleApiUpdateTasks = mutableSetOf<TaskProvider<JavaExec>>()
43+
languageToSonarpediaDirectory.forEach { (language, sonarpediaDirectory) ->
44+
registerRuleApiUpdateTask(language, file(sonarpediaDirectory)).also { ruleApiUpdateTasks.add(it) }
45+
registerRuleApiGenerateTask(language, file(sonarpediaDirectory))
46+
}
47+
48+
tasks.register("ruleApiUpdate") {
49+
description = "Update ALL rules description"
50+
group = "Rule API"
51+
ruleApiUpdateTasks.forEach { this.dependsOn(it) }
52+
}
53+
}

gradle-modules/src/main/kotlin/org/sonarsource/cloudnative/gradle/BuildUtils.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,38 @@
1717
package org.sonarsource.cloudnative.gradle
1818

1919
import org.gradle.api.Project
20+
import org.gradle.api.artifacts.dsl.RepositoryHandler
21+
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
22+
import org.gradle.api.internal.file.FileOperations
23+
import org.gradle.api.provider.ProviderFactory
2024

2125
fun Project.signingCondition(): Boolean {
2226
val branch = System.getenv()["CIRRUS_BRANCH"] ?: ""
2327
return (branch == "master" || branch.matches("branch-[\\d.]+".toRegex())) &&
2428
gradle.taskGraph.hasTask(":artifactoryPublish")
2529
}
30+
31+
internal fun RepositoryHandler.repox(
32+
repository: String,
33+
providers: ProviderFactory,
34+
fileOperations: FileOperations,
35+
): MavenArtifactRepository =
36+
maven {
37+
name = "artifactory"
38+
url = fileOperations.uri("https://repox.jfrog.io/repox/$repository")
39+
40+
// This authentication relies on env vars configured on Cirrus CI or on Gradle properties (`-P<prop>` flags or `gradle.properties` file)
41+
val artifactoryUsername = providers.environmentVariable("ARTIFACTORY_PRIVATE_USERNAME")
42+
.orElse(providers.gradleProperty("artifactoryUsername"))
43+
val artifactoryPassword = providers.environmentVariable("ARTIFACTORY_PRIVATE_PASSWORD")
44+
.orElse(providers.gradleProperty("artifactoryPassword"))
45+
46+
if (artifactoryUsername.isPresent && artifactoryPassword.isPresent) {
47+
authentication {
48+
credentials {
49+
username = artifactoryUsername.get()
50+
password = artifactoryPassword.get()
51+
}
52+
}
53+
}
54+
}

gradle-modules/src/main/kotlin/org/sonarsource/cloudnative/gradle/CommonSettingsPlugin.kt

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,8 @@ package org.sonarsource.cloudnative.gradle
1919
import com.gradle.develocity.agent.gradle.DevelocityConfiguration
2020
import javax.inject.Inject
2121
import org.gradle.api.Plugin
22-
import org.gradle.api.artifacts.dsl.RepositoryHandler
23-
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
2422
import org.gradle.api.initialization.Settings
2523
import org.gradle.api.internal.file.FileOperations
26-
import org.gradle.api.provider.ProviderFactory
2724
import org.gradle.kotlin.dsl.assign
2825
import org.gradle.kotlin.dsl.configure
2926
import org.gradle.kotlin.dsl.getByType
@@ -59,14 +56,14 @@ open class CommonSettingsPlugin
5956
repositories {
6057
mavenCentral()
6158
gradlePluginPortal()
62-
repox(settings.providers, fileOperations)
59+
repox("sonarsource", settings.providers, fileOperations)
6360
}
6461
}
6562

6663
dependencyResolutionManagement {
6764
repositories {
6865
mavenCentral()
69-
repox(settings.providers, fileOperations)
66+
repox("sonarsource", settings.providers, fileOperations)
7067
}
7168
}
7269

@@ -82,30 +79,6 @@ open class CommonSettingsPlugin
8279
settings.pluginManager.apply("com.gradle.develocity")
8380
}
8481

85-
private fun RepositoryHandler.repox(
86-
providers: ProviderFactory,
87-
fileOperations: FileOperations,
88-
): MavenArtifactRepository =
89-
maven {
90-
name = "artifactory"
91-
url = fileOperations.uri("https://repox.jfrog.io/repox/sonarsource")
92-
93-
// This authentication relies on env vars configured on Cirrus CI or on Gradle properties (`-P<prop>` flags or `gradle.properties` file)
94-
val artifactoryUsername = providers.environmentVariable("ARTIFACTORY_PRIVATE_USERNAME")
95-
.orElse(providers.gradleProperty("artifactoryUsername"))
96-
val artifactoryPassword = providers.environmentVariable("ARTIFACTORY_PRIVATE_PASSWORD")
97-
.orElse(providers.gradleProperty("artifactoryPassword"))
98-
99-
if (artifactoryUsername.isPresent && artifactoryPassword.isPresent) {
100-
authentication {
101-
credentials {
102-
username = artifactoryUsername.get()
103-
password = artifactoryPassword.get()
104-
}
105-
}
106-
}
107-
}
108-
10982
private fun Settings.configureDevelocity() {
11083
val develocity = extensions.getByType<DevelocityConfiguration>()
11184

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* SonarSource Cloud Native Gradle Modules
3+
* Copyright (C) 2024-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonarsource.cloudnative.gradle
18+
19+
import java.io.File
20+
import org.gradle.api.Project
21+
import org.gradle.api.internal.file.FileOperations
22+
import org.gradle.api.model.ObjectFactory
23+
import org.gradle.api.provider.MapProperty
24+
import org.gradle.api.services.BuildService
25+
import org.gradle.api.services.BuildServiceParameters
26+
import org.gradle.api.tasks.JavaExec
27+
import org.gradle.api.tasks.TaskProvider
28+
import org.gradle.kotlin.dsl.assign
29+
import org.gradle.kotlin.dsl.mapProperty
30+
import org.gradle.kotlin.dsl.register
31+
import org.gradle.kotlin.dsl.registerIfAbsent
32+
33+
abstract class RuleApiExtension(
34+
objects: ObjectFactory,
35+
// This is a workaround for https://github.com/gradle/gradle/issues/13121 to access `FileOperations`.
36+
// After the corresponding issue is resolved, this can be simplified into an interface.
37+
val fileOperations: FileOperations,
38+
) {
39+
val languageToSonarpediaDirectory: MapProperty<String, String> = objects.mapProperty<String, String>()
40+
}
41+
42+
/**
43+
* An empty build service to serve as a synchronization point for rule-api tasks.
44+
* Because rule-api requires exclusive access to `$HOME/.sonar/rule-api/rspec`, we force tasks to never run in parallel
45+
* by configuring this service.
46+
*/
47+
abstract class RuleApiService : BuildService<BuildServiceParameters.None>
48+
49+
fun Project.registerRuleApiUpdateTask(
50+
language: String,
51+
sonarpediaLocation: File,
52+
): TaskProvider<JavaExec> =
53+
registerRuleApiTask("ruleApiUpdate$language") {
54+
val branch = providers.gradleProperty("branch")
55+
description = "Update $language rules description"
56+
57+
workingDir = sonarpediaLocation
58+
args(
59+
buildList {
60+
add("update")
61+
if (branch.isPresent) {
62+
add("-branch")
63+
add(branch.get())
64+
}
65+
}
66+
)
67+
}
68+
69+
fun Project.registerRuleApiGenerateTask(
70+
language: String,
71+
sonarpediaLocation: File,
72+
): TaskProvider<JavaExec> =
73+
registerRuleApiTask("ruleApiGenerateRule$language") {
74+
val rule = providers.gradleProperty("rule")
75+
val branch = providers.gradleProperty("branch")
76+
description = "Update rule description for $language"
77+
78+
workingDir = sonarpediaLocation
79+
args(
80+
buildList {
81+
add("generate")
82+
add("-rule")
83+
add(rule.orNull ?: error("To generate rule rspec, please provide -Prule=SXXXX"))
84+
if (branch.isPresent) {
85+
add("-branch")
86+
add(branch.get())
87+
}
88+
}
89+
)
90+
}
91+
92+
private fun Project.registerRuleApiTask(
93+
name: String,
94+
configure: JavaExec.() -> Unit,
95+
): TaskProvider<JavaExec> =
96+
tasks.register<JavaExec>(name) {
97+
group = "Rule API"
98+
usesService(
99+
gradle.sharedServices.registerIfAbsent("ruleApiRepoProvider", RuleApiService::class) {
100+
// because rule-api requires exclusive access to `$HOME/.sonar/rule-api/rspec`, we force tasks to never run in parallel
101+
maxParallelUsages = 1
102+
}
103+
)
104+
classpath = configurations.getByName("ruleApi")
105+
mainClass = "com.sonarsource.ruleapi.Main"
106+
outputs.upToDateWhen {
107+
// As rule-api fetches data from rspec repo, we can't determine if the task is up-to-date
108+
false
109+
}
110+
111+
configure(this)
112+
}

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ spotless-gradle = "6.25.0"
33
blowdryer-gradle = "1.7.1"
44
develocity = "3.18.2"
55
jfrog-gradle = "5.2.5"
6+
rule-api = "2.9.0.4061"
67

78
[libraries]
89
diffplug-spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless-gradle" }
910
diffplug-blowdryer-setup = { module = "com.diffplug.blowdryerSetup:com.diffplug.blowdryerSetup.gradle.plugin", version.ref = "blowdryer-gradle" }
1011
develocity = { module = "com.gradle.develocity:com.gradle.develocity.gradle.plugin", version.ref = "develocity" }
1112
jfrog-buildinfo-gradle = { module = "org.jfrog.buildinfo:build-info-extractor-gradle", version.ref = "jfrog-gradle" }
13+
sonar-rule-api = { module = "org.sonarsource.rule-api:rule-api", version.ref = "rule-api" }
1214

1315
[plugins]
1416
spotless = { id = "com.diffplug.spotless", version.ref = "spotless-gradle" }

0 commit comments

Comments
 (0)