From 941c4f5648bc7c7df99e16b51dac8489a3e1580e Mon Sep 17 00:00:00 2001 From: Tobias Svane Mikkelsen Date: Fri, 29 May 2026 10:39:56 +0200 Subject: [PATCH 1/2] chore: migrate Kotlin shared Sonar workflows to self-hosted SonarQube [NOJIRA] Repoint the reusable Kotlin Sonar workflows at the self-hosted SonarQube (https://sonarqube.vpn.internal.monta.app, behind the Monta VPN): - sonar-cloud.yml + pull-request-kotlin.yml: add a Tailscale step before the gradle `sonar` step so the self-hosted runner can reach the VPN-internal instance, and declare TAILSCALE_AUTHKEY as a required secret (matching the existing code-coverage-kotlin.yml convention). - Rename display strings SonarCloud -> SonarQube. The `sonar.host.url` and the SONARQUBE_TOKEN value are supplied per-service (build.gradle.kts + caller secrets mapping), so no host URL is hardcoded here. Part of the full SonarCloud -> self-hosted SonarQube migration (#project-migrate-to-sonarqube). Pattern follows monta-app/server#23360. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/pull-request-kotlin.yml | 16 ++++++++++++++-- .github/workflows/sonar-cloud.yml | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pull-request-kotlin.yml b/.github/workflows/pull-request-kotlin.yml index 6ca4a46..f8de49a 100644 --- a/.github/workflows/pull-request-kotlin.yml +++ b/.github/workflows/pull-request-kotlin.yml @@ -46,6 +46,9 @@ on: default: false description: 'Skip PR title check' secrets: + TAILSCALE_AUTHKEY: + required: true + description: "Authentication token used for logging into Tailscale" GHL_USERNAME: required: true description: "Github Username (Gradle plugin)" @@ -54,7 +57,7 @@ on: description: "Github Password (Gradle plugin)" SONAR_TOKEN: required: true - description: "SonarCloud token" + description: "SonarQube token" jobs: setup: name: Setup @@ -129,7 +132,16 @@ jobs: /home/runner/.gradle/daemon/**/daemon-*.out.log retention-days: 2 overwrite: true - - name: Upload results to SonarCloud + # The self-hosted SonarQube lives behind the Monta VPN, so the runner + # needs to join the tailnet before the scan step can reach it. + - name: Tailscale + if: ${{ !inputs.skip-sonar }} + uses: tailscale/github-action@6cae46e2d796f265265cfcf628b72a32b4d7cade # v3.3.0 + with: + authkey: ${{ secrets.TAILSCALE_AUTHKEY }} + hostname: "github-${{ github.run_id }}" + args: "--login-server https://headscale.monta.com --accept-routes" + - name: Upload results to SonarQube if: ${{ !inputs.skip-sonar }} env: GHL_USERNAME: ${{ secrets.GHL_USERNAME }} diff --git a/.github/workflows/sonar-cloud.yml b/.github/workflows/sonar-cloud.yml index 51eece8..1367652 100644 --- a/.github/workflows/sonar-cloud.yml +++ b/.github/workflows/sonar-cloud.yml @@ -1,4 +1,4 @@ -name: Sonar Cloud Analysis +name: SonarQube Analysis on: workflow_call: inputs: @@ -26,6 +26,9 @@ on: default: "--no-daemon --parallel" description: 'Additional Gradle arguments' secrets: + TAILSCALE_AUTHKEY: + required: true + description: "Authentication token used for logging into Tailscale" GHL_USERNAME: required: true description: "Github Username (Gradle plugin)" @@ -49,7 +52,7 @@ jobs: runner-size: ${{ inputs.runner-size }} architecture: ${{ inputs.architecture }} sonar-cloud: - name: Sonar Cloud Analysis + name: SonarQube Analysis needs: setup runs-on: ${{ needs.setup.outputs.runner-name }} timeout-minutes: 30 @@ -64,12 +67,20 @@ jobs: distribution: corretto java-version: ${{ inputs.java-version }} cache: 'gradle' - - name: Cache SonarCloud packages + - name: Cache SonarQube packages uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar + # The self-hosted SonarQube lives behind the Monta VPN, so the runner + # needs to join the tailnet before the scan step can reach it. + - name: Tailscale + uses: tailscale/github-action@6cae46e2d796f265265cfcf628b72a32b4d7cade # v3.3.0 + with: + authkey: ${{ secrets.TAILSCALE_AUTHKEY }} + hostname: "github-${{ github.run_id }}" + args: "--login-server https://headscale.monta.com --accept-routes" - name: Build and analyze env: GHL_USERNAME: ${{ secrets.GHL_USERNAME }} From 74216e9654d6eda50e467431bc10c58200950770 Mon Sep 17 00:00:00 2001 From: Tobias Svane Mikkelsen Date: Fri, 29 May 2026 12:37:55 +0200 Subject: [PATCH 2/2] refactor: make Tailscale/SonarQube switch backward-compatible Make TAILSCALE_AUTHKEY an optional secret and guard the Tailscale step on its presence (env.TAILSCALE_AUTHKEY != ''). This lets Kotlin repos migrate to the self-hosted SonarQube independently: - repo not yet migrated (no TAILSCALE_AUTHKEY, build.gradle.kts still on sonarcloud.io) -> Tailscale step skipped, scan keeps hitting SonarCloud. - repo migrated (passes TAILSCALE_AUTHKEY + SONARQUBE_TOKEN, build.gradle.kts -> self-hosted) -> runner joins the tailnet and scans the self-hosted server. Avoids a flag day where merging this PR would break every caller pinned @main until each caller PR lands. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/pull-request-kotlin.yml | 13 ++++++++----- .github/workflows/sonar-cloud.yml | 12 ++++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/pull-request-kotlin.yml b/.github/workflows/pull-request-kotlin.yml index f8de49a..9d25946 100644 --- a/.github/workflows/pull-request-kotlin.yml +++ b/.github/workflows/pull-request-kotlin.yml @@ -47,8 +47,8 @@ on: description: 'Skip PR title check' secrets: TAILSCALE_AUTHKEY: - required: true - description: "Authentication token used for logging into Tailscale" + required: false + description: "Tailscale auth key. When set, the runner joins the tailnet so it can reach the self-hosted SonarQube. Leave unset to keep scanning SonarCloud." GHL_USERNAME: required: true description: "Github Username (Gradle plugin)" @@ -80,6 +80,8 @@ jobs: needs: setup runs-on: ${{ needs.setup.outputs.runner-name }} timeout-minutes: ${{ inputs.test-timeout-minutes }} + env: + TAILSCALE_AUTHKEY: ${{ secrets.TAILSCALE_AUTHKEY }} steps: # Checkout - name: Checkout @@ -133,12 +135,13 @@ jobs: retention-days: 2 overwrite: true # The self-hosted SonarQube lives behind the Monta VPN, so the runner - # needs to join the tailnet before the scan step can reach it. + # needs to join the tailnet before the scan step can reach it. Skipped + # automatically for repos still on SonarCloud (no TAILSCALE_AUTHKEY passed). - name: Tailscale - if: ${{ !inputs.skip-sonar }} + if: ${{ !inputs.skip-sonar && env.TAILSCALE_AUTHKEY != '' }} uses: tailscale/github-action@6cae46e2d796f265265cfcf628b72a32b4d7cade # v3.3.0 with: - authkey: ${{ secrets.TAILSCALE_AUTHKEY }} + authkey: ${{ env.TAILSCALE_AUTHKEY }} hostname: "github-${{ github.run_id }}" args: "--login-server https://headscale.monta.com --accept-routes" - name: Upload results to SonarQube diff --git a/.github/workflows/sonar-cloud.yml b/.github/workflows/sonar-cloud.yml index 1367652..e381f1f 100644 --- a/.github/workflows/sonar-cloud.yml +++ b/.github/workflows/sonar-cloud.yml @@ -27,8 +27,8 @@ on: description: 'Additional Gradle arguments' secrets: TAILSCALE_AUTHKEY: - required: true - description: "Authentication token used for logging into Tailscale" + required: false + description: "Tailscale auth key. When set, the runner joins the tailnet so it can reach the self-hosted SonarQube. Leave unset to keep scanning SonarCloud." GHL_USERNAME: required: true description: "Github Username (Gradle plugin)" @@ -56,6 +56,8 @@ jobs: needs: setup runs-on: ${{ needs.setup.outputs.runner-name }} timeout-minutes: 30 + env: + TAILSCALE_AUTHKEY: ${{ secrets.TAILSCALE_AUTHKEY }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: @@ -74,11 +76,13 @@ jobs: key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar # The self-hosted SonarQube lives behind the Monta VPN, so the runner - # needs to join the tailnet before the scan step can reach it. + # needs to join the tailnet before the scan step can reach it. Skipped + # automatically for repos still on SonarCloud (no TAILSCALE_AUTHKEY passed). - name: Tailscale + if: ${{ env.TAILSCALE_AUTHKEY != '' }} uses: tailscale/github-action@6cae46e2d796f265265cfcf628b72a32b4d7cade # v3.3.0 with: - authkey: ${{ secrets.TAILSCALE_AUTHKEY }} + authkey: ${{ env.TAILSCALE_AUTHKEY }} hostname: "github-${{ github.run_id }}" args: "--login-server https://headscale.monta.com --accept-routes" - name: Build and analyze