From 78e63488e1fa0d3fe998edea0e606f5f697a7afb Mon Sep 17 00:00:00 2001 From: Albert Tregnaghi Date: Fri, 26 Jan 2024 07:28:24 +0100 Subject: [PATCH] Automated integration test setup #2839 - switched setup, start, stop parts for integration testing to bash scripts only - improved integration test setup parts - changed CI/CD build behaviors for "feature-" and "gha_feature-*" branches - added documentation --- .github/workflows/github-action-scan.yml | 47 +++++- .github/workflows/gradle.yml | 4 +- github-actions/scan/README.adoc | 36 ++++- .../scan/__test__/init-scan.test.ts | 2 +- .../scan/__test__/integrationtest.test.ts | 39 +++-- .../scan/__test__/integrationtest/01-start.sh | 147 ++++++++++++++++++ .../scan/__test__/integrationtest/05-stop.sh | 27 ++++ .../init_sechub_data.sh} | 7 +- .../integrationtest/pds-codescan-demo.sh | 2 + .../integrationtest/pds-config-template.json | 11 ++ .../__test__/integrationtest/start_pds.sh | 52 +++++++ .../integrationtest/start_sechub_server.sh | 48 ++++++ .../scan/__test__/integrationtest/stop_pds.sh | 13 ++ .../integrationtest/stop_sechub_server.sh | 13 ++ .../__test__/integrationtest/testframework.ts | 28 ++++ .../integrationtest/wait_pds_alive.sh | 28 ++++ .../wait_sechub_server_alive.sh | 28 ++++ github-actions/scan/package.json | 7 +- github-actions/scan/src/client-download.ts | 12 +- github-actions/scan/src/fs-helper.ts | 32 +++- .../documents/techdoc/01_development.adoc | 6 +- 21 files changed, 540 insertions(+), 49 deletions(-) create mode 100755 github-actions/scan/__test__/integrationtest/01-start.sh create mode 100755 github-actions/scan/__test__/integrationtest/05-stop.sh rename github-actions/scan/__test__/{setup-integrationtest.sh => integrationtest/init_sechub_data.sh} (90%) create mode 100755 github-actions/scan/__test__/integrationtest/pds-codescan-demo.sh create mode 100644 github-actions/scan/__test__/integrationtest/pds-config-template.json create mode 100755 github-actions/scan/__test__/integrationtest/start_pds.sh create mode 100755 github-actions/scan/__test__/integrationtest/start_sechub_server.sh create mode 100755 github-actions/scan/__test__/integrationtest/stop_pds.sh create mode 100755 github-actions/scan/__test__/integrationtest/stop_sechub_server.sh create mode 100644 github-actions/scan/__test__/integrationtest/testframework.ts create mode 100755 github-actions/scan/__test__/integrationtest/wait_pds_alive.sh create mode 100755 github-actions/scan/__test__/integrationtest/wait_sechub_server_alive.sh diff --git a/.github/workflows/github-action-scan.yml b/.github/workflows/github-action-scan.yml index 77114d385d..23ba99f169 100644 --- a/.github/workflows/github-action-scan.yml +++ b/.github/workflows/github-action-scan.yml @@ -1,7 +1,10 @@ # SPDX-License-Identifier: MIT -name: Build SecHub GitHub Action +name: Build SecHub GHA (scan) -on: workflow_dispatch +on: + push: + branches: + - 'gha_*' jobs: build-scan: @@ -20,13 +23,47 @@ jobs: # which should be the default environment for the github actions runtime as well uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 + - name: Set up JDK 17 + uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 + with: + java-version: 17 + distribution: temurin + - name: Clean install run: npm ci - name: Build run: npm run build --if-present - - name: Run tests + - name: Run unit tests run: npm test - - + + - name: Setup integration test data + id : version-selector + run: | + echo "sechub_server_version=1.4.1" >> "$GITHUB_ENV" + echo "sechub_server_port=8443" >> "$GITHUB_ENV" + echo "pds_version=1.4.0" >> "$GITHUB_ENV" + echo "pds_port=8444" >> "$GITHUB_ENV" + + - name: Cache SecHub server download + # Cache V4 release: 13aacd865c20de90d75de3b17ebe84f7a17d57d2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 + with: + path: ~/runtime/server/${{ steps.version-selector.outputs.sechub_server_version }}/ + key: ${{ runner.os }}-sechub-server-${{ steps.version-selector.outputs.sechub_server_version }} + + - name: Cache PDS download + # Cache V4 release: 13aacd865c20de90d75de3b17ebe84f7a17d57d2 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 + with: + path: ~/runtime/pds/${{ steps.version-selector.outputs.pds_version }}/ + key: ${{ runner.os }}-pds-${{ steps.version-selector.outputs.pds_version }} + - name: Prepare for integration tests + run: ./__test__/integrationtest/01-start.sh $sechub_server_version $sechub_server_port $pds_version $pds_port + + - name: Run integration tests + run: npm run integration-test + + - name: Cleanup integration tests + run: ./__test__/integrationtest/05-stop.sh 8443 8444 diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 342f17fa18..79250bddcc 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -9,8 +9,10 @@ on: # So this branch is only for github pages. See https://github.com/mercedes-benz/sechub/issues/481 # for details - documentation - # We ignore everything where tag starts with v* - this is done by release build! + # ignore branches for github actions only - e.g. "gha_feature-12345-testfeature" + - gha_* tags-ignore: + # We ignore everything where tag starts with v* - this is done by release build! - v* # enable manual triggering of workflow workflow_dispatch: diff --git a/github-actions/scan/README.adoc b/github-actions/scan/README.adoc index 822eeca8eb..5878cc43fa 100644 --- a/github-actions/scan/README.adoc +++ b/github-actions/scan/README.adoc @@ -9,8 +9,8 @@ This GitHub action uses the SecHub cli to scan the repository for security issue To be able to use this action you need a SecHub project. Check the https://mercedes-benz.github.io/sechub/[documentation] on how to set one up. -```yaml - +[source,yaml] +---- - uses: mercedes-benz/sechub/github-actions/scan@72a27282da80952e6fadcef452c6a9085971c688 with: # OPTIONAL: Path to sechub.json for manual configuration. If no value is set the input parameters will be used to create it for the scan.' @@ -100,19 +100,43 @@ npm run test ==== Integration-Test As a precondition to run the integration tests locally you have to -- start a new SecHub server in integration test mode -- execute `__test__/setup-integrationtest.sh` +- execute `__test__/01-start.sh $secHubServerVersion $sechubServerPortNr $pdsVersion $pdsPortNr` -When this has been done, you can execute this command multiple times: +After the script has been executed, you can execute integration tests multiple times via following command: [source,npm] ---- npm run integration-test ---- -To enable full debug output: +To enable full debug output in integration tests please execute following before running the integration tests: [source,npm] ---- export SECHUB_DEBUG=true ---- +==== Debug tests +The unit and also the integration tests are written with `jest` test framework. + +===== Setup + +**VSCodium** +Used extensions +- Test explorer +- Jest Test explorer +- Jest + +In this setup the tests can be executed from sidebar and from links created inside editor. + +But ... unfortunately, the Jest UI integration works only for npm script "test" which is normally not used for +integration tests (to handle them special). + +If you want to debug an integration test, there is a workaround necessary: + +- open `package.json` and look into section `scripts` +- switch `test-debug-all` to `test` and name former `test` script entry to `test-debug-not-integrationtest` + (but please do not push this - otherwise build will fail on integration test!) +- restart your VSCode/VSCodium instance (if integration tests are not listed in test explorer) +- debug the parts, fix it etc. +- switch `test` script entry to `test-debug-all` and `test-debug-not-integrationtest` to `test`. +- if necessary push fixes/changes to remote... diff --git a/github-actions/scan/__test__/init-scan.test.ts b/github-actions/scan/__test__/init-scan.test.ts index 80d224e35b..99de8a7368 100644 --- a/github-actions/scan/__test__/init-scan.test.ts +++ b/github-actions/scan/__test__/init-scan.test.ts @@ -23,7 +23,7 @@ describe('initSecHubJson', function () { const parameter = initSecHubJson('runtime/sechub.json','', [], []); /* test */ - expect(parameter).toBeNull(); + expect(parameter).toEqual('runtime/sechub.json'); expect(createSecHubConfigJsonFile).toHaveBeenCalledTimes(1); }); }); diff --git a/github-actions/scan/__test__/integrationtest.test.ts b/github-actions/scan/__test__/integrationtest.test.ts index 83bca8f889..b1fbfb5f8d 100644 --- a/github-actions/scan/__test__/integrationtest.test.ts +++ b/github-actions/scan/__test__/integrationtest.test.ts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT import * as launcher from '../src/launcher'; +import {IntegrationTestContext as IntegrationTestContext} from './integrationtest/testframework'; import * as shell from 'shelljs'; import { isDebug, debug, getInput } from '@actions/core'; import { info } from '@actions/core'; @@ -11,15 +12,31 @@ import { LaunchContext } from '../src/launcher'; import { create } from '@actions/artifact'; jest.mock('@actions/core'); jest.mock('@actions/artifact'); + /* - * This is an integration test suite for github-action "scan". - * As precondition you have to start a local sechub server in integration test mode and execute "setup-integrationtest.sh". - * After this is done, you can execute these tests. +* This is an integration test suite for github-action "scan". +* As precondition you have to call "01-start.sh" (please look into script for an example and more details) +* +* After script has finished you can execute the integration tests via "npm run integration-test" +* +* At the end the servers can be stopped with "05-stop.sh" (please look into script for an example and more details) +* (This is an explanation to start the tests locally - the github action workflow "github-action-scan.yml" does it in exact same way for CI/CD) +* */ - const sechub_debug = shell.env['SECHUB_DEBUG']; const debug_enabled = sechub_debug=='true'; +const integrationTestContext = new IntegrationTestContext(); + +integrationTestContext.serverVersion='1.4.0'; +integrationTestContext.serverPort= 8443; +integrationTestContext.serverUserId='int-test_superadmin'; +integrationTestContext.serverApiToken='int-test_superadmin-pwd'; + +integrationTestContext.finish(); + +const mockedInputMap = new Map(); + beforeEach(() => { jest.resetAllMocks(); @@ -34,7 +51,6 @@ beforeEach(() => { return true; }); } - (info as jest.Mock).mockImplementation((message) => { console.log('gh-info: %s', message); @@ -55,14 +71,13 @@ beforeEach(() => { }); -const mockedInputMap = new Map(); function initInputMap() { mockedInputMap.clear(); - mockedInputMap.set(input.PARAM_SECHUB_SERVER_URL, 'https://localhost:8443'); - mockedInputMap.set(input.PARAM_SECHUB_USER, 'int-test_superadmin'); - mockedInputMap.set(input.PARAM_API_TOKEN, 'int-test_superadmin-pwd'); + mockedInputMap.set(input.PARAM_SECHUB_SERVER_URL, `https://localhost:${integrationTestContext.serverPort}`); + mockedInputMap.set(input.PARAM_SECHUB_USER, `${integrationTestContext.serverUserId}`); + mockedInputMap.set(input.PARAM_API_TOKEN, `${integrationTestContext.serverApiToken}`); mockedInputMap.set(input.PARAM_PROJECT_NAME, 'test-project'); mockedInputMap.set(input.PARAM_CLIENT_VERSION, '1.2.0'); @@ -71,7 +86,6 @@ function initInputMap() { mockedInputMap.set(input.PARAM_TRUST_ALL, 'true'); // self signed certificate in test... } - describe('integrationtest', function () { test('integrationtest 1', function () { @@ -82,14 +96,13 @@ describe('integrationtest', function () { const launchPromise = launcher.launch(); /* test */ - assertExitCode(launchPromise, 0); + assertLastClientExitCode(launchPromise, 0); }); }); -async function assertExitCode(launchPromise: Promise, exitCode: number) { +async function assertLastClientExitCode(launchPromise: Promise, exitCode: number) { const context = await launchPromise; - expect(context.lastClientExitCode).toEqual(exitCode); } \ No newline at end of file diff --git a/github-actions/scan/__test__/integrationtest/01-start.sh b/github-actions/scan/__test__/integrationtest/01-start.sh new file mode 100755 index 0000000000..765ef1c187 --- /dev/null +++ b/github-actions/scan/__test__/integrationtest/01-start.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT + +# Purpose for this script: +# * Preparation for integration tests +# +# Details: +# - Calculates pathes +# - Downloads PDS and SecHub server version +# - removes former temp data automatically (e.g. old reports etc.) +# - Starts SecHubServer and PDS +# - Waits for the servers to be alive +# - After this a standard setup is done for SecHub to be able to communicate with PDS, project setup etc. +# - exit of script is done at the end - means synchronous execution +# +# Usage: 01-start.sh $secHubServerVersion $sechubServerPortNr $pdsVersion $pdsPortNr +# Example: +# ``` +# cd $gitRoot/github-actions/scan +# ./01-start.sh 1.4.1 8443 1.4.0 8444 +# ``` +# +SERVER_VERSION=$1 +SERVER_PORT=$2 + +PDS_SERVER_VERSION=$3 +PDS_SERVER_PORT=$4 + +if [ "$SERVER_VERSION" = "" ]; then + echo "first argument not set - is used as server verion!" + exit 1 +fi +if [ "$SERVER_PORT" = "" ]; then + echo "second argument not set - is used as server port!" + exit 1 +fi + +if [ "$PDS_SERVER_VERSION" = "" ]; then + echo "third argument not set - is used as PDS server verion!" + exit 1 +fi +if [ "$PDS_SERVER_PORT" = "" ]; then + echo "fourth argument not set - is used as PDS server port!" + exit 1 +fi + +SCRIPT_DIR="$(dirname -- "$0")" +if [ "$SCRIPT_DIR" = "." ]; then + pwd + SCRIPT_DIR="$(pwd)" +fi +echo "SCRIPT_DIR = $SCRIPT_DIR" +cd ${SCRIPT_DIR} + +cd ../.. #github action scan folder +GHA_SCAN_FOLDER_PATH="$(pwd)" +RUNTIME_DIR="${GHA_SCAN_FOLDER_PATH}/runtime"; +echo "RUNTIME_DIR = $RUNTIME_DIR" + +SHARED_VOLUME="${RUNTIME_DIR}/shared-volume" +mkdir "$SHARED_VOLUME" -p + +## ---------------------------------- +## SecHub Server +## ---------------------------------- + +SERVER_FOLDER_PATH="${RUNTIME_DIR}/server/${SERVER_VERSION}" +SERVER_EXECUTABLE_NAME="sechub-server-${SERVER_VERSION}.jar" +SERVER_EXECUTABLE_PATH="${SERVER_FOLDER_PATH}/${SERVER_EXECUTABLE_NAME}" +SERVER_DOWNLOAD_URL="https://github.com/mercedes-benz/sechub/releases/download/v${SERVER_VERSION}-server/${SERVER_EXECUTABLE_NAME}" +SERVER_CERTFILE_PATH="${SERVER_FOLDER_PATH}/generated-localhost-certificate.p12" + +echo "SERVER_FOLDER_PATH=$SERVER_FOLDER_PATH" + +SANITY_PATH_CHECK_FOR_SCRIPT="$GHA_SCAN_FOLDER_PATH/__test__/integrationtest/01-start.sh" +if [ ! -f "$SANITY_PATH_CHECK_FOR_SCRIPT" ]; then + echo "Sanity check failed - not found: $SANITY_PATH_CHECK_FOR_SCRIPT" + exit 1 +fi + +## download server if not available +if [ ! -f $SERVER_EXECUTABLE_PATH ]; then + mkdir "${SERVER_FOLDER_PATH}" -p + echo "Start download from $SERVER_DOWNLOAD_URL" + curl -L ${SERVER_DOWNLOAD_URL} -o ${SERVER_EXECUTABLE_PATH} +else + echo "$SERVER_EXECUTABLE_NAME exists already - skip download" +fi + +## generate SecHub test certificate +if [ ! -f $SERVER_CERTFILE_PATH ]; then + mkdir "${SERVER_FOLDER_PATH}" -p + keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore "${SERVER_CERTFILE_PATH}" -validity 3650 -storepass 123456 --dname "CN=localhost, OU=ID" +fi + + +## ---------------------------------- +## PDS +## ---------------------------------- + +# https://github.com/mercedes-benz/sechub/releases/download/v1.4.0-pds/sechub-pds-1.4.0.jar +# https://github.com/mercedes-benz/sechub/releases/download/v1.4.0-pds/sechub-pds-1.4.0.jar +PDS_SERVER_FOLDER_PATH="${RUNTIME_DIR}/pds/${PDS_SERVER_VERSION}" +PDS_SERVER_EXECUTABLE_NAME="sechub-pds-${PDS_SERVER_VERSION}.jar" +PDS_SERVER_EXECUTABLE_PATH="${PDS_SERVER_FOLDER_PATH}/${PDS_SERVER_EXECUTABLE_NAME}" +PDS_SERVER_DOWNLOAD_URL="https://github.com/mercedes-benz/sechub/releases/download/v${PDS_SERVER_VERSION}-pds/${PDS_SERVER_EXECUTABLE_NAME}" +PDS_SERVER_CERTFILE_PATH="${PDS_SERVER_FOLDER_PATH}/generated-localhost-certificate.p12" + +echo "PDS_SERVER_FOLDER_PATH=$PDS_SERVER_FOLDER_PATH" + +## download PDS if not available +if [ ! -f $PDS_SERVER_EXECUTABLE_PATH ]; then + mkdir "${PDS_SERVER_FOLDER_PATH}" -p + echo "Start download from $PDS_SERVER_DOWNLOAD_URL" + curl -L ${PDS_SERVER_DOWNLOAD_URL} -o ${PDS_SERVER_EXECUTABLE_PATH} +else + echo "$PDS_SERVER_EXECUTABLE_NAME exists already - skip download" +fi + +## generate PDS test certificate +if [ ! -f $PDS_SERVER_CERTFILE_PATH ]; then + mkdir "${PDS_SERVER_FOLDER_PATH}" -p + keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore "${PDS_SERVER_CERTFILE_PATH}" -validity 3650 -storepass 123456 --dname "CN=localhost, OU=ID" +fi + +## create config file for pds +PDS_CONFIG_FILE="$PDS_SERVER_FOLDER_PATH/pds-config.json" +cp $SCRIPT_DIR/pds-config-template.json $PDS_CONFIG_FILE + + +cd $SCRIPT_DIR + +# Start SecHub async +SECHUB_LOGFILE=$SERVER_FOLDER_PATH/server.log +rm $SECHUB_LOGFILE -f # remove old log files on start +echo "./start_sechub_server.sh $SERVER_PORT $SERVER_EXECUTABLE_PATH $SERVER_CERTFILE_PATH $SECHUB_LOGFILE $SHARED_VOLUME" > $SECHUB_LOGFILE +./start_sechub_server.sh $SERVER_PORT $SERVER_EXECUTABLE_PATH $SERVER_CERTFILE_PATH $SECHUB_LOGFILE $SHARED_VOLUME + +# Start PDS async +PDS_LOGFILE=$PDS_SERVER_FOLDER_PATH/pds.log +rm $PDS_LOGFILE -f # remove old log files on start + +echo "./start_pds.sh $PDS_SERVER_PORT $PDS_SERVER_EXECUTABLE_PATH $PDS_SERVER_CERTFILE_PATH $PDS_LOGFILE $SHARED_VOLUME $PDS_CONFIG_FILE" > $PDS_LOGFILE +./start_pds.sh $PDS_SERVER_PORT $PDS_SERVER_EXECUTABLE_PATH $PDS_SERVER_CERTFILE_PATH $PDS_LOGFILE $SHARED_VOLUME $PDS_CONFIG_FILE + +./wait_sechub_server_alive.sh $SERVER_PORT +./wait_pds_alive.sh $PDS_SERVER_PORT \ No newline at end of file diff --git a/github-actions/scan/__test__/integrationtest/05-stop.sh b/github-actions/scan/__test__/integrationtest/05-stop.sh new file mode 100755 index 0000000000..101ff32323 --- /dev/null +++ b/github-actions/scan/__test__/integrationtest/05-stop.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT + +# Purpose for this script: +# * Cleanup after integration tests +# +# Details: +# - Stops running PDS and SecHub servers (asynchronous) +# +# Usage: 05-stop.sh $sechubServerPortNr $pdsPortNr +# Example: +# ``` +# cd $gitRoot/github-actions/scan +# ./05-stop.sh 8443 8444 +# ``` +SERVER_PORT=$1 + +SCRIPT_DIR="$(dirname -- "$0")" +if [ "$SCRIPT_DIR" = "." ]; then + pwd + SCRIPT_DIR="$(pwd)" +fi +echo "SCRIPT_DIR = $SCRIPT_DIR" +cd ${SCRIPT_DIR} + +./stop_sechub_server.sh $SERVER_PORT +./stop_pds.sh $SERVER_PORT \ No newline at end of file diff --git a/github-actions/scan/__test__/setup-integrationtest.sh b/github-actions/scan/__test__/integrationtest/init_sechub_data.sh similarity index 90% rename from github-actions/scan/__test__/setup-integrationtest.sh rename to github-actions/scan/__test__/integrationtest/init_sechub_data.sh index 25beda4896..3eea498066 100755 --- a/github-actions/scan/__test__/setup-integrationtest.sh +++ b/github-actions/scan/__test__/integrationtest/init_sechub_data.sh @@ -20,7 +20,7 @@ if [[ "$SECHUB_APITOKEN" == "" ]]; then echo "SECHUB_APITOKEN was not defined - use fallback" fi echo "----- Prepare sechub api script usage" -cd ../../.. +cd ../../../.. # at root level now cd sechub-developertools/scripts @@ -40,5 +40,6 @@ echo "----- Create test project:$project for user:$user" - - +# wait a short time - to give SecHub chance to handle events etc. +sleep 2s +# now the setup shall be done and effective diff --git a/github-actions/scan/__test__/integrationtest/pds-codescan-demo.sh b/github-actions/scan/__test__/integrationtest/pds-codescan-demo.sh new file mode 100755 index 0000000000..c4d5ac0016 --- /dev/null +++ b/github-actions/scan/__test__/integrationtest/pds-codescan-demo.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT \ No newline at end of file diff --git a/github-actions/scan/__test__/integrationtest/pds-config-template.json b/github-actions/scan/__test__/integrationtest/pds-config-template.json new file mode 100644 index 0000000000..60b6e7c6bf --- /dev/null +++ b/github-actions/scan/__test__/integrationtest/pds-config-template.json @@ -0,0 +1,11 @@ +{ + "apiVersion" : "1.0", + "serverId" : "gha_integrationtest_server", + "products" : [ { + "id" : "codescan_demo", + "path" : "./pds-codescan-demo.sh", + "scanType" : "codeScan", + "description" : "This is only a fake code scan - used by integration tests. The code scan will just return data from uploaded zip file" + } + ] + } \ No newline at end of file diff --git a/github-actions/scan/__test__/integrationtest/start_pds.sh b/github-actions/scan/__test__/integrationtest/start_pds.sh new file mode 100755 index 0000000000..902e5c4a47 --- /dev/null +++ b/github-actions/scan/__test__/integrationtest/start_pds.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT + +SERVER_PORT=$1 +PATH_TO_EXECUTABLE=$2 +PATH_TO_CERTIFICATE=$3 +PATH_TO_LOGFILE=$4 +SHARED_VOLUME=$5 +PDS_CONFIG_FILE=$6 + +echo "SERVER_PORT=$SERVER_PORT" >> $PATH_TO_LOGFILE + +echo "[ START ] PDS" + +if [ "$SERVER_PORT" = "" ]; then + echo "first argument not set - is used as server port!" + exit 1 +fi +if [ "$PATH_TO_EXECUTABLE" = "" ]; then + echo "second argument not set - is used as server jar executable path!" + exit 1 +fi + +if [ "$PATH_TO_CERTIFICATE" = "" ]; then + echo "third argument not set - is used as server certificate path!" + exit 1 +fi +if [ "$PATH_TO_LOGFILE" = "" ]; then + echo "fourth argument not set - is used as server log file path!" + exit 1 +fi +if [ "$SHARED_VOLUME" = "" ]; then + echo "fifth argument not set - is used as shared volume path!" + exit 1 +fi +if [ "$PDS_CONFIG_FILE" = "" ]; then + echo "sixth argument not set - is used as PDS config file path!" + exit 1 +fi + +SCRIPT_DIR="$(dirname -- "$0")" + +echo "Start PDS at localhost:${SERVER_PORT}, executable at: ${PATH_TO_EXECUTABLE}" +# `curl -s --insecure https://localhost:${this.serverPort}/api/anonymous/check/alive`; +java \ + -Dfile.encoding=UTF-8 \ + -Dspring.profiles.active=pds_integrationtest,pds_h2 \ + -Dserver.ssl.key-store=${PATH_TO_CERTIFICATE} \ + -Dserver.port=${SERVER_PORT} \ + -Dpds.storage.sharedvolume.upload.dir=$SHARED_VOLUME \ + -Dpds.config.file=$PDS_CONFIG_FILE \ + -jar ${PATH_TO_EXECUTABLE}>>${PATH_TO_LOGFILE} & diff --git a/github-actions/scan/__test__/integrationtest/start_sechub_server.sh b/github-actions/scan/__test__/integrationtest/start_sechub_server.sh new file mode 100755 index 0000000000..79ed6a977b --- /dev/null +++ b/github-actions/scan/__test__/integrationtest/start_sechub_server.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT + +SERVER_PORT=$1 +PATH_TO_EXECUTABLE=$2 +PATH_TO_CERTIFICATE=$3 +PATH_TO_LOGFILE=$4 +SHARED_VOLUME=$5 +echo "SERVER_PORT=$SERVER_PORT" >> $PATH_TO_LOGFILE + +echo "[ START ] SecHub" + +if [ "$SERVER_PORT" = "" ]; then + echo "first argument not set - is used as server port!" + exit 1 +fi +if [ "$PATH_TO_EXECUTABLE" = "" ]; then + echo "second argument not set - is used as server jar executable path!" + exit 1 +fi + +if [ "$PATH_TO_CERTIFICATE" = "" ]; then + echo "third argument not set - is used as server certificate path!" + exit 1 +fi +if [ "$PATH_TO_LOGFILE" = "" ]; then + echo "fourth argument not set - is used as server log file path!" + exit 1 +fi +if [ "$SHARED_VOLUME" = "" ]; then + echo "fifth argument not set - is used as shared volume path!" + exit 1 +fi + + +SCRIPT_DIR="$(dirname -- "$0")" + +echo "Start SecHub server at localhost:${SERVER_PORT}, executable at: ${PATH_TO_EXECUTABLE}" +# `curl -s --insecure https://localhost:${this.serverPort}/api/anonymous/check/alive`; +java \ + -Dfile.encoding=UTF-8 \ + -Dspring.profiles.active=dev,mocked_products,h2,integrationtest \ + -Dserver.ssl.key-store=${PATH_TO_CERTIFICATE} \ + -Dsechub.server.debug=true \ + -Dserver.port=${SERVER_PORT} \ + -Dsechub.integrationtest.ignore.missing.serverproject=true \ + -Dsechub.storage.sharedvolume.upload.dir=$SHARED_VOLUME \ + -jar ${PATH_TO_EXECUTABLE}>>${PATH_TO_LOGFILE} & diff --git a/github-actions/scan/__test__/integrationtest/stop_pds.sh b/github-actions/scan/__test__/integrationtest/stop_pds.sh new file mode 100755 index 0000000000..23dc20c97b --- /dev/null +++ b/github-actions/scan/__test__/integrationtest/stop_pds.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT + +# $1 = server port +SERVER_PORT=$1 +echo "[ STOP ] PDS" +echo "Shutdown PDS at localhost:${SERVER_PORT}" +if [ "$SERVER_PORT" = "" ]; then + echo "first argument not set - is used as server port!" + exit 1 +fi +curl -s --insecure https://localhost:${SERVER_PORT}/api/anonymous/integrationtest/shutdown +echo "Shutdown initiated" diff --git a/github-actions/scan/__test__/integrationtest/stop_sechub_server.sh b/github-actions/scan/__test__/integrationtest/stop_sechub_server.sh new file mode 100755 index 0000000000..a5affe0032 --- /dev/null +++ b/github-actions/scan/__test__/integrationtest/stop_sechub_server.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT + +# $1 = server port +SERVER_PORT=$1 +echo "[ STOP ] SecHub" +echo "Shutdown SecHub server at localhost:${SERVER_PORT}" +if [ "$SERVER_PORT" = "" ]; then + echo "first argument not set - is used as server port!" + exit 1 +fi +curl -s --insecure https://localhost:${SERVER_PORT}/api/anonymous/integrationtest/shutdown +echo "Shutdown initiated" diff --git a/github-actions/scan/__test__/integrationtest/testframework.ts b/github-actions/scan/__test__/integrationtest/testframework.ts new file mode 100644 index 0000000000..de4f525120 --- /dev/null +++ b/github-actions/scan/__test__/integrationtest/testframework.ts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT + +import * as shell from 'shelljs'; + +export class IntegrationTestContext { + + serverVersion: string|undefined; + serverPort: number|undefined; + serverUserId: string|undefined; + serverApiToken: string|undefined; + + pathToServerFolder: string|undefined; + pathToServerExecutable: string|undefined; + serverExecutableName: string|undefined; + serverCertFilePath: string|undefined; + + public finish() { + this.pathToServerFolder = `./runtime/server/${this.serverVersion}`; + this.serverExecutableName = `sechub-server-${this.serverVersion}.jar`; + this.pathToServerExecutable = `${this.pathToServerFolder}/${this.serverExecutableName}`; + this.serverCertFilePath = `${this.pathToServerFolder}/generated-localhost-certificate.p12`; + + shell.env['SECHUB_SERVER'] = `https://localhost:${this.serverPort}`; + shell.env['SECHUB_USERID'] = `${this.serverUserId}`; + shell.env['SECHUB_APITOKEN'] = `${this.serverApiToken}`; + } + +} diff --git a/github-actions/scan/__test__/integrationtest/wait_pds_alive.sh b/github-actions/scan/__test__/integrationtest/wait_pds_alive.sh new file mode 100755 index 0000000000..a26bbc2565 --- /dev/null +++ b/github-actions/scan/__test__/integrationtest/wait_pds_alive.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT + +SERVER_PORT=$1 + +echo "[ WAIT ] Until PDS alive" + +if [ "$SERVER_PORT" = "" ]; then + echo "first argument not set - is used as server port!" + exit 1 +fi + +declare -i MAX_WAIT_SECONDS=30 +declare -i waitCount=0 + +## Wait until available +until $(curl --output /dev/null --silent --head --fail --insecure https://localhost:${SERVER_PORT}/api/anonymous/check/alive); do + printf '.' + if [ $waitCount -gt $MAX_WAIT_SECONDS ]; then + echo "WAIT max time exceeded: $MAX_WAIT_SECONDS seconds" + exit 1 + fi + sleep 1 + waitCount=$waitCount+1 +done + +echo "" +echo "> PDS is alive" diff --git a/github-actions/scan/__test__/integrationtest/wait_sechub_server_alive.sh b/github-actions/scan/__test__/integrationtest/wait_sechub_server_alive.sh new file mode 100755 index 0000000000..567cdd8e82 --- /dev/null +++ b/github-actions/scan/__test__/integrationtest/wait_sechub_server_alive.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT + +SERVER_PORT=$1 + +echo "[ WAIT ] Until SecHub server alive" + +if [ "$SERVER_PORT" = "" ]; then + echo "first argument not set - is used as server port!" + exit 1 +fi + +declare -i MAX_WAIT_SECONDS=30 +declare -i waitCount=0 + +## Wait until available +until $(curl --output /dev/null --silent --head --fail --insecure https://localhost:${SERVER_PORT}/api/anonymous/check/alive); do + printf '.' + if [ $waitCount -gt $MAX_WAIT_SECONDS ]; then + echo "WAIT max time exceeded: $MAX_WAIT_SECONDS seconds" + exit 1 + fi + sleep 1 + waitCount=$waitCount+1 +done + +echo "" +echo "> SecHub server is alive" diff --git a/github-actions/scan/package.json b/github-actions/scan/package.json index b1eca1b0f1..3dbeab3c22 100644 --- a/github-actions/scan/package.json +++ b/github-actions/scan/package.json @@ -8,10 +8,9 @@ "cleanBuild": "ncc cache clean;ncc build src/main.ts", "lint": "eslint src", "prettier": "npx prettier --write src", - "test": "npx jest", - "test-x": "npx jest --testPathIgnorePatterns='integrationtest.test.ts'", - "integration-test": "npx jest --runInBand --testPathPattern='integrationtest.test.ts'" - + "test-debug-all": "npx jest", + "test": "npx jest --testPathIgnorePatterns='integrationtest.test.ts'", + "integration-test": "jest --runInBand --testPathPattern='integrationtest.test.ts'" }, "license": "MIT", "dependencies": { diff --git a/github-actions/scan/src/client-download.ts b/github-actions/scan/src/client-download.ts index 8cb27da3f2..8b3042ebdd 100644 --- a/github-actions/scan/src/client-download.ts +++ b/github-actions/scan/src/client-download.ts @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -import * as fs from 'fs'; import * as core from '@actions/core'; -import { shellExecOrFail } from './fs-helper'; +import * as fs from 'fs'; +import { shellExecSynchOrFail } from './fs-helper'; import { LaunchContext } from './launcher'; /** @@ -24,10 +24,10 @@ export function downloadClientRelease(context: LaunchContext): void { core.debug(`SecHub-Client download URL: ${zipDownloadUrl}`); core.debug(`SecHub-Client download folder: ${context.clientDownloadFolder}`); - shellExecOrFail(`mkdir ${context.clientDownloadFolder} -p`); + shellExecSynchOrFail(`mkdir ${context.clientDownloadFolder} -p`); - shellExecOrFail(`curl -L ${zipDownloadUrl} -o ${secHubZipFilePath}`); - shellExecOrFail(`unzip -o ${secHubZipFilePath} -d ${context.clientDownloadFolder}`); - shellExecOrFail(`chmod +x ${secHubZipFilePath}`); + shellExecSynchOrFail(`curl -L ${zipDownloadUrl} -o ${secHubZipFilePath}`); + shellExecSynchOrFail(`unzip -o ${secHubZipFilePath} -d ${context.clientDownloadFolder}`); + shellExecSynchOrFail(`chmod +x ${secHubZipFilePath}`); } diff --git a/github-actions/scan/src/fs-helper.ts b/github-actions/scan/src/fs-helper.ts index 0b39218f18..c7679ef492 100644 --- a/github-actions/scan/src/fs-helper.ts +++ b/github-actions/scan/src/fs-helper.ts @@ -3,6 +3,8 @@ import * as shell from 'shelljs'; import * as core from '@actions/core'; import * as path from 'path'; +import * as child from 'child_process'; + import { ShellString } from 'shelljs'; /** @@ -34,23 +36,37 @@ export function getFiles(pattern: string): string[] { return reportFiles; } -export class ShellFailedWithExitCodeNotZeroError extends Error { - constructor(command: string, shellExecResult: ShellString) { - super(`Shell script call failed.\nExit code: ${shellExecResult.code}\nCommand: "${command}"\nStdErr: ${shellExecResult.stderr}`); +export class ShellFailedWithExitCodeNotAcceptedError extends Error { + constructor(command: string, shellExecResult: ShellString, acceptedExitCodes: number[]) { + super(`Shell script call failed.\nExit code: ${shellExecResult.code} - accepted would be: ${acceptedExitCodes}.\nCommand: "${command}"\nStdErr: ${shellExecResult.stderr}\nStdOut: ${shellExecResult.stdout}`); } } /** - * Executes given command by shell - errors are handled - * @param command + * Executes given command by shell synchronous - errors are handled. + * Attention: This mechanism has problems with script execution where child processes are created! + * In this case the script execution by shelljs freezes! Workaround here: Use shellExecAsync(..) in + * this case! + * @param command command to execute + * @param acceptedExitCodes - an array with accepted exit codes. if not defined only 0 is accepted * @throws ShellFailedWithExitCodeNotZeroError * @returns shellstring */ -export function shellExecOrFail(command: string): ShellString { +export function shellExecSynchOrFail(command: string, acceptedExitCodes: number[] = [0]): ShellString { + const shellExecResult = shell.exec(command); - if ( shellExecResult.code!=0){ - throw new ShellFailedWithExitCodeNotZeroError(command, shellExecResult); + if (! acceptedExitCodes.includes(shellExecResult.code)){ + throw new ShellFailedWithExitCodeNotAcceptedError(command, shellExecResult, acceptedExitCodes); } return shellExecResult; +} + +/** + * Executes given command asynchronous + * @param command command to execute + * @returns child process + */ +export function shellExecAsync(command: string): child.ChildProcess { + return child.exec(command); } \ No newline at end of file diff --git a/sechub-doc/src/docs/asciidoc/documents/techdoc/01_development.adoc b/sechub-doc/src/docs/asciidoc/documents/techdoc/01_development.adoc index 1dda6a2080..ea6cbb27fc 100644 --- a/sechub-doc/src/docs/asciidoc/documents/techdoc/01_development.adoc +++ b/sechub-doc/src/docs/asciidoc/documents/techdoc/01_development.adoc @@ -184,7 +184,9 @@ Overview of branch types: to `develop` branch as well. |`develop` |master | Development branch. This branch will be part of the next release. Do not commit into this branch. Instead create a dedicated feature-branch. -|`feature-${issue}` |develop | For each feature we create a `feature-1234-xyz` branch. It will be merged into the `develop` branch when finished. +|`feature-${issue}` |develop | For each feature we create a `feature-1234-xyz` branch. It will be merged into the `develop` branch when finished. A git push to remote will trigger a full CI/CD build for SecHub client, server and PDS but not for SecHub github actions. +|`gha_feature-${issue}` |develop | For each github action feature we create a `gha_feature-1234-xyz` branch. It will be merged into the `develop` branch when finished. A git push to remote will trigger CI/CD build for SecHub github actions, but NOT a full CI/CD build for SecHub client, server and PDS. + |=== @@ -243,4 +245,4 @@ NOTE: This will disable ssl encryption and also Spring security which requires s If you want to administrate this by developer admin ui, you should also switch to the HTTP protocol as described in section -<> \ No newline at end of file +<>