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
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
name: Mend CLI scan for Maven
name: Mend CLI scan

on:
workflow_call:
inputs:
java_version:
description: "The Java version to use to compile. Default: 21"
required: false
type: string
default: "21"
maven_version:
description: "The Maven version to use. Defaults to version of the runner image"
required: false
type: string
default: "3.9.14"
SCA:
required: true
type: boolean
Expand All @@ -23,18 +13,13 @@ on:
SAST:
required: true
type: boolean
mode:
description: "'fresh' (checkout + build + scan) or 'deferred' (consume artifact + scan)"
required: false
type: string
default: "fresh"
triggering_run_id:
description: "workflow_run id to pull artifacts from (required for mode='deferred')"
description: "workflow_run id to pull artifacts from"
required: false
type: string
default: ""
pr_feedback:
description: "PR check and comment feedback (deferred mode only)"
description: "Whether to add PR check and comment feedback"
required: false
type: boolean
default: false
Expand Down Expand Up @@ -70,51 +55,32 @@ jobs:
security-events: write

steps:
# Fresh mode: checkout + build

- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
if: inputs.mode == 'fresh'
with:
persist-credentials: false

- uses: project-ncl/shared-github-actions/.github/actions/maven-build@94481b379fd7ddda5fe363d1c1d5fc311930c607 # main
if: inputs.mode == 'fresh'
with:
java_version: ${{ inputs.java_version }}
build_command: "mvn -B -V clean install -DskipTests"
maven_version: ${{ inputs.maven_version }}

# Deferred mode: fetch PR metadata and build artifact

- name: Download PR metadata
if: inputs.mode == 'deferred'
if: inputs.pr_feedback
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: pr-metadata
path: pr-metadata
run-id: ${{ inputs.triggering_run_id }}
run-id: ${{ inputs.triggering_run_id || github.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Read PR metadata
if: inputs.mode == 'deferred'
if: inputs.pr_feedback
id: metadata
run: |
echo "pr_number=$(cat pr-metadata/pr-number)" >> "$GITHUB_OUTPUT"
echo "pr_sha=$(cat pr-metadata/pr-sha)" >> "$GITHUB_OUTPUT"

- name: Download PR build artifact
if: inputs.mode == 'deferred'
- name: Download build artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: pr-build
path: .
run-id: ${{ inputs.triggering_run_id }}
run-id: ${{ inputs.triggering_run_id || github.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

# Deferred mode: create in-progress check-run

- name: Create in-progress check-run
if: inputs.mode == 'deferred' && inputs.pr_feedback
if: inputs.pr_feedback
id: check
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # 9.0.0
env:
Expand All @@ -131,27 +97,23 @@ jobs:
});
return check.id;

# Common: run Mend scan

- name: Run Mend CLI scan
uses: project-ncl/shared-github-actions/.github/actions/mend@94481b379fd7ddda5fe363d1c1d5fc311930c607 # main
with:
PROJECT_NAME: ${{ github.event.repository.name }}
TAGS: >-
${{ inputs.mode == 'deferred'
${{ inputs.pr_feedback
&& format('trigger:pr, pr:{0}, run:{1}', steps.metadata.outputs.pr_number, github.run_id)
|| format('trigger:{0}, run:{1}', github.event_name, github.run_id) }}
SCA: ${{ inputs.SCA }}
SCA_REACHABILITY: ${{ inputs.SCA_REACHABILITY }}
SAST: ${{ inputs.SAST }}
source_ref: ${{ inputs.mode == 'deferred' && format('refs/pull/{0}/head', steps.metadata.outputs.pr_number) || '' }}
source_sha: ${{ inputs.mode == 'deferred' && steps.metadata.outputs.pr_sha || '' }}

# Deferred mode: update check-run + PR comment with results
source_ref: ${{ steps.metadata.outputs.pr_number && format('refs/pull/{0}/head', steps.metadata.outputs.pr_number) || '' }}
source_sha: ${{ steps.metadata.outputs.pr_sha || '' }}

- name: Build result summary
id: summary
if: ${{ !cancelled() && inputs.mode == 'deferred' && inputs.pr_feedback }}
if: ${{ !cancelled() && inputs.pr_feedback }}
run: |
SCA_LOG="$HOME/.mend/logs/sca-results.txt"
SAST_LOG="$HOME/.mend/logs/sast-results.sarif"
Expand Down Expand Up @@ -229,7 +191,7 @@ jobs:
echo "overall=success" >> "$GITHUB_OUTPUT"

- name: Update check-run
if: ${{ !cancelled() && inputs.mode == 'deferred' && inputs.pr_feedback }}
if: ${{ !cancelled() && inputs.pr_feedback }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # 9.0.0
env:
CHECK_ID: ${{ steps.check.outputs.result }}
Expand All @@ -251,7 +213,7 @@ jobs:
});

- name: Post or update PR comment
if: ${{ !cancelled() && inputs.mode == 'deferred' && inputs.pr_feedback }}
if: ${{ !cancelled() && inputs.pr_feedback }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # 9.0.0
env:
PR_NUMBER: ${{ steps.metadata.outputs.pr_number }}
Expand Down
92 changes: 92 additions & 0 deletions .github/workflows/npm-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: NPM CI

on:
workflow_call:
inputs:
fetch_all_commits:
description: "Whether to fetch all commits. Default: false"
required: false
type: boolean
default: false
node_version:
description: "The Node.js version to use. Default: 22.x"
required: false
type: string
default: "22.x"
build_command:
description: "The build command to use. Default: npm ci and npm run build"
required: false
type: string
default: "npm ci && npm run build"
test_command:
description: "The command to use for tests. Default: null"
required: false
type: string
formatter_command:
description: "The command to use for formatting. Default: null"
required: false
type: string
upload_artifacts:
description: "Whether to upload artifacts and their metadata. Default: false"
required: false
type: boolean
default: false

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.node_version }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest

permissions:
contents: read

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: ${{ inputs.fetch_all_commits == true && '0' || '1' }}
persist-credentials: false

- uses: project-ncl/shared-github-actions/.github/actions/npm-build@8ba48d272f8e53b55e5a266a4c3feeab6e7f0526 # main
with:
node_version: ${{ inputs.node_version }}
build_command: ${{ inputs.build_command }}

- if: inputs.test_command
env:
INPUTS_TEST_COMMAND: ${{ inputs.test_command }}
run: bash -c "${INPUTS_TEST_COMMAND}"

- if: inputs.formatter_command
env:
INPUTS_FORMATTER_COMMAND: ${{ inputs.formatter_command }}
run: bash -c "${INPUTS_FORMATTER_COMMAND}"

- name: Save PR metadata
if: inputs.upload_artifacts
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_SHA: ${{ github.event.pull_request.head.sha }}
run: |
mkdir -p pr-metadata
echo "$PR_NUMBER" > pr-metadata/pr-number
echo "$PR_SHA" > pr-metadata/pr-sha

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: inputs.upload_artifacts
with:
name: pr-build
path: |
.
!.git
!pr-metadata
retention-days: 1

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: inputs.upload_artifacts
with:
name: pr-metadata
path: pr-metadata
retention-days: 1
35 changes: 26 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,22 @@ the Java version etc. It is possible to use this within a matrix job.
```
</details>

## Maven Mend
Workflow to run Mend analysis, both SCA (Software Composition Analysis) and SAST (Static Application Security Testing), on Maven projects. Because it has to have access to secrets in the organization or repository, it has two modes: `fresh` and `deferred`.
## NPM CI (`npm-ci.yml`)
Standard CI workflow for NPM projects.

Fresh mode checkouts the code, builds the Maven project, and runs the Mend analysis. It is designed for cronjob schedule, and push to main workflow runs - because for those, the secrets are accessible.
- **Tasks**: Checkout code, set up Node.js, set up NPM, run build command, optionally run tests, optionally check for code formatting errors, and optionally push build artifact (which is used by Mend workflow).

Workflows run in the context of the PR from a fork do not have access to the secrets. For that use-case, deferred mode exists. It is meant to be executed `on: workflow_run` depending on Maven CI workflow with `upload_artifact` set to `true` - this way, the Maven Mend workflow is run in the context of the base repository. It downloads saved PR metadata and build artifact, sets check-run on the PR, runs the Mend analysis, and posts PR comment.
## Mend CI (`mend-ci.yml`)
Workflow to run Mend analysis, both SCA (Software Composition Analysiss) and SAST (Static Application Security Testing), on Maven projects. Because it has to have access to secrets in the organization or repository, on Pull Requests, it is meant to run `on: workflow_run`.

It depends on some build workflow with `upload_artifact` set to `true`, and before analysis, downloads an build artifact.

If `pr_feedback` is `false`, it downloads the build artifact, and runs the Mend analysis. This is meant for cronjob schedule, and push to main workflow runs.

Workflows run in the context of the PR from a fork do not have access to the secrets. It is meant to be executed `on: workflow_run` - this way, the Mend workflow is run in the context of the base repository. `pr_feedback` should be set to `true` and `triggering_run_id` set to ID of a triggering workflow. It downloads saved PR metadata and build artifact, sets check-run on the PR, runs the Mend analysis, and posts PR comment.

<details>
<summary>Example of 'fresh' mode usage</summary>
<summary>Example of push to main and cronjob usage</summary>

```yaml
name: Mend CLI scan for Maven
Expand All @@ -71,8 +78,19 @@ permissions:
security-events: write

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<sha>
- uses: project-ncl/shared-github-actions/.github/actions/maven-build@<sha>
- uses: actions/upload-artifact@<sha>
with:
name: pr-build
path: .

call-maven-mend-ci:
uses: project-ncl/shared-github-actions/.github/workflows/maven-mend-ci.yml@<sha>
needs: build
uses: project-ncl/shared-github-actions/.github/workflows/mend-ci.yml@<sha>
with:
SCA: true
SAST: true
Expand All @@ -86,7 +104,7 @@ jobs:
</details>

<details>
<summary>Example of 'deferred' mode usage</summary>
<summary>Example of PR usage</summary>

```yaml
name: Java CI with Maven
Expand Down Expand Up @@ -126,11 +144,10 @@ concurrency:
jobs:
scan:
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request'
uses: project-ncl/shared-github-actions/.github/workflows/maven-mend-ci.yml@<sha>
uses: project-ncl/shared-github-actions/.github/workflows/mend-ci.yml@<sha>
with:
SCA: true
SAST: true
mode: deferred
triggering_run_id: ${{ github.event.workflow_run.id }}
pr_feedback: true
secrets:
Expand Down