Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
dbcca1c
feat(CNP-165): Add quality-gate action
sasjo Nov 18, 2024
0d90144
build: Update dist files
sasjo Nov 18, 2024
e7be9bf
chore: Fix eslint error
sasjo Nov 18, 2024
e7ca9ae
chore: Fix sonar issue
sasjo Nov 18, 2024
3c83f6c
chore: Fix composite action
sasjo Nov 18, 2024
f1fe28c
chore: Use setup-qodana from action_path
sasjo Nov 18, 2024
e04423d
fix: Use correct input name
sasjo Nov 18, 2024
7cffe40
fix: Use relative action path
sasjo Nov 18, 2024
045f628
fix: Revert to full action name
sasjo Nov 18, 2024
4098af9
fix: Discover node last
sasjo Nov 18, 2024
164c5f9
fix: Discover baseline as qodana.sarif.json
sasjo Nov 18, 2024
3f58bee
chore: Validate default qodana.yaml
sasjo Nov 18, 2024
ffe9120
feat: Add post-step to commit qodana.yaml
sasjo Nov 19, 2024
3e2adc9
fix: Remove working-directory input
sasjo Nov 19, 2024
72b188b
feat: Push config to feature branch not only PR
sasjo Nov 20, 2024
396df17
chore: Fix test
sasjo Nov 20, 2024
3923e27
feat: Use run-checks to determine Qodana success
sasjo Nov 20, 2024
536602b
fix: Read check_runs from response
sasjo Nov 20, 2024
70b3972
chore: Update log statement
sasjo Nov 20, 2024
11983b7
fix: Log missing qodana.yaml file
sasjo Nov 20, 2024
e0a7c5e
chore: Log location of generated qodana.yaml
sasjo Nov 20, 2024
213f0a3
chore: List files in dir
sasjo Nov 20, 2024
36527ca
chore: Log project directory content
sasjo Nov 20, 2024
e25684d
fix: Remove post step
sasjo Nov 20, 2024
b4acb91
chore: Print debug
sasjo Nov 20, 2024
7fac87d
chore: Use qodana_recommended for generated file
sasjo Nov 21, 2024
76ed4d9
chore: Print yaml content
sasjo Nov 21, 2024
46a1bd1
chore: Update qodana_recommended.yaml path
sasjo Nov 21, 2024
887e6f0
feat: Add gcp-secret-manager support
sasjo Nov 21, 2024
303681e
chore: Clean up setup-qodana
sasjo Nov 21, 2024
ea16e54
feat: Rename code-owners to collection
sasjo Nov 21, 2024
55bc9e4
fix: Do not enter PR mode if qodana.yaml is new
sasjo Nov 21, 2024
6377dce
fix: Update create-project URL
sasjo Nov 22, 2024
e2e9285
feat: Support gzipped baseline files
sasjo Nov 22, 2024
617d6bd
docs: Update README
sasjo Nov 22, 2024
efaa169
chore: Log added files for debugging
sasjo Nov 22, 2024
ee32d2d
fix: Negate added-files expression
sasjo Nov 22, 2024
294efde
chore: Log added and changed files
sasjo Nov 22, 2024
931e83d
fix: Use a copy of qodana.yaml
sasjo Nov 22, 2024
82b097b
feat: Discover PR SHA in setup
sasjo Nov 25, 2024
a1ded08
feat: Set QODANA_ISSUE_NUMBER
sasjo Nov 25, 2024
9c98ba8
feat: Support skip and force comments
sasjo Nov 25, 2024
e396aba
fix: Use expression syntax
sasjo Nov 25, 2024
7ec03ac
chore: List files after run
sasjo Nov 29, 2024
d12b397
chore: Always list files
sasjo Nov 29, 2024
7b76fcd
chore: List qodana results directory
sasjo Nov 29, 2024
5b09309
feat: Init baseline during onboarding
sasjo Nov 29, 2024
ec505c8
chore: List files on failure
sasjo Nov 29, 2024
f90a1a8
feat: Automate baseline
sasjo Nov 29, 2024
29d7784
feat: Support init and rebase messages
sasjo Nov 29, 2024
5e23f14
docs: Update README [skip ci]
sasjo Nov 29, 2024
9924e27
fix: Fix bash error
sasjo Nov 29, 2024
cba9beb
feat: Use GCS managed quality baseline
sasjo Nov 29, 2024
db7a1d7
fix: Update secrets name
sasjo Nov 29, 2024
a2c7a86
fix: Handle missing gcs file
sasjo Nov 29, 2024
6000bd1
fix: Use echo -e
sasjo Nov 29, 2024
0539c5c
fix: Update GCS path
sasjo Nov 29, 2024
af4dfe6
fix: Update bucket path
sasjo Nov 29, 2024
de11e78
fix: Refactor baseline management
sasjo Nov 29, 2024
079e82e
fix: Use correct script path
sasjo Nov 29, 2024
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
1 change: 1 addition & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ updates:
- /setup-git
- /setup-msbuild
- /setup-nuget-sources
- /setup-qodana
- /setup-terraform
- /slack-notify
- /sonar-scanner
Expand Down
21 changes: 21 additions & 0 deletions .scripts/build-modules.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const { existsSync } = require('fs');
const fs = require('fs-extra');
const path = require('path');
const esbuild = require('esbuild');
Expand Down Expand Up @@ -41,6 +42,26 @@ const build = async (baseDir) => {
],
});

if (existsSync(path.join(baseDir, 'src', 'pre.js'))) {
await esbuild.build({
entryPoints: [`${srcDir}/post.js`],
platform: 'node',
bundle: true,
minify: true,
outfile: `${destDir}/post.js`,
});
}

if (existsSync(path.join(baseDir, 'src', 'post.js'))) {
await esbuild.build({
entryPoints: [`${srcDir}/post.js`],
platform: 'node',
bundle: true,
minify: true,
outfile: `${destDir}/post.js`,
});
}

console.timeEnd(`build ${baseDir}`);
};

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The following actions are available
* [pact-can-i-deploy](pact-can-i-deploy#readme)
* [pact-publish](pact-publish#readme)
* [pact-tag-version](pact-tag-version#readme)
* [quality-gate](quality-gate#readme)
* [repository-dispatch](repository-dispatch#readme)
* ~~[rs-create-installerpkg](rs-create-installerpkg#readme)~~
* ~~[rs-permission-converter](rs-permission-converter#readme)~~
Expand Down
78 changes: 78 additions & 0 deletions quality-gate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# quality-gate

This is a GitHub Action to enforce a quality gate on your software project. This a composite-action that strives to
provide a vendor-agnostic quality gate feature. Behind the scenes the quality-gate is calculated by an external
code-scanning vendor.

The goal of this action is to hide implementation details of the active quality-gate provider. It is not a goal to
support multiple vendors at the same time. When this action is implemented in a pipeline, Extenda Retail should be able
to change the underlying quality-gate implementation without impacting pipelines.

## Usage

See [action.yml](action.yml).

The quality-gate action behavior can be controlled with the follow commit messages

* `[init quality]` - Use on first run to initialize a quality baseline
* `[rebase quality]` - Rebase the quality baseline, e.g. if baseline issues has been fixed
* `[force quality]` - Force a full quality check instead of just checking changed files
* `[skip quality]` - Skip the quality gate check

### Secrets

This action can be used either with GCP Secret Manager or with GitHub Action secrets.

If this action is used with GCP Secret Manager it requires a GCP service account key with permission to access
secret payloads. Once created, the JSON key should be `base64` encoded and added as secret in the GitHub repository.

It is recommended that the service account _only_ has permissions to access secrets. Do not allow modifications or
access to any other resources in your project.

To use the action with GitHub Actions secrets, set the `QUALITY_GATE_TOKEN` environment variable with the the secret
value.

### Examples

This example showcases how a `push` based test workflow can include a quality-gate to measure both code quality and
coverage.

```yaml
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-java@v4
with:
java-version-file: .java-version
distribution: temurin
cache: maven

- uses: extenda/actions/maven@v0
with:
service-account-key: ${{ secrets.SECRET_AUTH }}
args: verify

- name: Quality Gate
uses: extenda/actions/quality-gate@v0
with:
service-account-key: ${{ secrets.SECRET_AUTH }}
collection: platform
```

If GCP Secret Manager isn't in use, pass the `QUALITY_GATE_TOKEN` as an environment variable instead of using
`service-account-key`.

```yaml
- name: Quality Gate
uses: extenda/actions/quality-gate@v0
with:
collection: platform
env:
QUALITY_GATE_TOKEN: ${{ secrets.QUALITY_GATE_TOKEN }}
```
97 changes: 97 additions & 0 deletions quality-gate/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: Quality Gate
description: Enforce a quality gate on your software project.
inputs:
service-account-key:
description: |
The service account key which will be used to access CI/CD pipeline secrets and
the storage bucket if data management is enabled.

This must be set unless the `QUALITY_GATE_TOKEN` environment variable is set
with the access token for the service provider.
default: ''
github-token:
description: The GitHub Actions token.
default: ${{ github.token }}
collection:
description: |
The collection name under which the project will be sorted. This can be a
team name or a category. This can be used to organize projects within
the quality gate reporting dashboards.
required: true
storage-bucket:
description: |
The GCS bucket used to store quality gate data. Set this to a blank string to
disable any data management performed by this action.
default: quality-gate-data
runs:
using: composite
steps:
- name: Resolve quality gate access token
if: env.QUALITY_GATE_TOKEN == ''
uses: extenda/actions/gcp-secret-manager@v0
with:
service-account-key: ${{ inputs.service-account-key }}
secrets: |
QUALITY_GATE_TOKEN: qodana-organization-token

- name: Setup gcloud
uses: extenda/actions/setup-gcloud@v0
if: inputs.service-account-key != ''
with:
service-account-key: ${{ inputs.service-account-key }}
export-default-credentials: 'true'

- name: Download quality baseline from GCS
if: ${{ inputs.service-account-key != '' && inputs.storage-bucket != '' && !contains(github.event.head_commit.message, '[skip quality]') }}
run: |
gcloud storage cp \
gs://${{ inputs.storage-bucket }}/qodana/${{ github.repository }}/qodana-sarif.json \
managed-qodana.sarif.json || true
shell: bash

- name: Setup Qodana
uses: extenda/actions/setup-qodana@feat/quality-gate
id: setup-qodana
with:
qodana-token: ${{ env.QUALITY_GATE_TOKEN }}
qodana-team: ${{ inputs.collection }}
github-token: ${{ inputs.github-token }}

- name: Scan with Qodana
uses: extenda/qodana-action@feat/issue-number-discovery
if: ${{ !contains(github.event.head_commit.message, '[skip quality]') }}
with:
args: ${{ steps.setup-qodana.outputs.args }}
github-token: ${{ inputs.github-token }}
pr-mode: ${{ steps.setup-qodana.outputs.pr-mode }}
env:
QODANA_TOKEN: ${{ steps.setup-qodana.outputs.project-token }}

- name: Init quality gate baseline
if: failure() && inputs.service-account-key != '' && inputs.storage-bucket != '' && contains(github.event.head_commit.message, '[init quality]')
run: |
generated_baseline="${{ runner.temp }}/qodana/results/qodana.sarif.json"
gcs_path="gs://${{ inputs.storage-bucket }}/qodana/${{ github.repository }}/qodana-sarif.json"
${{ github.action_path }}/update-qodana-baseline.sh "$generated_baseline" "$gcs_path"

echo ""
echo -e "\033[01;31mQUALITY-GATE BASELINE CREATED - RERUN JOB\033[00m"
echo ""
echo "The quality-gate baseline has been created."
echo "A rerun of this job should now pass the quality-gate check."
echo ""
shell: bash

- name: Rebase quality gate baseline
if: always() && inputs.service-account-key != '' && inputs.storage-bucket != '' && contains(github.event.head_commit.message, '[rebase quality]')
run: |
generated_baseline="${{ runner.temp }}/qodana/results/qodana.sarif.json"
gcs_path="gs://${{ inputs.storage-bucket }}/qodana/${{ github.repository }}/qodana-sarif.json"
${{ github.action_path }}/update-qodana-baseline.sh "$generated_baseline" "$gcs_path"

echo ""
echo -e "\033[01;31mQUALITY-GATE BASELINE REBASED\033[00m"
echo ""
echo "The quality-gate baseline has been rebased."
echo ""
shell: bash
15 changes: 15 additions & 0 deletions quality-gate/update-qodana-baseline.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env sh

generated_baseline="$1"
gcs_path="$2"

if [ -f qodana.sarif.json ]; then
echo "User-provided qodana.sarif.json exists. Baseline automation disabled."
echo "rerun=false" >> "$GITHUB_OUTPUT"
exit 0
fi

if [ -f "$generated_baseline" ]; then
gcloud storage cp "$generated_baseline" "$gcs_path"
echo "rerun=true" >> "$GITHUB_OUTPUT"
fi
26 changes: 26 additions & 0 deletions setup-qodana/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# setup-qodana

Setup Qodana for use with the Quality Gate Action. This action will discover qodana arguments and output them for use in
the [quality-gate](../quality-gate#readme). This action is not meant to be used directly, but rather as a part of the
quality-gate composite.

This action supports the following

* Project types
* TypeScript and JavaScript
* JVM Languages (Java, Kotlin)
* .NET Core (C#)
* Qodana Cloud project
* Create project and project-token
* Detect coverage directory
* lcov
* Jacoco XML
* Detect `qodana.sarif.json`
* Decompress gzipped `qodana.sarif.json.gz` if detected
* Sanity-check `qodana.yaml`
* Generate `qodana.yaml` file if missing
* Validate and enforce quality gate metrics
* Fresh code
* Issue count
* Push `qodana.yaml` and `qodana.sarif.json` to auto-configure Qodana on first run in a branch that isn't the default
branch. The baseline will only be included if the quality gate failed.
32 changes: 32 additions & 0 deletions setup-qodana/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Setup Qodana
description: Setup and configure Qodana for use as a Quality Gate.
inputs:
qodana-token:
description: The Qodana organization access token.
required: true
qodana-team:
description: The Qodana team name used to organize projects.
required: true
project-directory:
description: Root directory of the project (default '.')
default: '.'
github-token:
description: The GitHub Actions token.
default: ${{ github.token }}
outputs:
baseline:
description: The baseline.sarif.json file.
coverage-dir:
description: The directory containing coverage reports.
args:
description: The comma-separated arguments to pass to the qodana action.
project-token:
description: The qodana project token.
pr-mode:
description: Indicates if Qodana should run in pull request mode and do a diff-analysis.
update-baseline:
description: Indicates that the qodana.sarif.json baseline should updated after the run.
runs:
using: node20
main: dist/index.js
# post: dist/post.js
332 changes: 332 additions & 0 deletions setup-qodana/dist/index.js

Large diffs are not rendered by default.

229 changes: 229 additions & 0 deletions setup-qodana/dist/post.js

Large diffs are not rendered by default.

Loading