diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml new file mode 100644 index 0000000000..4e9b9ac2ac --- /dev/null +++ b/.github/workflows/analyze.yml @@ -0,0 +1,104 @@ +name: Analyze Repository with Jacoco and SonarQube + +permissions: + id-token: write + contents: write + actions: write + +on: + schedule: + - cron: '19 7 * * *' # Daily at 7:19 AM UTC + workflow_dispatch: + +env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + IDE_SIGNING_ALIAS: ${{ secrets.IDE_SIGNING_ALIAS }} + IDE_SIGNING_AUTH_PASS: ${{ secrets.IDE_SIGNING_AUTH_PASS }} + IDE_SIGNING_AUTH_USER: ${{ secrets.IDE_SIGNING_AUTH_USER }} + IDE_SIGNING_KEY_PASS: ${{ secrets.IDE_SIGNING_KEY_PASS }} + IDE_SIGNING_STORE_PASS: ${{ secrets.IDE_SIGNING_STORE_PASS }} + IDE_SIGNING_URL: ${{ secrets.IDE_SIGNING_URL }} + IDE_SIGNING_KEY_BIN: ${{ secrets.IDE_SIGNING_KEY_BIN }} + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MVN_USERNAME }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MVN_PASSWORD }} + ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MVN_SIGNING_KEY }} + ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MVN_SIGNING_KEY_ID }} + ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MVN_SIGNING_KEY_PASSWORD }} + FIREBASE_CONSOLE_URL: ${{ secrets.FIREBASE_CONSOLE_URL }} + SENTRY_DSN_DEBUG: ${{ secrets.SENTRY_DSN_DEBUG }} + +jobs: + analyze: + name: analysis + runs-on: self-hosted + timeout-minutes: 60 + + steps: + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.12.1 + with: + access_token: ${{ github.token }} + + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Check if Nix is installed + id: check_nix + run: | + if command -v nix >/dev/null 2>&1; then + echo "nix is installed" + echo "nix_installed=true" >> $GITHUB_ENV + else + echo "nix is not installed" + echo "nix_installed=false" >> $GITHUB_ENV + fi + + - name: Install Flox + if: env.nix_installed == 'false' + uses: flox/install-flox-action@v2 + + - name: Create google-services.json + env: + GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }} + run: | + echo "$GOOGLE_SERVICES_JSON" > app/google-services.json + echo "google-services.json created successfully" + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle', '**/*.gradle.kts', '**/libs.versions.toml') }} + restore-keys: ${{ runner.os }}-gradle + + - name: Assemble V8 Debug + run: | + echo "gradle_time_start=$(date +%s)" >> $GITHUB_ENV + flox activate -d flox/base -- ./gradlew :app:assembleV8Debug --no-daemon + echo "gradle_time_end=$(date +%s)" >> $GITHUB_ENV + + - name: Stop Gradle daemons + run: | + flox activate -d flox/base -- ./gradlew --stop + echo "Gradle daemons stopped" + + - name: Cache SonarQube packages + uses: actions/cache@v4 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + + - name: Build and analyze + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: flox activate -d flox/base -- ./gradlew :testing:tooling:assemble :testing:common:assemble sonarqube --info -x lint --continue + + - name: Cleanup google-services.json + if: always() + run: | + rm -f app/google-services.json + echo "google-services.json cleaned up successfully" diff --git a/build.gradle.kts b/build.gradle.kts index 8e2b26005e..8c09555454 100755 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -44,6 +44,7 @@ plugins { alias(libs.plugins.rikka.refine) apply false alias(libs.plugins.google.protobuf) apply false alias(libs.plugins.spotless) + alias(libs.plugins.sonarqube) id("jacoco") } @@ -56,7 +57,6 @@ buildscript { classpath(libs.kotlin.gradle.plugin) classpath(libs.nav.safe.args.gradle.plugin) classpath(libs.kotlin.serialization.plugin) - classpath(libs.nav.safe.args.gradle.plugin) } } @@ -219,6 +219,7 @@ spotless { ".githooks/**/*", "scripts/**/*", ) + targetExclude("scripts/debug-keystore/adfa-keystore.jks") } } @@ -259,9 +260,46 @@ tasks.named("clean") { } } +sonar { + properties { + val binaries = subprojects.flatMap { subproj -> + val dirs = listOf( + subproj.layout.buildDirectory.dir("classes/java/main").get().asFile, + subproj.layout.buildDirectory.dir("classes/kotlin/main").get().asFile, + + subproj.layout.buildDirectory.dir("intermediates/javac/v8Debug/classes").get().asFile, + subproj.layout.buildDirectory.dir("tmp/kotlin-classes/v8Debug").get().asFile + ) + + // include directories that actually exist + dirs.filter { it.exists() }.map { it.absolutePath } + } + + property("sonar.java.binaries", binaries.joinToString(",")) + + property("sonar.c.file.suffixes", "-") + property("sonar.cpp.file.suffixes", "-") + property("sonar.objc.file.suffixes", "-") + + property("sonar.coverage.jacoco.xmlReportPaths", + project.layout.buildDirectory + .dir("reports/jacoco/jacocoAggregateReport/jacocoAggregateReport.xml").get().asFile.absolutePath + ) + + + property("sonar.host.url", "https://sonarcloud.io") + property("sonar.projectKey", "appdevforall_CodeOnTheGo") + property("sonar.organization", "app-dev-for-all") + property("sonar.androidVariant", "v8Debug") + property("sonar.token", System.getenv("SONAR_TOKEN")) + } +} + +tasks.named("sonarqube") { + dependsOn("jacocoAggregateReport") +} tasks.register("jacocoAggregateReport") { - // TODO: Skip xml-inflater and llama-impl until bugs are fixed val excludedProjects = emptySet() // Depend only on testV8DebugUnitTest tasks in subprojects diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 94fa403cf5..204785d213 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -71,6 +71,8 @@ protobuf = "4.32.1" protobuf-plugin = "0.9.5" protoc-gen-kotlin-ext = "1.1.0" +sonarqube = "7.0.0.6105" + [libraries] # Dependencies in composite build @@ -301,3 +303,5 @@ google-protobuf = { id = "com.google.protobuf", version.ref = "protobuf-plugin" rikka-autoresconfig = { id = "dev.rikka.tools.autoresconfig", version = "1.2.2" } rikka-materialthemebuilder = { id = "dev.rikka.tools.materialthemebuilder", version = "1.5.1" } rikka-refine = { id = "dev.rikka.tools.refine", version.ref = "rikka-refine" } + +sonarqube = { id = "org.sonarqube", version.ref = "sonarqube" }