Skip to content

build(deps): bump the github-actions group across 2 directories with 10 updates #326

build(deps): bump the github-actions group across 2 directories with 10 updates

build(deps): bump the github-actions group across 2 directories with 10 updates #326

Workflow file for this run

# SPDX-License-Identifier: Apache-2.0
# © Crown Copyright 2025. This work has been developed by the National Digital Twin Programme and is legally attributed to the Department for Business and Trade (UK) as the governing entity.
name: CI
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
- develop
- 'release/**'
- 'hotfix/**'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
detect-changes:
name: Detect Changes
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
run_pac_ci: >-
${{ (github.event_name == 'pull_request' && steps.changes.outputs.pac == 'true') ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'develop')) }}
run_conventional_commits_ci: >-
${{ (github.event_name == 'pull_request' && steps.changes.outputs.conventional_commits == 'true') ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'develop')) }}
run_sbom_aggregation_ci: >-
${{ (github.event_name == 'pull_request' && steps.changes.outputs.sbom_aggregation == 'true') ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'develop')) }}
steps:
- name: Checkout code
if: github.event_name == 'pull_request'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
name: detect changes
if: github.event_name == 'pull_request'
id: changes
with:
filters: |
pac:
- 'tools/policy-as-code/**'
conventional_commits:
- 'tools/conventional-commits/respository-ruleset/**'
sbom_aggregation:
- 'tools/sbom-aggregation/**'
lint-policies:
permissions:
contents: read
name: Lint Policies
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.run_pac_ci == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup OPA
uses: open-policy-agent/setup-opa@b2b258e089860efaadaaf71bf6e3aecb4a3eeff1 # v2.4.0
with:
version: latest
- name: Setup Regal
uses: open-policy-agent/setup-regal@761188c3b435761fa254beca508a44875619648f # v2.0.0
with:
version: latest
- name: OPA Check (Strict)
run: |
opa check --strict tools/policy-as-code/policy
- name: Regal Lint (github format)
run: |
mkdir -p test-results
regal lint -f github tools/policy-as-code/policy
- name: Regal Lint (sarif format)
if: success() || failure()
run: |
mkdir -p test-results
regal lint -f sarif tools/policy-as-code/policy -o test-results/lint-results.sarif
- name: Upload linting results
if: success() || failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pac-linting-report
path: test-results/lint-results.sarif
test-policies:
permissions:
contents: read
needs: detect-changes
if: needs.detect-changes.outputs.run_pac_ci == 'true'
name: Test Policies
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup OPA
uses: open-policy-agent/setup-opa@b2b258e089860efaadaaf71bf6e3aecb4a3eeff1 # v2.4.0
with:
version: latest
- name: Run OPA Tests and Generate Reports
run: |
# Create output directory
mkdir -p test-results
mkdir -p test-results/coverage
mkdir -p tmp
opa test --format=json tools/policy-as-code/policy > tmp/test-results.json
# 1. GENERATE TEST RESULTS (JUnit XML)
# Convert JSON results to JUnit XML using jq
jq -r '
"<testsuites time=\"\((map(.duration) | add // 0) / 1000000000)\">
<testsuite name=\"OPA\" tests=\"\(length)\" failures=\"\(map(select(.fail)) | length)\" time=\"\((map(.duration) | add // 0) / 1000000000)\">
\(map("<testcase name=\"\(.name)\" classname=\"\(.package)\" time=\"\(.duration/1000000000)\">\(if .fail then "<failure message=\"Test failed\"/>" else "" end)</testcase>") | join(""))
</testsuite>
</testsuites>"
' tmp/test-results.json > test-results/test-results-junit.xml
# 2. GENERATE TEST EXECUTION REPORT (Generic Test Data for SonarQube)
# Convert OPA JSON test results to Sonar Generic Execution XML format
jq -r '
"<testExecutions version=\"1\">" +
(
[
group_by(.location.file)[] |
"<file path=\"\(.[0].location.file)\">" +
(
map(
"<testCase name=\"\(.name)\" duration=\"\((.duration / 1000000) | floor)\">" +
(if .fail then "<failure message=\"Test failed\"/>" else "" end) +
"</testCase>"
) | join("")
) +
"</file>"
] | join("")
) +
"</testExecutions>"
' tmp/test-results.json > test-results/test-results.xml
# 3. GENERATE COVERAGE REPORT (Generic Test Data)
# Convert OPA JSON coverage to Sonar Generic XML format
opa test --coverage --format=json tools/policy-as-code/policy | jq -r '
"<coverage version=\"1\">" +
(
[
.files | to_entries[] |
"<file path=\"\(.key)\">" +
(
[
(.value.covered[]? | range(.start.row; .end.row + 1) | "<lineToCover lineNumber=\"\(.)\" covered=\"true\"/>"),
(.value.not_covered[]? | range(.start.row; .end.row + 1) | "<lineToCover lineNumber=\"\(.)\" covered=\"false\"/>")
] | join("")
) +
"</file>"
] | join("")
) +
"</coverage>"
' > test-results/coverage/coverage.xml
- name: Upload test reports
if: success() || failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pac-test-report
path: test-results/
test-conventional-commits:
permissions:
contents: read
needs: detect-changes
if: needs.detect-changes.outputs.run_conventional_commits_ci == 'true'
name: Test Conventional Commits
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.12'
cache: 'pip'
- name: Install dependencies
run: |
pip install --only-binary :all: --require-hashes -r tools/conventional-commits/respository-ruleset/requirements.txt
- name: Run Tests
run: |
# Create output directory
mkdir -p test-results
pytest --junitxml=test-results/test-results-conventional-commits.xml \
-- tools/conventional-commits/respository-ruleset/test_commit_regex.py
- name: Upload test reports
if: success() || failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: conventional-commits-test-report
path: test-results/
test-sbom-aggregation:
permissions:
contents: read
needs: detect-changes
if: needs.detect-changes.outputs.run_sbom_aggregation_ci == 'true'
name: Test SBOM Aggregation
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.12'
cache: 'pip'
- name: Install dependencies
run: |
pip install --only-binary :all: --require-hashes -r tools/sbom-aggregation/requirements.txt
- name: Run Tests
run: |
# Create output directory
mkdir -p test-results/coverage
pytest --cov-config=tools/sbom-aggregation/.coveragerc \
--cov=merge_sboms \
--cov-report xml:test-results/coverage/sbom-cov.xml \
--junitxml=test-results/test-results-sbom-aggregation.xml \
-- tools/sbom-aggregation/test_merge_sboms.py
- name: Upload test reports
if: success() || failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: sbom-aggregation-test-report
path: test-results/
publish-results:
permissions:
contents: read
security-events: write
needs:
- lint-policies
- test-policies
- test-conventional-commits
- test-sbom-aggregation
if: always()
name: Publish Reports
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: '*-report'
path: test-results
merge-multiple: true
- name: Detect reports exist
id: results-check
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const fs = require('fs');
const args = [];
const hasSarif = fs.existsSync('test-results/lint-results.sarif');
const hasUnit = fs.existsSync('test-results/test-results.xml');
const hasConventionalCommits = fs.existsSync('test-results/test-results-conventional-commits.xml');
const hasSbomUnit = fs.existsSync('test-results/test-results-sbom-aggregation.xml');
const hasCoverage = fs.existsSync('test-results/coverage/coverage.xml');
const hasSbomCoverage = fs.existsSync('test-results/coverage/sbom-cov.xml');
core.setOutput('sarif-reports', hasSarif);
core.setOutput('unit-reports', hasUnit || hasConventionalCommits || hasSbomUnit);
core.setOutput('coverage-reports', hasCoverage || hasSbomCoverage);
// Check for lint report
if (hasSarif) {
args.push('-Dsonar.sarifReportPaths=test-results/lint-results.sarif');
}
// Check for Test Execution reports
if (hasUnit) {
args.push('-Dsonar.testExecutionReportPaths=test-results/test-results.xml');
}
// Check for Python test reports
const pythonTestReports = [];
if (hasConventionalCommits) {
pythonTestReports.push('test-results/test-results-conventional-commits.xml');
}
if (hasSbomUnit) {
pythonTestReports.push('test-results/test-results-sbom-aggregation.xml');
}
if (pythonTestReports.length > 0) {
args.push(`-Dsonar.python.xunit.reportPath=${pythonTestReports.join(',')}`);
}
// Check for coverage report
if (hasCoverage) {
args.push('-Dsonar.coverageReportPaths=test-results/coverage/coverage.xml');
}
if (hasSbomCoverage) {
args.push('-Dsonar.python.coverage.reportPaths=test-results/coverage/sbom-cov.xml');
}
// Join with spaces and set as output
core.setOutput('sonar-args', args.join(' '));
- name: Upload SARIF file
if: |
github.event.repository.visibility == 'public' &&
steps.results-check.outputs.sarif-reports == 'true'
uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
sarif_file: test-results/lint-results.sarif
category: regal-lint
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8.2.1
if: github.actor != 'dependabot[bot]'
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: >
${{ steps.results-check.outputs.sonar-args }}
- name: Publish Test Results
uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 # v3.0.0
if: |
always() &&
steps.results-check.outputs.unit-reports == 'true'
with:
name: OPA Test Results
path: 'test-results/test-results-*.xml'
reporter: java-junit