mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 08:04:45 +00:00
build: opt-in local SonarQube analysis via local.properties
Adds a `sonar` Gradle target that activates only when `sonar.host.url` is present in local.properties (gitignored). Developers who don't opt in are unaffected: the scanner plugin is neither resolved nor applied, so no dependency downloads, no extra tasks, no config-time cost
This commit is contained in:
+62
@@ -213,6 +213,68 @@ toolchain drifted — file it before publishing.
|
||||
|
||||
---
|
||||
|
||||
## Local SonarQube analysis (opt-in)
|
||||
|
||||
The build supports running a [SonarQube](https://www.sonarsource.com/products/sonarqube/)
|
||||
analysis against a locally hosted server. It is **off by default**: unless you
|
||||
opt in, the scanner plugin is neither downloaded nor applied and the build is
|
||||
unaffected.
|
||||
|
||||
### 1. Install and start a local SonarQube server
|
||||
|
||||
Either run the official Docker image:
|
||||
|
||||
```bash
|
||||
docker run -d --name sonarqube -p 9000:9000 sonarqube:community
|
||||
```
|
||||
|
||||
or download the [Community Build zip](https://www.sonarsource.com/products/sonarqube/downloads/),
|
||||
unzip it, and start it (requires a JDK 17+ on `PATH`):
|
||||
|
||||
```bash
|
||||
cd sonarqube-<version>
|
||||
bin/macosx-universal-64/sonar.sh console # pick the folder matching your OS
|
||||
```
|
||||
|
||||
Once it reports up, open <http://localhost:9000> (first login `admin`/`admin`,
|
||||
you'll be asked to change it), create a **local project** named `Amethyst` with
|
||||
project key `Amethyst`, and generate a **project analysis token** for it
|
||||
(*Project Settings → Analysis Method → With Gradle*, or
|
||||
*My Account → Security → Generate token*). The token looks like `sqp_…`.
|
||||
|
||||
### 2. Point the build at your server
|
||||
|
||||
Add the server and token to `local.properties` (gitignored — the token never
|
||||
lands in the repo):
|
||||
|
||||
```properties
|
||||
sonar.host.url=http://localhost:9000
|
||||
sonar.token=sqp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
### 3. Run the analysis
|
||||
|
||||
```bash
|
||||
./gradlew sonar
|
||||
```
|
||||
|
||||
When it finishes, browse the results at
|
||||
<http://localhost:9000/dashboard?id=Amethyst>.
|
||||
|
||||
Every `sonar.*` entry in `local.properties` is forwarded to the scanner, so any
|
||||
[analysis parameter](https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/analysis-parameters/)
|
||||
can be set there. `sonar.projectKey` / `sonar.projectName` default to the root
|
||||
project name (`Amethyst`).
|
||||
|
||||
Even when opted in, the scanner plugin only loads on invocations that actually
|
||||
request the `sonar` task — ordinary builds and IDE syncs are unaffected (which
|
||||
is also why `./gradlew tasks` doesn't list it).
|
||||
|
||||
Note: the SonarQube Gradle scanner plugin is LGPL-3.0. It is a build-time-only
|
||||
tool fetched after explicit opt-in; it is never linked into shipped artifacts.
|
||||
|
||||
---
|
||||
|
||||
## Release runbook
|
||||
|
||||
The release flow is driven by a tag push. Every cut ships Android + Desktop +
|
||||
|
||||
@@ -1,4 +1,36 @@
|
||||
import com.android.build.gradle.tasks.GenerateResValues
|
||||
import com.diffplug.gradle.spotless.SpotlessExtensionPredeclare
|
||||
import java.util.Properties
|
||||
|
||||
// Local SonarQube analysis is opt-in: it activates only when `sonar.host.url`
|
||||
// is present in local.properties (gitignored) AND a sonar task was requested,
|
||||
// so neither developers who haven't opted in nor ordinary builds/IDE syncs of
|
||||
// opted-in developers resolve or apply the scanner plugin. The Kotlin DSL
|
||||
// compiles this buildscript {} section in an earlier stage that can't see the
|
||||
// file's imports (hence the qualified Properties) or share code with the body,
|
||||
// but it can publish values — the gate is computed once here and read below
|
||||
// via `by extra`.
|
||||
buildscript {
|
||||
val localProperties = File(rootDir, "local.properties")
|
||||
val sonarProperties by extra(
|
||||
java.util.Properties().apply {
|
||||
if (localProperties.exists()) localProperties.inputStream().use { load(it) }
|
||||
},
|
||||
)
|
||||
val sonarEnabled by extra(
|
||||
sonarProperties.getProperty("sonar.host.url") != null &&
|
||||
gradle.startParameter.taskNames.any { it.substringAfterLast(":") in setOf("sonar", "sonarqube") },
|
||||
)
|
||||
if (sonarEnabled) {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
dependencies {
|
||||
// LGPL-3.0, build-time only — never linked into shipped artifacts.
|
||||
classpath(libs.sonarqube.gradle.plugin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.androidApplication) apply false
|
||||
@@ -73,6 +105,32 @@ subprojects {
|
||||
}
|
||||
}
|
||||
|
||||
// Second half of the opt-in local SonarQube support gated above in buildscript {}.
|
||||
// All sonar.* entries in local.properties are forwarded as system properties, so
|
||||
// `./gradlew sonar` behaves exactly like passing them via -Dsonar.xxx=... on the
|
||||
// command line. sonar.projectKey/projectName default to the root project name
|
||||
// ("Amethyst") and only need overriding in local.properties if desired.
|
||||
val sonarEnabled: Boolean by extra
|
||||
if (sonarEnabled) {
|
||||
val sonarProperties: Properties by extra
|
||||
apply(plugin = "org.sonarqube")
|
||||
|
||||
sonarProperties
|
||||
.stringPropertyNames()
|
||||
.filter { it.startsWith("sonar.") }
|
||||
.forEach { System.setProperty(it, sonarProperties.getProperty(it)) }
|
||||
|
||||
// The scanner's sonarResolver task reads AGP's generated-res-values provider
|
||||
// but doesn't depend on the task that produces it — wire it up in every
|
||||
// module that has both (today only :amethyst enables resValues, but the
|
||||
// scanner defect is module-agnostic).
|
||||
subprojects {
|
||||
tasks.named { it == "sonarResolver" }.configureEach {
|
||||
dependsOn(tasks.withType<GenerateResValues>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val installGitHook = tasks.register<Copy>("installGitHook") {
|
||||
val dotGit = File(rootProject.rootDir, ".git")
|
||||
val hooksDir: File = if (dotGit.isFile) {
|
||||
|
||||
@@ -62,6 +62,7 @@ secp256k1KmpJniAndroid = "0.23.0"
|
||||
schnorr256k1Kmp = "1.0.5"
|
||||
securityCryptoKtx = "1.1.0"
|
||||
slf4j = "2.0.18"
|
||||
sonarqubeGradlePlugin = "7.3.1.8318"
|
||||
spotless = "8.8.0"
|
||||
streamWebrtcAndroid = "1.3.10"
|
||||
translate = "17.0.3"
|
||||
@@ -210,6 +211,8 @@ secp256k1-kmp-common = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp", v
|
||||
secp256k1-kmp-jni-android = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-android", version.ref = "secp256k1KmpJniAndroid" }
|
||||
secp256k1-kmp-jni-jvm = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-jvm", version.ref = "secp256k1KmpJniAndroid" }
|
||||
schnorr256k1-kmp = { group = "com.vitorpamplona.schnorr256k1", name = "schnorr256k1-kmp", version.ref = "schnorr256k1Kmp" }
|
||||
# Build-time only, gated behind the local.properties sonar opt-in in the root build script (LGPL-3.0).
|
||||
sonarqube-gradle-plugin = { group = "org.sonarsource.scanner.gradle", name = "sonarqube-gradle-plugin", version.ref = "sonarqubeGradlePlugin" }
|
||||
stream-webrtc-android = { group = "io.getstream", name = "stream-webrtc-android", version.ref = "streamWebrtcAndroid" }
|
||||
unifiedpush = { group = "com.github.UnifiedPush", name = "android-connector", version.ref = "unifiedpush" }
|
||||
play-services-cast-framework = { group = "com.google.android.gms", name = "play-services-cast-framework", version.ref = "playServicesCast" }
|
||||
|
||||
Reference in New Issue
Block a user