mirror of
https://github.com/jackyzha0/quartz.git
synced 2025-12-28 07:14:05 -06:00
648 lines
17 KiB
Markdown
648 lines
17 KiB
Markdown
Example yaml file name must be **.gitlab-ci.yaml**
|
|
|
|
```yaml
|
|
stages:
|
|
# - test
|
|
- build
|
|
- deploy
|
|
|
|
pre-job:
|
|
stage: .pre
|
|
script:
|
|
- echo 'This message is from .pre-job'
|
|
|
|
build-job:
|
|
stage: build
|
|
script:
|
|
- echo 'This message is from build-job'
|
|
|
|
test-job:
|
|
stage: test
|
|
script:
|
|
- echo 'This message is from test-job'
|
|
|
|
deploy-job:
|
|
stage: deploy
|
|
script:
|
|
- echo 'This message is from deploy-job'
|
|
|
|
post-job:
|
|
stage: .post
|
|
script:
|
|
- echo 'This message is from .post-job'
|
|
```
|
|
![[Screenshot from 2023-03-14 11-12-33.png]]
|
|
|
|
Default stages use default order other than that you can use
|
|
```yaml
|
|
stages:
|
|
-test1
|
|
-test2
|
|
-test3
|
|
```
|
|
|
|
Example:
|
|
```yaml
|
|
stages:
|
|
- build
|
|
- deploy
|
|
|
|
build:
|
|
image: node
|
|
stage: build
|
|
script:
|
|
# - apt update -y
|
|
# - apt install npm -y
|
|
- npm install
|
|
artifacts:
|
|
paths:
|
|
- node_modules
|
|
- package-lock.json
|
|
# expire_in: 1 week
|
|
|
|
deploy:
|
|
image: node
|
|
stage: deploy
|
|
script:
|
|
# - apt update -y
|
|
# - apt install nodejs -y
|
|
- node index.js > /dev/null 2>&1 & # these command runs in background and does not effect timeout
|
|
```
|
|
|
|
|
|
## Gitlab Runners
|
|
|
|
Application that works for picking CI/CD and execute CI/CD jobs.
|
|
|
|
|
|
Settings > shared runners or specific runners
|
|
|
|
Runners has tag like docker mongodb ruby. That means which can runners can handle.
|
|
|
|
for example windows tag we can use in our yaml.
|
|
```yaml
|
|
windows-info:
|
|
tags:
|
|
- windows
|
|
script:
|
|
- systeminfo
|
|
```
|
|
|
|
Runner must be same version with gitlab.
|
|
|
|
**sudo gitlab-runner register** for register runner. You can take runner token from setttings
|
|
|
|
run gitlab-runner locally
|
|
```yaml
|
|
stages:
|
|
- build_stage
|
|
- deploy_stage
|
|
|
|
build:
|
|
stage: build_stage
|
|
script:
|
|
- docker --version
|
|
- docker build -t pyapp .
|
|
tags:
|
|
- localshell
|
|
- localrunner
|
|
|
|
deploy:
|
|
stage: deploy_stage
|
|
script:
|
|
- docker stop pyappcontainer1 || true && docker rm pyappcontainer1 || true
|
|
- docker run -d --name pyappcontainer1 -p 8080:8080 pyapp
|
|
tags:
|
|
- localshell
|
|
- localrunner
|
|
```
|
|
|
|
Git-runner add admin group ==> sudo usermod -aG docker gitlab-runner
|
|
|
|
![[Screenshot from 2023-03-14 13-08-06.png]]
|
|
|
|
Variables ==> use security token,url , long string etc.
|
|
Gitlab variable [url](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html)
|
|
|
|
predefine
|
|
```yaml
|
|
demo_job:
|
|
script:
|
|
- echo $CI_COMMIT_MESSAGE
|
|
- echo $CI_JOB_NAME
|
|
```
|
|
|
|
direcly set in yaml
|
|
```yaml
|
|
variables:
|
|
name: 'John'
|
|
message: 'How are you?'
|
|
|
|
display_message:
|
|
variables:
|
|
name: 'Mark'
|
|
script:
|
|
- echo "Hello $name, $message"
|
|
```
|
|
|
|
secret variable
|
|
|
|
```yaml
|
|
push_image:
|
|
script:
|
|
- docker login -u $USERNAME -p $PASSWORD
|
|
- docker tag pyapp:latest $USERNAME/mypyapp:latest
|
|
- docker push $USERNAME/mypyapp:latest
|
|
tags:
|
|
- localshell
|
|
- localrunner
|
|
```
|
|
|
|
# Enviroments
|
|
```yaml
|
|
stages:
|
|
- test
|
|
- build
|
|
- deploy staging
|
|
- automated testing
|
|
- deploy production
|
|
|
|
variables:
|
|
IMAGE_TAG: $CI_REGISTRY_IMAGE/employee-image:$CI_COMMIT_SHORT_SHA
|
|
STAGING_APP: emp-portal-staging
|
|
PRODUCTION_APP: emp-portal-production
|
|
|
|
HEROKU_STAGING: "registry.heroku.com/$STAGING_APP/web"
|
|
HEROKU_PRODUCTION: "registry.heroku.com/$PRODUCTION_APP/web"
|
|
|
|
|
|
lint_test:
|
|
image: python:3.8.0-slim
|
|
stage: test
|
|
before_script:
|
|
- pip install flake8-html
|
|
script:
|
|
- flake8 --format=html --htmldir=flake_reports/
|
|
artifacts:
|
|
when: always
|
|
paths:
|
|
- flake_reports/
|
|
|
|
pytest:
|
|
image: python:3.8.0-slim
|
|
stage: test
|
|
before_script:
|
|
- pip install pytest-html
|
|
- pip install -r requirements.txt
|
|
script:
|
|
- pytest --html=pytest_reports/pytest-report.html --self-contained-html
|
|
artifacts:
|
|
when: always
|
|
paths:
|
|
- pytest_reports/
|
|
|
|
build:
|
|
image: docker:latest
|
|
services:
|
|
- docker:dind
|
|
stage: build
|
|
before_script:
|
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
script:
|
|
- docker build -t $IMAGE_TAG .
|
|
- docker images
|
|
- docker push $IMAGE_TAG
|
|
|
|
deploy_stage:
|
|
image: docker:latest
|
|
services:
|
|
- docker:dind
|
|
stage: deploy staging
|
|
environment:
|
|
name: staging
|
|
url: https://$STAGING_APP.herokuapp.com/
|
|
before_script:
|
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
script:
|
|
- docker pull $IMAGE_TAG
|
|
- docker tag $IMAGE_TAG $HEROKU_STAGING
|
|
- docker login -u _ -p $HEROKU_STAGING_API_KEY registry.heroku.com
|
|
- docker push $HEROKU_STAGING
|
|
- docker run --rm -e HEROKU_API_KEY=$HEROKU_STAGING_API_KEY wingrunr21/alpine-heroku-cli container:release web --app $STAGING_APP
|
|
- echo "App deployed to stagig server at https://$STAGING_APP.herokuapp.com/"
|
|
only:
|
|
- main
|
|
|
|
test_stage:
|
|
image: alpine
|
|
stage: automated testing
|
|
before_script:
|
|
- apk --no-cache add curl
|
|
script:
|
|
- curl https://$STAGING_APP.herokuapp.com/ | grep "Employee Data"
|
|
only:
|
|
- main
|
|
|
|
deploy_production:
|
|
image: docker:latest
|
|
services:
|
|
- docker:dind
|
|
stage: deploy production
|
|
environment:
|
|
name: production
|
|
url: https://$PRODUCTION_APP.herokuapp.com/
|
|
before_script:
|
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
script:
|
|
- docker pull $IMAGE_TAG
|
|
- docker tag $IMAGE_TAG $HEROKU_PRODUCTION
|
|
- docker login -u _ -p $HEROKU_PRODUCTION_API_KEY registry.heroku.com
|
|
- docker push $HEROKU_PRODUCTION
|
|
- docker run --rm -e HEROKU_API_KEY=$HEROKU_PRODUCTION_API_KEY wingrunr21/alpine-heroku-cli container:release web --app $PRODUCTION_APP
|
|
- echo "App deployed to production server at https://$PRODUCTION_APP.herokuapp.com/"Project - deploy to production
|
|
only:
|
|
- main
|
|
```
|
|
|
|
environment:
|
|
name: production
|
|
url: https://$PRODUCTION_APP.herokuapp.com/
|
|
|
|
# Dynamic enviroments
|
|
|
|
https://gitlab.com/gitlab-org/gitlab-runner/-/issues/1809
|
|
|
|
|
|
```yaml
|
|
stages:
|
|
- test
|
|
- build
|
|
- deploy feature
|
|
- automated feature testing
|
|
- deploy staging
|
|
- automated testing
|
|
- deploy production
|
|
|
|
variables:
|
|
IMAGE_TAG: $CI_REGISTRY_IMAGE/employee-image:$CI_COMMIT_SHORT_SHA
|
|
STAGING_APP: emp-portal-staging
|
|
PRODUCTION_APP: emp-portal-production
|
|
|
|
HEROKU_STAGING: "registry.heroku.com/$STAGING_APP/web"
|
|
HEROKU_PRODUCTION: "registry.heroku.com/$PRODUCTION_APP/web"
|
|
|
|
|
|
lint_test:
|
|
image: python:3.8.0-slim
|
|
stage: test
|
|
before_script:
|
|
- pip install flake8-html
|
|
script:
|
|
- flake8 --format=html --htmldir=flake_reports/
|
|
artifacts:
|
|
when: always
|
|
paths:
|
|
- flake_reports/
|
|
|
|
pytest:
|
|
image: python:3.8.0-slim
|
|
stage: test
|
|
before_script:
|
|
- pip install pytest-html
|
|
- pip install -r requirements.txt
|
|
script:
|
|
- pytest --html=pytest_reports/pytest-report.html --self-contained-html
|
|
artifacts:
|
|
when: always
|
|
paths:
|
|
- pytest_reports/
|
|
|
|
build:
|
|
image: docker:latest
|
|
services:
|
|
- docker:dind
|
|
stage: build
|
|
before_script:
|
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
script:
|
|
- docker build -t $IMAGE_TAG .
|
|
- docker images
|
|
- docker push $IMAGE_TAG
|
|
|
|
deploy_feature:
|
|
image: docker:latest
|
|
services:
|
|
- docker:dind
|
|
stage: deploy feature
|
|
environment:
|
|
name: review/$CI_COMMIT_REF_NAME
|
|
url: https://$CI_ENVIRONMENT_SLUG.herokuapp.com/
|
|
before_script:
|
|
- export FEATURE_APP="$CI_ENVIRONMENT_SLUG"
|
|
- export HEROKU_FEATURE="registry.heroku.com/$FEATURE_APP/web"
|
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
script:
|
|
- echo "FEATURE_APP=$CI_ENVIRONMENT_SLUG" >> deploy_feature.env
|
|
- docker pull $IMAGE_TAG
|
|
- docker tag $IMAGE_TAG $HEROKU_FEATURE
|
|
- docker run --rm -e HEROKU_API_KEY=$HEROKU_STAGING_API_KEY wingrunr21/alpine-heroku-cli create $FEATURE_APP
|
|
- docker login -u _ -p $HEROKU_STAGING_API_KEY registry.heroku.com
|
|
- docker push $HEROKU_FEATURE
|
|
- docker run --rm -e HEROKU_API_KEY=$HEROKU_STAGING_API_KEY wingrunr21/alpine-heroku-cli container:release web --app $FEATURE_APP
|
|
- echo "App deployed to FEATURE server at https://$FEATURE_APP.herokuapp.com/"
|
|
artifacts:
|
|
reports:
|
|
dotenv: deploy_feature.env
|
|
only:
|
|
- /^feature-.*$/
|
|
|
|
test_feature:
|
|
image: alpine
|
|
stage: automated feature testing
|
|
before_script:
|
|
- apk --no-cache add curl
|
|
script:
|
|
- curl https://$FEATURE_APP.herokuapp.com/ | grep "Employee Data"
|
|
dependencies:
|
|
- deploy_feature
|
|
only:
|
|
- /^feature-.*$/
|
|
|
|
deploy_stage:
|
|
image: docker:latest
|
|
services:
|
|
- docker:dind
|
|
stage: deploy staging
|
|
environment:
|
|
name: staging
|
|
url: https://$STAGING_APP.herokuapp.com/
|
|
before_script:
|
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
script:
|
|
- docker pull $IMAGE_TAG
|
|
- docker tag $IMAGE_TAG $HEROKU_STAGING
|
|
- docker login -u _ -p $HEROKU_STAGING_API_KEY registry.heroku.com
|
|
- docker push $HEROKU_STAGING
|
|
- docker run --rm -e HEROKU_API_KEY=$HEROKU_STAGING_API_KEY wingrunr21/alpine-heroku-cli container:release web --app $STAGING_APP
|
|
- echo "App deployed to stagig server at https://$STAGING_APP.herokuapp.com/"
|
|
only:
|
|
- main
|
|
|
|
test_stage:
|
|
image: alpine
|
|
stage: automated testing
|
|
before_script:
|
|
- apk --no-cache add curl
|
|
script:
|
|
- curl https://$STAGING_APP.herokuapp.com/ | grep "Employee Data"
|
|
only:
|
|
- main
|
|
|
|
deploy_production:
|
|
image: docker:latest
|
|
services:
|
|
- docker:dind
|
|
stage: deploy production
|
|
environment:
|
|
name: production
|
|
url: https://$PRODUCTION_APP.herokuapp.com/
|
|
before_script:
|
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
script:
|
|
- docker pull $IMAGE_TAG
|
|
- docker tag $IMAGE_TAG $HEROKU_PRODUCTION
|
|
- docker login -u _ -p $HEROKU_PRODUCTION_API_KEY registry.heroku.com
|
|
- docker push $HEROKU_PRODUCTION
|
|
- docker run --rm -e HEROKU_API_KEY=$HEROKU_PRODUCTION_API_KEY wingrunr21/alpine-heroku-cli container:release web --app $PRODUCTION_APP
|
|
- echo "App deployed to production server at https://$PRODUCTION_APP.herokuapp.com/"Project - deploy to production
|
|
only:
|
|
- main
|
|
when: manual
|
|
|
|
```
|
|
|
|
|
|
# GitLab DevSecOps
|
|
|
|
SAST sonar cloud.
|
|
```yaml
|
|
stages:
|
|
- runSAST
|
|
|
|
run-sast-job:
|
|
stage: runSAST
|
|
image: maven:3.8.5-openjdk-11-slim
|
|
script: |
|
|
mvn verify package sonar:sonar -Dsonar.host.url=https://sonarcloud.io/ -Dsonar.organization=gitlabdevsecopsintegration -Dsonar.projectKey=gitlabdevsecopsintegration -Dsonar.login=token01
|
|
```
|
|
==================================================================
|
|
Sonar cloud quality gateways
|
|
```
|
|
1) Create Custom Quality Gate in SonarCloud and Add conditions to the Quality Gate
|
|
2) Assign this Quality Gate to the Project
|
|
3) Add script in .gitlab-ci.yml file to enable quality gate check (Note: This will fail your build in case Quality Gate fails)
|
|
|
|
sleep 5
|
|
apt-get update
|
|
apt-get -y install curl jq
|
|
quality_status=$(curl -s -u 14ad4797c02810a818f21384add02744d3f9e34d: https://sonarcloud.io/api/qualitygates/project_status?projectKey=gitLabdevsecopsintegration | jq -r '.projectStatus.status')
|
|
echo "SonarCloud Analysis Status is $quality_status";
|
|
if [[ $quality_status == "ERROR" ]] ; then exit 1;fi
|
|
|
|
|
|
-----------Sample JSON Response from SonarCloud or SonarQube Quality Gate API---------------------
|
|
|
|
{
|
|
"projectStatus": {
|
|
"status": "ERROR",
|
|
"conditions": [
|
|
{
|
|
"status": "ERROR",
|
|
"metricKey": "coverage",
|
|
"comparator": "LT",
|
|
"errorThreshold": "90",
|
|
"actualValue": "0.0"
|
|
}
|
|
],
|
|
"periods": [],
|
|
"ignoredConditions": false
|
|
}
|
|
}
|
|
```
|
|
|
|
```yaml
|
|
stages:
|
|
- runSAST
|
|
|
|
run-sast-job:
|
|
stage: runSAST
|
|
image: maven:3.8.5-openjdk-11-slim
|
|
script: |
|
|
apt-get update
|
|
apt-get -y install curl jq
|
|
mvn verify package sonar:sonar -Dsonar.host.url=https://sonarcloud.io/ -Dsonar.organization=gitlabdevsecopsintegrtion -Dsonar.projectKey=gitLabdevsecopsintegration -Dsonar.login=14ad4797c02810a818f21384add02744d3f9e34d
|
|
sleep 5
|
|
quality_status=$(curl -s -u 14ad4797c02810a818f21384add02744d3f9e34d: https://sonarcloud.io/api/qualitygates/project_status?projectKey=gitLabdevsecopsintegration | jq -r '.projectStatus.status')
|
|
echo "SonarCloud Analysis Status is $quality_status";
|
|
if [[ $quality_status == "ERROR" ]] ; then exit 1;fi
|
|
```
|
|
==================================================================
|
|
Test coverage
|
|
```
|
|
1) Unit Test cases should be present in test folder
|
|
2) Junit Plugin should be present in pom.xml
|
|
3) Jacoco Plugin should be present in pom.xml
|
|
4) Jacoco report execution goal should be present in build tag in pom.xml
|
|
5) Maven "verify" goal should be run while running sonar analysis
|
|
```
|
|
|
|
```yaml
|
|
stages:
|
|
- runSAST
|
|
|
|
run-sast-job:
|
|
stage: runSAST
|
|
image: maven:3.8.5-openjdk-11-slim
|
|
script: |
|
|
mvn verify package sonar:sonar -Dsonar.host.url=https://sonarcloud.io/ -Dsonar.organization=gitlabdevsecopsintegration -Dsonar.projectKey=gitlabdevsecopsintegration -Dsonar.login=2fda8f4a1af600afbede42c54c868083d8e34c01
|
|
```
|
|
==================================================================
|
|
SCA in gitlab security
|
|
|
|
Steps to integrate Snyk using .gitlab-ci.yml file:
|
|
|
|
1) Add Snyk Plugin to Pom.xml
|
|
2) Define Snyk Token as an environment Variable on the runner machine
|
|
3) Add code changes to .gitlab-ci.yml file
|
|
|
|
```yaml
|
|
stages:
|
|
- runSCAScanUsingSnyk
|
|
|
|
run-sca-job:
|
|
stage: runSCAScanUsingSnyk
|
|
image: maven:3.8.5-openjdk-11-slim
|
|
script: |
|
|
SNYK_TOKEN='2f4afa39-c493-4c6d-b34e-080c1a8f9014'
|
|
export SNYK_TOKEN
|
|
mvn snyk:test -fn
|
|
```
|
|
|
|
==================================================================
|
|
|
|
DAST tool using OWASP ZAP
|
|
|
|
```yaml
|
|
stages:
|
|
- runDASTScanUsingZAP
|
|
|
|
run-dast-job:
|
|
stage: runDASTScanUsingZAP
|
|
image: maven:3.8.5-openjdk-11-slim
|
|
script: |
|
|
apt-get update
|
|
apt-get -y install wget
|
|
wget https://github.com/zaproxy/zaproxy/releases/download/v2.11.1/ZAP_2.11.1_Linux.tar.gz
|
|
mkdir zap
|
|
tar -xvf ZAP_2.11.1_Linux.tar.gz
|
|
cd ZAP_2.11.1
|
|
./zap.sh -cmd -quickurl https://www.example.com -quickprogress -quickout ../zap_report.html
|
|
artifacts:
|
|
paths:
|
|
- zap_report.html
|
|
```
|
|
==================================================================
|
|
End to end CI/CD pipeline for java projects
|
|
|
|
|
|
```yaml
|
|
stages:
|
|
- runSASTScanUsingSonarCloud
|
|
- runSCAScanUsingSnyk
|
|
- runDASTScanUsingZAP
|
|
|
|
run-sast-job:
|
|
stage: runSASTScanUsingSonarCloud
|
|
image: maven:3.8.5-openjdk-11-slim
|
|
script: |
|
|
mvn verify package sonar:sonar -Dsonar.host.url=https://sonarcloud.io/ -Dsonar.organization=gitlabdevsecopsintegration -Dsonar.projectKey=gitlabdevsecopsintegration -Dsonar.login=2fda8f4a1af600afbede42c54c868083d8e34c01
|
|
|
|
run-sca-job:
|
|
stage: runSCAScanUsingSnyk
|
|
image: maven:3.8.5-openjdk-11-slim
|
|
script: |
|
|
SNYK_TOKEN='2f4afa39-c493-4c6d-b34e-080c1a8f9014'
|
|
export SNYK_TOKEN
|
|
mvn snyk:test -fn
|
|
|
|
run-dast-job:
|
|
stage: runDASTScanUsingZAP
|
|
image: maven:3.8.5-openjdk-11-slim
|
|
script: |
|
|
apt-get update
|
|
apt-get -y install wget
|
|
wget https://github.com/zaproxy/zaproxy/releases/download/v2.11.1/ZAP_2.11.1_Linux.tar.gz
|
|
mkdir zap
|
|
tar -xvf ZAP_2.11.1_Linux.tar.gz
|
|
cd ZAP_2.11.1
|
|
./zap.sh -cmd -quickurl https://www.example.com -quickprogress -quickout ../zap_report.html
|
|
artifacts:
|
|
paths:
|
|
- zap_report.html
|
|
```
|
|
|
|
# Gitlab buildin SAST and DAST
|
|
GitLab SAST Analyzer Documentation Page: https://docs.gitlab.com/ee/user/application_security/sast/
|
|
|
|
|
|
GitLab DAST Analyzer Documentation Page: https://docs.gitlab.com/ee/user/application_security/dast/
|
|
|
|
```yaml
|
|
include:
|
|
- template: Security/SAST.gitlab-ci.yml
|
|
- template: DAST.gitlab-ci.yml
|
|
|
|
variables:
|
|
SAST_EXPERIMENTAL_FEATURES: "true"
|
|
DAST_WEBSITE: http://www.example.com
|
|
DAST_FULL_SCAN_ENABLED: "true"
|
|
DAST_BROWSER_SCAN: "true"
|
|
|
|
stages:
|
|
- test
|
|
- runSASTScanUsingSonarCloud
|
|
- runSCAScanUsingSnyk
|
|
- runDASTScanUsingZAP
|
|
- dast
|
|
|
|
run-sast-job:
|
|
stage: runSASTScanUsingSonarCloud
|
|
image: maven:3.8.5-openjdk-11-slim
|
|
script: |
|
|
mvn verify package sonar:sonar -Dsonar.host.url=https://sonarcloud.io/ -Dsonar.organization=gitlabdevsecopsintegrationkey -Dsonar.projectKey=gitlabdevsecopsintegrationkey -Dsonar.login=9ff892826b54980437f4fb0fbc72f4049ec97585
|
|
|
|
run-sca-job:
|
|
stage: runSCAScanUsingSnyk
|
|
image: maven:3.8.5-openjdk-11-slim
|
|
script: |
|
|
SNYK_TOKEN='2f4afa39-c493-4c6d-b34e-080c1a8f9014'
|
|
export SNYK_TOKEN
|
|
mvn snyk:test -fn
|
|
|
|
run-dast-job:
|
|
stage: runDASTScanUsingZAP
|
|
image: maven:3.8.5-openjdk-11-slim
|
|
script: |
|
|
apt-get update
|
|
apt-get -y install wget
|
|
wget https://github.com/zaproxy/zaproxy/releases/download/v2.11.1/ZAP_2.11.1_Linux.tar.gz
|
|
mkdir zap
|
|
tar -xvf ZAP_2.11.1_Linux.tar.gz
|
|
cd ZAP_2.11.1
|
|
./zap.sh -cmd -quickurl https://www.example.com -quickprogress -quickout ../zap_report.html
|
|
artifacts:
|
|
paths:
|
|
- zap_report.html
|
|
```
|
|
|