diff --git a/.github/workflows/ci-cd-default.yml b/.github/workflows/ci-cd-default.yml
index f3d1020b..1ac8f349 100644
--- a/.github/workflows/ci-cd-default.yml
+++ b/.github/workflows/ci-cd-default.yml
@@ -83,12 +83,19 @@ on:
default: 0
jobs:
- check-docker-disk-space:
- name: Check Docker Disk Space & Prune if Needed
+ # No separate up-front Docker disk check job: the GitHub-hosted runners are fresh per job, so
+ # checking one runner's disk says nothing about the next. 'build-deploy' checks its own runner.
+ create-matrix:
+ name: Create build matrix
runs-on: ${{ inputs.runs-on }}
defaults:
run:
shell: bash
+ outputs:
+ app-vars-matrix: ${{ steps.create-matrix.outputs.app-vars }}
+ applications-version: ${{ steps.create-matrix.outputs.applications-version }}
+ codeql-app-vars-matrix: ${{ steps.codeql-matrix.outputs.app-vars }}
+ codeql-has-apps: ${{ steps.codeql-matrix.outputs.has-apps }}
steps:
- name: Notify Internal Status (ci-cd-started)
uses: dsb-norge/github-actions/ci-cd/notify-internal-status@v4
@@ -97,22 +104,6 @@ jobs:
step-name: ci-cd-started
basic-auth-password: ${{ secrets.CLOUD_MONITOR_BASIC_AUTH_PASSWORD }}
- - name: "๐ฉบ Check Docker disk usage and prune if necessary"
- uses: dsb-norge/github-actions/ci-cd/check-docker-disk-space@v4
- with:
- threshold-gb: "7"
-
- create-matrix:
- name: Create build matrix
- needs: check-docker-disk-space
- runs-on: ${{ inputs.runs-on }}
- defaults:
- run:
- shell: bash
- outputs:
- app-vars-matrix: ${{ steps.create-matrix.outputs.app-vars }}
- applications-version: ${{ steps.create-matrix.outputs.applications-version }}
- steps:
- name: "๐งน Clean: ๐ข workspace"
id: clean
uses: dsb-norge/directory-recreate@v1
@@ -135,6 +126,25 @@ jobs:
with:
apps: ${{ inputs.apps }}
+ - name: "๐ฐ Generate: ๐ CodeQL build matrix"
+ id: codeql-matrix
+ if: ${{ ! ( github.event_name == 'pull_request' && github.event.action == 'closed' ) }}
+ env:
+ APP_VARS_MATRIX: ${{ steps.create-matrix.outputs.app-vars }}
+ run: |
+ # Spring Boot apps with changes that have not opted out of CodeQL. 'codeql-enabled' is not
+ # normalized to a boolean until create-build-envs, so both 'false' and false opt out here.
+ MATRIX=$(jq -c '
+ .include |= map(select(
+ .["app-vars"]["application-type"] == "spring-boot"
+ and (.["app-vars"]["has-changes"] | tostring) == "true"
+ and (.["app-vars"]["codeql-enabled"] | tostring) != "false"
+ ))
+ | .["application-name"] = [.include[]["application-name"]]
+ ' <<< "${APP_VARS_MATRIX}")
+ echo "app-vars=${MATRIX}" >> "$GITHUB_OUTPUT"
+ echo "has-apps=$(jq '.include | length > 0' <<< "${MATRIX}")" >> "$GITHUB_OUTPUT"
+
- name: Notify Internal Status (build-matrix-finished)
if: always()
uses: dsb-norge/github-actions/ci-cd/notify-internal-status@v4
@@ -245,38 +255,8 @@ jobs:
with:
dsb-build-envs: ${{ steps.build-env.outputs.json }}
- - name: "๐ Initialize CodeQL for Java and Kotlin"
- if: |
- matrix.app-vars['has-changes'] == true
- && ! ( github.event_name == 'pull_request' && github.event.action == 'closed' )
- && matrix.app-vars.application-type == 'spring-boot'
- && fromJSON(steps.build-env.outputs.json).codeql-enabled
- # Soft rollout: CodeQL steps must not fail callers that lack GHAS or
- # security-events: write. Remove once all consumers are onboarded.
- continue-on-error: true
- uses: github/codeql-action/init@v4
- with:
- languages: java-kotlin
- # build-mode: manual (was: none) โ the tracer follows the real Maven
- # build below, which runs on our runners with access to internal
- # artifacts. none-mode was not an alternative: it never extracted
- # Kotlin at all, so pure-Kotlin apps failed analysis with "CodeQL
- # could not process any code written in Java/Kotlin".
- # The flip side of manual mode is that the tracer injects itself into
- # kotlinc and refuses Kotlin versions newer than the bundle supports,
- # which fails the Maven build itself (not this step). build-maven-project
- # guards against that by disabling Kotlin extraction when needed.
- build-mode: manual
- # Add the code-quality query suite (maintainability/reliability โ the
- # SonarQube-type findings) on top of the security queries. Findings
- # land in code scanning alongside the security results: while GitHub
- # Code Quality is in preview, only its own default setup may upload
- # to the Code Quality product (`analysis-kinds: code-quality` is
- # rejected in custom workflows with "use the queries input with
- # code-quality instead"). TODO: switch to Code Quality uploads when
- # the preview opens for advanced setup.
- queries: +code-quality
-
+ # CodeQL for Java/Kotlin runs in the parallel 'codeql' job below, so its tracer does not slow
+ # down this build and its tests.
- name: "๐ Spring Boot app: โ Build and analyze with Maven"
id: maven-build
if: |
@@ -287,29 +267,6 @@ jobs:
with:
dsb-build-envs: ${{ steps.build-env.outputs.json }}
- - name: "๐ Perform CodeQL Analysis for Java and Kotlin"
- if: |
- matrix.app-vars['has-changes'] == true
- && ! ( github.event_name == 'pull_request' && github.event.action == 'closed' )
- && matrix.app-vars.application-type == 'spring-boot'
- && fromJSON(steps.build-env.outputs.json).codeql-enabled
- continue-on-error: true
- uses: github/codeql-action/analyze@v4
- with:
- category: "/language:java-kotlin/app:${{ matrix.app-vars.application-name }}"
-
- - name: "๐งน Clear CodeQL tracer env (LD_PRELOAD) after analysis"
- if: |
- matrix.app-vars['has-changes'] == true
- && ! ( github.event_name == 'pull_request' && github.event.action == 'closed' )
- && matrix.app-vars.application-type == 'spring-boot'
- # CodeQL's manual-mode tracer sets LD_PRELOAD via GITHUB_ENV; it
- # persists for the rest of the job and breaks subsequent Deno steps
- # that use scoped --allow-run. Clear it so docker/az/helm/etc. actions
- # keep their tight subprocess allowlists.
- shell: bash
- run: echo "LD_PRELOAD=" >> "$GITHUB_ENV"
-
- name: "๐ Upload code coverage"
if: |
matrix.app-vars['has-changes'] == true
@@ -487,6 +444,34 @@ jobs:
with:
dsb-build-envs: ${{ steps.build-env.outputs.json }}
+ - name: "๐งน Prune: ๐ the project's own artifacts from the dependencies cache"
+ if: |
+ matrix.app-vars['has-changes'] == true
+ && ! ( github.event_name == 'pull_request' && github.event.action == 'closed' )
+ && matrix.app-vars.application-type == 'spring-boot'
+ && fromJSON(steps.build-env.outputs.json).github-dependencies-cache-enabled
+ && steps.cache-restore.outputs.cache-hit != 'true'
+ # 'install' and 'deploy' put the project's own artifacts in the local repo under a version that
+ # is unique per build, a WAR alone can be hundreds of MB. Kept in the cache they pile up build
+ # after build, and every job pays for them on restore and save. The artifact dirs holding this
+ # build's version are the project's own modules: drop them with all their versions, so older
+ # builds already in the cache go too. Anything needed later is simply fetched again.
+ env:
+ CACHE_PATH: ${{ steps.cache-restore.outputs.cache-normalized-path }}
+ APP_VERSION: ${{ fromJSON(steps.build-env.outputs.json).application-version }}
+ run: |
+ if [[ -z "${CACHE_PATH}" || ! -d "${CACHE_PATH}" || -z "${APP_VERSION}" ]]; then
+ echo "Nothing to prune (cache path: '${CACHE_PATH}', version: '${APP_VERSION}')."
+ exit 0
+ fi
+ echo "Cache size before pruning: $(du -sh "${CACHE_PATH}" | cut -f1)"
+ find "${CACHE_PATH}" -mindepth 2 -type d -name "${APP_VERSION}" -prune -printf '%h\n' | sort -u |
+ while read -r ARTIFACT_DIR; do
+ echo "Removing ${ARTIFACT_DIR#"${CACHE_PATH}"/} ($(du -sh "${ARTIFACT_DIR}" | cut -f1))"
+ rm -rf "${ARTIFACT_DIR}"
+ done
+ echo "Cache size after pruning: $(du -sh "${CACHE_PATH}" | cut -f1)"
+
- name: "๐พ Save ๐ build dependencies cache"
id: cache-save
if: |
@@ -557,6 +542,116 @@ jobs:
basic-auth-password: ${{ secrets.CLOUD_MONITOR_BASIC_AUTH_PASSWORD }}
appvars: ${{ toJSON(matrix.app-vars) }}
+ # CodeQL for Java/Kotlin, in parallel with 'build-deploy' instead of tracing its full build with
+ # tests. This job only compiles and packages (build-only), so the analysis is off the critical path.
+ # Not part of 'ci-cd-conclusion': like the CodeQL steps it replaces, it must never block a build.
+ codeql:
+ name: CodeQL (Java/Kotlin)
+ needs: create-matrix
+ if: needs.create-matrix.outputs.codeql-has-apps == 'true'
+ runs-on: ${{ matrix.app-vars.runs-on || inputs.runs-on }}
+ strategy:
+ matrix: ${{ fromJSON(needs.create-matrix.outputs.codeql-app-vars-matrix) }}
+ fail-fast: false
+ env:
+ # Apps may enable Kotlin incremental compilation (eg. '-Dkotlin.compiler.incremental=true' in
+ # .mvn/maven.config) to avoid recompiling in deploy/image steps. The CodeQL tracer is only
+ # known to work with the regular compiler, so force it here. MAVEN_ARGS wins over
+ # .mvn/maven.config, '-ntp' is what the runner image sets by default.
+ MAVEN_ARGS: -ntp -Dkotlin.compiler.incremental=false
+ defaults:
+ run:
+ shell: bash
+ steps:
+ - name: "๐งน Clean: ๐ข workspace"
+ uses: dsb-norge/directory-recreate@v1
+
+ - name: "โฌ Checkout working branch"
+ uses: actions/checkout@v6
+
+ - name: "๐ Obtain GitHub app installation access token for the CI/CD app"
+ id: auth
+ uses: dsb-norge/github-actions/get-github-app-installation-token@v4
+ with:
+ github-app-id: ${{ vars.ORG_CICD_APP_ID }}
+ github-app-installation-id: ${{ vars.ORG_CICD_APP_INSTALLATION_ID }}
+ github-app-private-key: "${{ secrets.ORG_CICD_APP_PRIVATE_KEY }}"
+
+ - name: "๐ฐ Generate: ๐ข DSB build variables"
+ id: build-env
+ uses: dsb-norge/github-actions/ci-cd/create-build-envs@v4
+ with:
+ app-vars: ${{ toJSON(matrix.app-vars) }}
+ github-json: ${{ toJSON(github) }}
+ secrets-json: ${{ toJSON(secrets) }}
+ vars-json: ${{ toJSON(vars) }}
+ sonarqube-token: ${{ secrets.ORG_SONAR_TOKEN }}
+ jasypt-password: ${{ secrets.ORG_JASYPT_LOCAL_ENCRYPTOR_PASSWORD }}
+ acr-tenant-id: ${{ vars.ORG_DSBNO_AZURE_TENANT_ID }}
+ acr-subscription-id: ${{ vars.ORG_ACR_SUBSCRIPTION_ID }}
+ acr-push-service-principal-id: ${{ vars.ORG_ACR_PUSH_SERVICE_PRINCIPAL_ID }}
+ github-repo-token: ${{ secrets.GITHUB_TOKEN }}
+ npmjs-token: ${{ secrets.ORG_NPM_TOKEN }}
+ app-config-repo-token: ${{ steps.auth.outputs.github-token }}
+ pr-deploy-aks-creds-tenant-id: ${{ vars.ORG_DSBNO_AZURE_TENANT_ID }}
+ pr-deploy-aks-creds-subscription-id: ${{ vars.ORG_AKS_DEV_SUBSCRIPTION_ID }}
+ pr-deploy-aks-creds-service-principal-id: ${{ vars.ORG_AKS_DEV_ADMIN_SERVICE_PRINCIPAL_ID }}
+ acr-delete-service-principal-id: ${{ vars.ORG_ACR_DELETE_SERVICE_PRINCIPAL_ID }}
+
+ # Restore only: 'build-deploy' owns saving the cache.
+ - name: "๐ Restore ๐ build dependencies cache"
+ if: fromJSON(steps.build-env.outputs.json).github-dependencies-cache-enabled
+ uses: dsb-norge/github-actions/ci-cd/dependencies-cache-restore@v4
+ with:
+ dsb-build-envs: ${{ steps.build-env.outputs.json }}
+
+ - name: "๐ Initialize CodeQL for Java and Kotlin"
+ id: codeql-init
+ # Second check of the opt-out, after create-build-envs has normalized it.
+ if: fromJSON(steps.build-env.outputs.json).codeql-enabled
+ # Soft rollout: CodeQL steps must not fail callers that lack GHAS or
+ # security-events: write. Remove once all consumers are onboarded.
+ continue-on-error: true
+ uses: github/codeql-action/init@v4
+ with:
+ languages: java-kotlin
+ # build-mode: manual (was: none) โ the tracer follows the Maven
+ # build below, which runs on our runners with access to internal
+ # artifacts. none-mode was not an alternative: it never extracted
+ # Kotlin at all, so pure-Kotlin apps failed analysis with "CodeQL
+ # could not process any code written in Java/Kotlin".
+ # The flip side of manual mode is that the tracer injects itself into
+ # kotlinc and refuses Kotlin versions newer than the bundle supports,
+ # which fails the Maven build itself (not this step). build-maven-project
+ # guards against that by disabling Kotlin extraction when needed.
+ build-mode: manual
+ # Add the code-quality query suite (maintainability/reliability โ the
+ # SonarQube-type findings) on top of the security queries. Findings
+ # land in code scanning alongside the security results: while GitHub
+ # Code Quality is in preview, only its own default setup may upload
+ # to the Code Quality product (`analysis-kinds: code-quality` is
+ # rejected in custom workflows with "use the queries input with
+ # code-quality instead"). TODO: switch to Code Quality uploads when
+ # the preview opens for advanced setup.
+ queries: +code-quality
+
+ - name: "๐ Spring Boot app: โ Build for CodeQL with Maven"
+ id: codeql-build
+ if: steps.codeql-init.outcome == 'success'
+ # A failing build here is reported by 'build-deploy' already, never fail the workflow on it.
+ continue-on-error: true
+ uses: dsb-norge/github-actions/ci-cd/build-maven-project@v4
+ with:
+ dsb-build-envs: ${{ steps.build-env.outputs.json }}
+ build-only: "true"
+
+ - name: "๐ Perform CodeQL Analysis for Java and Kotlin"
+ if: steps.codeql-build.outcome == 'success'
+ continue-on-error: true
+ uses: github/codeql-action/analyze@v4
+ with:
+ category: "/language:java-kotlin/app:${{ matrix.app-vars.application-name }}"
+
deploy-to-static:
name: Deploy app(s) to static environments
if: |
diff --git a/ci-cd/README.md b/ci-cd/README.md
index de0e09ee..3b71b578 100644
--- a/ci-cd/README.md
+++ b/ci-cd/README.md
@@ -87,9 +87,11 @@ Artifact attestation requires outbound HTTPS (443) access to Sigstore endpoints
CodeQL runs as part of the build for `spring-boot` (java-kotlin), `vue` (javascript-typescript), `python` and `maven-library` apps, with the `code-quality` query suite on top of the security queries. Maven-based apps with Kotlin sources additionally get [detekt](https://detekt.dev/) findings uploaded to code scanning, see [detekt](#detekt).
+In `ci-cd-default.yml` the Java/Kotlin analysis of `spring-boot` apps runs in its own `codeql` job, in parallel with `build-deploy`, rather than tracing the regular build. That job calls `build-maven-project` with `build-only: true`, which runs `clean package -DskipTests` (keeping `maven-build-project-arguments`) and skips test reporting, artifact deploy, attestation and SBOM. The regular build and its tests thus run without the tracer, and the analysis is off the critical path. The `codeql` job is not part of `ci-cd-conclusion`, so it never blocks a build.
+
#### Kotlin version guard
-Java/Kotlin analysis uses `build-mode: manual`, which means CodeQL's tracer injects itself into `kotlinc` during the real Maven build. When the project's Kotlin version is newer than the installed CodeQL bundle supports, the extractor throws before compilation:
+Java/Kotlin analysis uses `build-mode: manual`, which means CodeQL's tracer injects itself into `kotlinc` during the Maven build it follows. When the project's Kotlin version is newer than the installed CodeQL bundle supports, the extractor throws before compilation:
```
com.semmle.extractor.java.interceptors.KotlinInterceptor$KotlinVersionTooRecentError:
@@ -290,6 +292,7 @@ flowchart TD
mavenDockerBuild["build-spring-boot-image"]
npmBuild["build-nodejs-project"]
npmDockerBuild["build-docker-image"]
+ cachePrune["Prune Own Artifacts From Cache"]
cacheSave["Save Dependencies Cache"]
eventTypeCheck{Event Type?}
ephemeralDeploy["deploy-to-ephemeral"]
@@ -301,7 +304,7 @@ flowchart TD
getToken --> createEnvs --> buildEnvsOutput --> cacheRestore --> appTypeCheck
appTypeCheck -- "spring-boot" --> mavenBuild --> mavenDockerBuild
appTypeCheck -- "vue" --> npmBuild --> npmDockerBuild
- mavenDockerBuild --> cacheSave
+ mavenDockerBuild --> cachePrune --> cacheSave
npmDockerBuild --> cacheSave
cacheSave --> eventTypeCheck
eventTypeCheck -- "PR" --> ephemeralDeploy
@@ -309,6 +312,16 @@ flowchart TD
eventTypeCheck -- "push/manual" --> pruneImages
end
+ %% CodeQL job (matrix, spring-boot apps only), in parallel with build-deploy
+ subgraph codeqlJob["CodeQL Job (Matrix) (codeql)"]
+ codeqlEnvs["create-build-envs"]
+ codeqlCache["dependencies-cache-restore"]
+ codeqlInit["codeql-action/init"]
+ codeqlBuild["build-maven-project (build-only)"]
+ codeqlAnalyze["codeql-action/analyze"]
+ codeqlEnvs --> codeqlCache --> codeqlInit --> codeqlBuild --> codeqlAnalyze
+ end
+
%% AND join node for jobs that require both createMatrixJob and buildDeployJob
andDeployStatic["Waits for both jobs"]
andPruneMaven["Waits for both jobs"]
@@ -334,6 +347,7 @@ flowchart TD
%% Job dependencies (needs)
start --> createMatrixJob
matrixOutput -.-> buildDeployJob
+ matrixOutput -.-> codeqlJob
createMatrixJob --|waits on|--> andDeployStatic
buildDeployJob --|waits on|--> andDeployStatic
andDeployStatic --> deployStaticJob
diff --git a/ci-cd/build-maven-project/action.yml b/ci-cd/build-maven-project/action.yml
index 838ae05c..02fc4136 100644
--- a/ci-cd/build-maven-project/action.yml
+++ b/ci-cd/build-maven-project/action.yml
@@ -89,6 +89,15 @@ inputs:
description: "JSON object with extra environment variables to set in same scope as maven is invoked."
required: false
default: ""
+ build-only:
+ description: |
+ Set to 'true' for builds that run next to the regular build, ie. the CodeQL job and the
+ Spring Boot image build. Skips tests, test reporting, artifact deploy, attestation and SBOM,
+ as the regular build already takes care of those. Without 'mvn-cmd' it runs
+ 'clean package -DskipTests' (keeping the app's arguments, a full 'maven-build-project-command'
+ is still honoured).
+ required: false
+ default: "false"
dsb-build-envs:
description: |
DSB build environment variables JSON.
@@ -165,6 +174,7 @@ runs:
INPUT_DSB_BUILD_ENVS: ${{ inputs.dsb-build-envs }}
INPUT_MVN_VERSION_CMD: ${{ inputs.mvn-version-cmd }}
INPUT_MVN_CMD: ${{ inputs.mvn-cmd }}
+ INPUT_BUILD_ONLY: ${{ inputs.build-only }}
GITHUB_WORKSPACE: ${{ github.workspace }}
run: deno run --allow-env --allow-read --allow-write ${{ github.action_path }}/action/1_mvn-cmd.ts
@@ -213,7 +223,7 @@ runs:
- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@567cc7f8dcea3eba5da355f6ebc95663310d8a07
- if: ${{ !cancelled() && github.action == 'maven-build' }}
+ if: ${{ !cancelled() && github.action == 'maven-build' && inputs.build-only != 'true' }}
with:
check_name: "Test Results - ${{ fromJSON(inputs.dsb-build-envs).application-source-path }}"
files: |
@@ -222,7 +232,7 @@ runs:
- name: Log Test Results
uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 # v3.0.0
- if: ${{ !cancelled() && github.action == 'maven-build'}}
+ if: ${{ !cancelled() && github.action == 'maven-build' && inputs.build-only != 'true' }}
with:
name: "Maven Test Results - ${{ fromJSON(inputs.dsb-build-envs).application-source-path }}"
path: "${{ fromJSON(inputs.dsb-build-envs).application-source-path }}/**/target/**/TEST-*.xml"
@@ -230,6 +240,7 @@ runs:
fail-on-empty: "false"
- id: deploy-artifacts
+ if: inputs.build-only != 'true'
shell: bash
env:
INPUT_BUILD_ENVS: "${{ inputs.dsb-build-envs }}"
diff --git a/ci-cd/build-maven-project/action/1_mvn-cmd.ts b/ci-cd/build-maven-project/action/1_mvn-cmd.ts
index 3019e455..74725490 100644
--- a/ci-cd/build-maven-project/action/1_mvn-cmd.ts
+++ b/ci-cd/build-maven-project/action/1_mvn-cmd.ts
@@ -8,6 +8,7 @@ const MVN_VERSION_ARGUMENTS_DEFAULT: string = '-B'
const MVN_VERSION_GOALS_DEFAULT: string = 'versions:set'
const MVN_ARGUMENTS_DEFAULT: string = '-B'
const MVN_GOALS_DEFAULT: string = 'clean install'
+const MVN_GOALS_BUILD_ONLY: string = 'clean package'
/**
* Main function for the GitHub Action.
@@ -58,18 +59,25 @@ export async function run(): Promise {
core.setOutput('mvn-version-cmd', mvnVersionCmd)
core.endGroup()
- const skipBuildTests: boolean = typeof appVars['maven-build-skip-tests'] === 'boolean' ? appVars['maven-build-skip-tests'] : appVars['maven-build-skip-tests'] === 'true'
+ // Build-only mode (used by the CodeQL job): compile and package without tests, and without
+ // 'install' so nothing lands in the local repo. The app's goals are ignored on purpose, they may
+ // carry e.g. sonar, a full 'maven-build-project-command' is still honoured.
+ const buildOnly: boolean = getActionInput('build-only') === 'true'
+ const skipBuildTests: boolean = buildOnly || (typeof appVars['maven-build-skip-tests'] === 'boolean' ? appVars['maven-build-skip-tests'] : appVars['maven-build-skip-tests'] === 'true')
// --- Determine Maven Build Command ---
core.startGroup('Determining Maven Build Command')
+ if (buildOnly) {
+ core.info(`Build-only mode: using goals '${MVN_GOALS_BUILD_ONLY}' and skipping tests.`)
+ }
const mvnCmd = determineMavenCommand(
mvnCmdInput,
appVars,
safePomFilePath,
- MVN_GOALS_DEFAULT,
+ buildOnly ? MVN_GOALS_BUILD_ONLY : MVN_GOALS_DEFAULT,
MVN_ARGUMENTS_DEFAULT,
'maven-build-project-command',
- 'maven-build-project-goals',
+ buildOnly ? undefined : 'maven-build-project-goals',
'maven-build-project-arguments',
skipBuildTests,
)
diff --git a/ci-cd/build-maven-project/action/test_mvn-cmd.ts b/ci-cd/build-maven-project/action/test_mvn-cmd.ts
new file mode 100644
index 00000000..8d4c89f4
--- /dev/null
+++ b/ci-cd/build-maven-project/action/test_mvn-cmd.ts
@@ -0,0 +1,55 @@
+import { assertEquals, assertStringIncludes } from 'common/test_deps.ts'
+import { mockCore, resetMockCore } from 'common/utils/mock-core.ts'
+import { join, setCore } from 'common/deps.ts'
+import { AppVars } from 'common/interfaces/application-variables.ts'
+import { run } from './1_mvn-cmd.ts'
+
+// Replace the real core with the mock
+setCore(mockCore)
+
+/** Runs the step against a temp workspace with a pom.xml, returns the determined build command. */
+async function runWith(appVars: Partial, buildOnly?: string): Promise {
+ resetMockCore()
+ const workspace = await Deno.makeTempDir()
+ const previousWorkspace = Deno.env.get('GITHUB_WORKSPACE')
+ try {
+ await Deno.writeTextFile(join(workspace, 'pom.xml'), '')
+ Deno.env.set('GITHUB_WORKSPACE', workspace)
+ mockCore.outputs['DSB_BUILD_ENVS'] = JSON.stringify({ 'application-source-path': './', 'application-version': '1.2.3', ...appVars })
+ if (buildOnly !== undefined) mockCore.outputs['BUILD_ONLY'] = buildOnly
+ await run()
+ return mockCore.outputs['mvn-cmd']
+ } finally {
+ if (previousWorkspace === undefined) Deno.env.delete('GITHUB_WORKSPACE')
+ else Deno.env.set('GITHUB_WORKSPACE', previousWorkspace)
+ await Deno.remove(workspace, { recursive: true })
+ }
+}
+
+Deno.test('mvn-cmd - regular build uses the app goals and runs tests', async () => {
+ const cmd = await runWith({ 'maven-build-project-goals': 'clean install sonar:sonar', 'maven-build-project-arguments': '-Duser.timezone=Europe/Oslo' })
+ assertStringIncludes(cmd, 'clean install sonar:sonar')
+ assertStringIncludes(cmd, '-Duser.timezone=Europe/Oslo')
+ assertEquals(cmd.includes('-DskipTests'), false)
+})
+
+Deno.test('mvn-cmd - build-only packages without tests and ignores the app goals', async () => {
+ const cmd = await runWith({ 'maven-build-project-goals': 'clean install sonar:sonar', 'maven-build-project-arguments': '-Duser.timezone=Europe/Oslo' }, 'true')
+ assertEquals(cmd.endsWith(' clean package'), true)
+ assertStringIncludes(cmd, '-Duser.timezone=Europe/Oslo')
+ assertStringIncludes(cmd, '-DskipTests')
+ assertEquals(cmd.includes('install'), false)
+ assertEquals(cmd.includes('sonar'), false)
+})
+
+Deno.test('mvn-cmd - build-only keeps a full maven-build-project-command but skips tests', async () => {
+ const cmd = await runWith({ 'maven-build-project-command': 'mvn -B -Pci verify' }, 'true')
+ assertStringIncludes(cmd, '-Pci verify')
+ assertStringIncludes(cmd, '-DskipTests')
+})
+
+Deno.test('mvn-cmd - build-only false behaves like a regular build', async () => {
+ const cmd = await runWith({}, 'false')
+ assertEquals(cmd.endsWith(' clean install'), true)
+ assertEquals(cmd.includes('-DskipTests'), false)
+})
diff --git a/ci-cd/build-spring-boot-image/action.yml b/ci-cd/build-spring-boot-image/action.yml
index 3ee10db4..f889cd25 100644
--- a/ci-cd/build-spring-boot-image/action.yml
+++ b/ci-cd/build-spring-boot-image/action.yml
@@ -188,6 +188,8 @@ runs:
run: deno run --allow-env --allow-read --allow-write ${{ github.action_path }}/action/5_filter-build-envs.ts
# build docker image
+ # build-only: the regular build already attested the maven artifacts and produced the SBOM used
+ # below, so don't do that a second time here.
- uses: dsb-norge/github-actions/ci-cd/build-maven-project@v4
if: fromJSON(inputs.dsb-build-envs)['has-changes'] == true
with:
@@ -195,6 +197,7 @@ runs:
mvn-cmd: ${{ steps.mvn-cmd.outputs.mvn-cmd }}
set-extra-envs: ${{ steps.extra-envs.outputs.json }}
dsb-build-envs: ${{ steps.filter-build-envs.outputs.dsb-build-envs }}
+ build-only: "true"
- id: tag-and-push
if: fromJSON(inputs.dsb-build-envs)['has-changes'] == true