mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
chore(sonar): switch to cloud
This commit is contained in:
+185
-53
@@ -3,28 +3,51 @@ name: SonarQube
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# Push to dev only, deliberately.
|
||||
# SonarQube Cloud analysis: pushes to dev and main produce branch analysis,
|
||||
# pull requests get PR analysis with decoration on the PR itself (the scanner
|
||||
# picks the context up from the Actions environment). Fork PRs are skipped —
|
||||
# they don't receive SONAR_TOKEN, so the guard on the coverage jobs stops the
|
||||
# whole run (the scan job follows through `needs:`) instead of failing it.
|
||||
# Automatic Analysis must stay disabled on sonarcloud.io — it is mutually
|
||||
# exclusive with CI-based analysis, and it cannot import the lcov coverage
|
||||
# this workflow produces.
|
||||
#
|
||||
# The SonarQube instance is Community Edition, which has no branch or
|
||||
# pull-request analysis: the project holds exactly one view. Analysing a feature
|
||||
# branch would overwrite that view with the branch's numbers until somebody
|
||||
# scanned dev again, so the one branch that may publish is the one everybody
|
||||
# reads. workflow_dispatch stays available for a manual re-run.
|
||||
# Coverage is generated by four parallel jobs (same split as test.yml) instead
|
||||
# of one serial `npm run test:cov` — the serial run takes ~30 minutes, the
|
||||
# fan-out about half that. The scan job then drops each package's lcov into
|
||||
# the path sonar.javascript.lcov.reportPaths expects and runs the scanner.
|
||||
on:
|
||||
push:
|
||||
branches: [dev]
|
||||
branches: [main, dev]
|
||||
pull_request:
|
||||
branches: [main, dev]
|
||||
# Same code filter as test.yml (plus the sonar config itself): a PR that
|
||||
# touches none of these can't change the analysis, so don't spend the
|
||||
# ~15-minute coverage fan-out on it. Pushes stay unfiltered so the branch
|
||||
# view is always refreshed from the merge result.
|
||||
paths:
|
||||
- 'server/**'
|
||||
- 'client/**'
|
||||
- 'shared/**'
|
||||
- 'plugin-sdk/**'
|
||||
- 'package.json'
|
||||
- 'package-lock.json'
|
||||
- 'sonar-project.properties'
|
||||
- '.github/workflows/sonarqube.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
scan:
|
||||
name: Coverage & Scan
|
||||
server-coverage:
|
||||
name: Server Coverage
|
||||
runs-on: ubuntu-latest
|
||||
# Fork PRs don't get SONAR_TOKEN, so the scan at the end could never run —
|
||||
# skip the coverage work too rather than produce artifacts nothing reads.
|
||||
# (Same guard on all four coverage jobs; the scan job inherits the skip
|
||||
# through `needs:`.)
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
# Sonar reads git blame to date each issue and to decide what counts as
|
||||
# new code. A shallow clone leaves it blind to both.
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
@@ -35,59 +58,168 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install plugin-sdk dependencies
|
||||
# plugin-sdk is not a root workspace (workspaces = client, server, shared)
|
||||
# — it ships as its own npm package with its own lockfile, so the root
|
||||
# npm ci does not reach it. Its coverage is part of test:cov all the same.
|
||||
run: cd plugin-sdk && npm ci
|
||||
|
||||
- name: Ensure @swc/core's Linux binary for unplugin-swc
|
||||
# Same reason as test.yml: the lockfile was generated on Windows and omits
|
||||
# @swc/core's Linux optional native binary, so npm ci skips it on the
|
||||
# runner and the server's SWC transform cannot load.
|
||||
#
|
||||
# @testing-library/dom rides along, and has to. This install re-resolves
|
||||
# the tree, and --legacy-peer-deps means npm does not put peer
|
||||
# dependencies back — @testing-library/dom is a peer of
|
||||
# @testing-library/react and not a direct dependency of the client, so it
|
||||
# was silently dropped and all 616 client test files died on
|
||||
# "Cannot find module". In test.yml this step lives in the server job,
|
||||
# which never runs client tests, so the collision only appears once the
|
||||
# jobs are folded into one. The version is read from the peer range rather
|
||||
# than pinned here, so a bump to @testing-library/react carries over.
|
||||
# The lockfile was generated on Windows and omits @swc/core's Linux
|
||||
# optional native binary, so npm ci/install skips it on the runner.
|
||||
# Install the matching version explicitly so the server's SWC transform
|
||||
# (server/vitest.config.ts) can load.
|
||||
run: |
|
||||
SWC_VERSION=$(node -p "require('@swc/core/package.json').version")
|
||||
DOM_RANGE=$(node -p "require('@testing-library/react/package.json').peerDependencies['@testing-library/dom']")
|
||||
npm install --no-save --legacy-peer-deps \
|
||||
"@swc/core-linux-x64-gnu@$SWC_VERSION" \
|
||||
"@testing-library/dom@$DOM_RANGE"
|
||||
|
||||
- name: Verify the test toolchain survived that install
|
||||
# Cheap tripwire: if a future dependency bump loses another peer the same
|
||||
# way, this fails here with the name of the module instead of 600
|
||||
# identical test failures twenty minutes later.
|
||||
run: node -e "require.resolve('@testing-library/dom'); require.resolve('@swc/core')"
|
||||
npm install --no-save --legacy-peer-deps "@swc/core-linux-x64-gnu@$SWC_VERSION"
|
||||
|
||||
- name: Build shared
|
||||
# Only shared, not the root `build`. server and client run their suites
|
||||
# from source through vitest, so building them would add a tsc pass and a
|
||||
# full vite bundle that nothing here reads.
|
||||
run: npm run build --workspace=shared
|
||||
|
||||
- name: Run tests with coverage
|
||||
# One script for all four packages — it already ends with
|
||||
# `--prefix plugin-sdk`. Each writes lcov to <pkg>/coverage/, which is
|
||||
# where sonar.javascript.lcov.reportPaths looks. Without those files every
|
||||
# line reports as uncovered and the dashboard shows a collapse that did
|
||||
# not happen, so this step failing has to stop the scan rather than let it
|
||||
# publish a false number.
|
||||
run: npm run test:cov
|
||||
run: cd server && npm run test:coverage
|
||||
|
||||
- name: Upload coverage
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: sonar-server-coverage
|
||||
path: server/coverage/
|
||||
retention-days: 1
|
||||
|
||||
client-coverage:
|
||||
name: Client Coverage
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
|
||||
# Only the shared and client workspaces — a full root npm ci is not
|
||||
# needed, and the scoped install keeps @testing-library/dom (a peer of
|
||||
# @testing-library/react) in the tree without the re-resolution dance
|
||||
# the folded coverage job used to need.
|
||||
- name: Install dependencies
|
||||
run: npm ci --workspace shared && npm ci --workspace client
|
||||
|
||||
- name: Build shared
|
||||
run: npm run build --workspace=shared
|
||||
|
||||
- name: Run tests with coverage
|
||||
run: cd client && npm run test:coverage
|
||||
|
||||
- name: Upload coverage
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: sonar-client-coverage
|
||||
path: client/coverage/
|
||||
retention-days: 1
|
||||
|
||||
shared-coverage:
|
||||
name: Shared Coverage
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --workspace shared
|
||||
|
||||
- name: Run tests with coverage
|
||||
run: cd shared && npm run test:coverage
|
||||
|
||||
- name: Upload coverage
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: sonar-shared-coverage
|
||||
path: shared/coverage/
|
||||
retention-days: 1
|
||||
|
||||
plugin-sdk-coverage:
|
||||
name: Plugin SDK Coverage
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
defaults:
|
||||
run:
|
||||
working-directory: plugin-sdk
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
# 22 matches the TREK server runtime and has node:sqlite, which the
|
||||
# dev-server tests exercise (same choice as test.yml and
|
||||
# publish-plugin-sdk.yml).
|
||||
node-version: 22
|
||||
|
||||
# plugin-sdk is standalone (own lockfile, not a root workspace), so it
|
||||
# installs from its own directory.
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run tests with coverage
|
||||
run: npm run test:coverage
|
||||
|
||||
- name: Upload coverage
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: sonar-plugin-sdk-coverage
|
||||
path: plugin-sdk/coverage/
|
||||
retention-days: 1
|
||||
|
||||
scan:
|
||||
name: Scan
|
||||
runs-on: ubuntu-latest
|
||||
# Coverage jobs failing must stop the scan: a missing lcov file would make
|
||||
# every line of that package report as uncovered, and the dashboard would
|
||||
# show a collapse that did not happen.
|
||||
needs: [server-coverage, client-coverage, shared-coverage, plugin-sdk-coverage]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
# Sonar reads git blame to date each issue and to decide what counts
|
||||
# as new code. A shallow clone leaves it blind to both.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download server coverage
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: sonar-server-coverage
|
||||
path: server/coverage/
|
||||
|
||||
- name: Download client coverage
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: sonar-client-coverage
|
||||
path: client/coverage/
|
||||
|
||||
- name: Download shared coverage
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: sonar-shared-coverage
|
||||
path: shared/coverage/
|
||||
|
||||
- name: Download plugin-sdk coverage
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: sonar-plugin-sdk-coverage
|
||||
path: plugin-sdk/coverage/
|
||||
|
||||
- name: Scan
|
||||
uses: SonarSource/sonarqube-scan-action@v8
|
||||
env:
|
||||
# SonarQube Cloud is the scan action's default host, so no
|
||||
# SONAR_HOST_URL — the token is a sonarcloud.io token for the
|
||||
# liketrek org.
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
|
||||
with:
|
||||
# New Code is configured as "previous version", so this string is what
|
||||
# draws that line — changing it re-bases every "new code" metric.
|
||||
|
||||
Reference in New Issue
Block a user