Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/CHANGELOGS/run_sonar_scan.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,32 @@

All notable changes to the **run_sonar_scan** callable workflow are documented in this file.

## 2.0.0

### Added

- `coverage-artifact-pattern` input (default `coverage-*`). Every same-run artifact whose name matches
is downloaded into `coverage-artifact-path`, one directory per artifact when more than one matches,
so callers no longer need a coverage merge job or a conditional artifact-name output.
- A warning and a job summary line when no downloaded artifact contains an `lcov.info`; the scan still
runs.
- A check that fails the job when `coverage-artifact-path` already exists with content in the
checkout, so repository files are never reported as coverage.
- A job summary listing the downloaded coverage files.

### Changed

- **Breaking:** the job is renamed from `sonar-scan` ("Run SonarQube scan") to `scan` ("Scan"), so the
check reads `<caller job name> / Scan`.
- **Breaking:** `coverage-artifact-path` defaults to `.sonar-coverage` instead of `.`.
- The job skips runs triggered by `dependabot[bot]` or `renovate[bot]`; callers can drop that condition.
- Workflow permissions default to none, and the job requests only `contents: read`.
- The checkout no longer persists credentials.

### Removed

- **Breaking:** the `coverage-artifact-name` input. Use `coverage-artifact-pattern`.

## 1.3.0

### Added
Expand Down
108 changes: 48 additions & 60 deletions .github/workflows/READMES/run_sonar_scan.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,72 @@
# Run SonarQube Scan

This reusable workflow checks out the ref that triggered the caller, or a specified commit or ref, and runs a SonarQube scan with full Git history. Callers can optionally download a coverage artifact produced earlier in the same workflow run before the scan starts.

## Prerequisites

The consuming repository must include its SonarQube configuration, such as a `sonar-project.properties` file, or otherwise provide configuration supported by the scan action.
Runs a SonarQube scan with full Git history, importing coverage artifacts from the same workflow run.
Skips runs by `dependabot[bot]` and `renovate[bot]`. Requires the repo's `sonar-project.properties`.

## Usage

```yaml
jobs:
sonar-scan:
uses: OpenSesame/core-github-actions/.github/workflows/run_sonar_scan.yml@workflows/run_sonar_scan/1.3.0
sonar:
name: Sonar
needs: [ci-context, app-ci]
if: ${{ !cancelled() && needs.ci-context.outputs.should-run-ci == 'true' }}
uses: OpenSesame/core-github-actions/.github/workflows/run_sonar_scan.yml@<released-sha> # workflows/run_sonar_scan/2.0.0
permissions:
contents: read # Required to check out the code to scan.
with:
commit-identifier: ${{ needs.ci-context.outputs.commit-identifier }}
secrets:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
```

To scan a different commit, tag, or branch, pass `commit-identifier`:
The check is reported as `Sonar / Scan`.

```yaml
with:
commit-identifier: ${{ github.sha }}
```
## Coverage

To import coverage produced by another job, upload the report as an artifact and make the Sonar job depend on the test job:
1. Upload each package's lcov report as an artifact named `coverage-<package>`:

```yaml
jobs:
unit-tests:
runs-on: ubuntu-26.04
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install dependencies
run: npm ci

- name: Run tests with coverage
run: npm test -- --coverage

- name: Upload coverage
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: unit-test-coverage
path: coverage/lcov.info
if-no-files-found: error

sonar-scan:
needs: unit-tests
uses: OpenSesame/core-github-actions/.github/workflows/run_sonar_scan.yml@<released-sha>
with:
coverage-artifact-name: unit-test-coverage
coverage-artifact-path: .coverage
secrets:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
```
```yaml
- name: Upload coverage
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-user-management-api
path: lambdas/user-management-api/coverage/lcov.info
if-no-files-found: error
```

This downloads the report to `.coverage/lcov.info`. Configure SonarQube to read that location, for example with `sonar.javascript.lcov.reportPaths=.coverage/lcov.info`. The workflow does not run tests or generate coverage. If `coverage-artifact-name` is set but the artifact does not exist, the download step fails the job.
2. If Jest runs outside the repo root, make lcov paths repo-relative so Sonar maps coverage to the
right files:

## Inputs
```js
// lambdas/user-management-api/jest.config.js
module.exports = {
coverageReporters: [
['lcov', { projectRoot: require('node:path').resolve(__dirname, '../..') }],
'text',
],
};
```

| Input | Type | Required | Default | Description |
| ------------------------ | ------ | -------- | --------------------- | --------------------------------------------------------------------------- |
| `commit-identifier` | string | No | Triggering ref or SHA | Commit SHA, tag, or branch to check out and scan. |
| `coverage-artifact-name` | string | No | `''` | Name of a coverage artifact uploaded earlier in the same workflow run. |
| `coverage-artifact-path` | string | No | `.` | Directory into which the coverage artifact is downloaded before the scan. |
3. Set the same report path in every repo:

## Secrets
```properties
sonar.javascript.lcov.reportPaths=.sonar-coverage/**/lcov.info
```

| Secret | Required | Description |
| ------------- | -------- | ---------------------------------------------- |
| `SONAR_TOKEN` | Yes | Token used to authenticate the SonarQube scan. |
If no `lcov.info` is downloaded, the job warns and scans without coverage.

The workflow job runs on Ubuntu 26.04.
## Inputs

## Contribution
| Input | Default | Description |
| --------------------------- | --------------------- | ------------------------------------------------------------------------ |
| `commit-identifier` | Triggering ref or SHA | Commit SHA, tag, or branch to scan. |
| `coverage-artifact-pattern` | `coverage-*` | Same-run artifact names to download. Empty skips coverage. |
| `coverage-artifact-path` | `.sonar-coverage` | Download directory. Must not already exist with content in the checkout. |

- Update the workflow, README, and changelog together.
- Create a PR and set a version label following the [versioning instructions](../../../VERSIONING.md).
Secret `SONAR_TOKEN` is required.

## References
## Contribution

- [SonarQube scan action](https://github.com/SonarSource/sonarqube-scan-action)
Update the workflow, README, and changelog together, and set a version label per
[VERSIONING.md](../../../VERSIONING.md).
78 changes: 65 additions & 13 deletions .github/workflows/run_sonar_scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,94 @@ on:
type: string
required: false
default: ''
coverage-artifact-name:
description: Name of a coverage artifact uploaded earlier in the same workflow run
coverage-artifact-pattern:
description: Glob of coverage artifact names uploaded earlier in the same workflow run; empty skips the download
type: string
required: false
default: ''
default: coverage-*
coverage-artifact-path:
description: Directory into which the coverage artifact is downloaded
description: Directory into which matching coverage artifacts are downloaded
type: string
required: false
default: '.'
default: .sonar-coverage
secrets:
SONAR_TOKEN:
required: true

permissions:
contents: read
permissions: {}

defaults:
run:
shell: bash

jobs:
sonar-scan:
name: Run SonarQube scan
scan:
name: Scan
if: ${{ github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]' }}
runs-on: ubuntu-26.04
permissions:
contents: read # Required to check out the code to scan.

steps:
- name: Checkout
- name: Check out code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.commit-identifier }}
fetch-depth: 0
persist-credentials: false

- name: Download coverage artifact
if: ${{ inputs.coverage-artifact-name != '' }}
- name: Check coverage path is empty
if: ${{ inputs.coverage-artifact-pattern != '' }}
env:
COVERAGE_PATH: ${{ inputs.coverage-artifact-path }}
run: |
if [[ -e "$COVERAGE_PATH" && -n "$(ls -A "$COVERAGE_PATH" 2>/dev/null)" ]]; then
echo "::error title=Coverage::coverage-artifact-path '$COVERAGE_PATH' already exists in the checkout and is not empty. Use a directory that only holds downloaded coverage, such as the default .sonar-coverage."
exit 1
fi

- name: Download coverage artifacts
if: ${{ inputs.coverage-artifact-pattern != '' }}
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: ${{ inputs.coverage-artifact-name }}
pattern: ${{ inputs.coverage-artifact-pattern }}
path: ${{ inputs.coverage-artifact-path }}

- name: Report coverage files
if: ${{ inputs.coverage-artifact-pattern != '' }}
env:
COVERAGE_PATTERN: ${{ inputs.coverage-artifact-pattern }}
COVERAGE_PATH: ${{ inputs.coverage-artifact-path }}
run: |
set -Eeuo pipefail

files=""
if [[ -d "$COVERAGE_PATH" ]]; then
files=$(find "$COVERAGE_PATH" -type f -name lcov.info | sort)
fi

if [[ -z "$files" ]]; then
echo "::warning title=Coverage::No lcov.info found in artifacts matching '$COVERAGE_PATTERN'; Sonar will report no coverage"
{
echo "## Coverage"
echo ""
echo "⚠️ No \`lcov.info\` found in artifacts matching \`$COVERAGE_PATTERN\`; Sonar will report no coverage."
} >> "$GITHUB_STEP_SUMMARY"
exit 0
fi

echo "Coverage files downloaded:"
echo "$files"
{
echo "## Coverage"
echo ""
echo "Artifacts matching \`$COVERAGE_PATTERN\` downloaded to \`$COVERAGE_PATH\`:"
echo ""
while IFS= read -r file; do
echo "- \`$file\`"
done <<<"$files"
} >> "$GITHUB_STEP_SUMMARY"

- name: Run SonarQube scan
uses: SonarSource/sonarqube-scan-action@d209202bc7d53ff1cc128f7f907dac145c9d6ae9 # v8.3.0
env:
Expand Down
Loading