feat: add gradle-webapp pipeline type #2

Merged
KofTwentyTwo merged 4 commits from feature/gradle-webapp-pipeline into develop 2026-03-22 23:36:54 +00:00
30 changed files with 1796 additions and 31 deletions

No files matched your search

+2 -2
View File
@@ -39,7 +39,7 @@ echo "Packing generate_config.sh..."
echo ''
# Embed each template
for tpl in java-webapp node-api node-webapp terraform sdk-distribution validate-cd-repo argocd-apps; do
for tpl in java-webapp gradle-webapp node-api node-webapp terraform sdk-distribution validate-cd-repo argocd-apps; do
echo "cat > /tmp/munitor/templates/${tpl}.yml.tpl << 'MUNITOR_TPL_EOF'"
cat "${SRC}/templates/${tpl}.yml.tpl"
echo 'MUNITOR_TPL_EOF'
@@ -47,7 +47,7 @@ echo "Packing generate_config.sh..."
done
# Embed partials
for partial in java-webapp-deploy node-api-deploy node-webapp-deploy; do
for partial in java-webapp-deploy gradle-webapp-deploy node-api-deploy node-webapp-deploy; do
echo "cat > /tmp/munitor/templates/partials/${partial}.yml.tpl << 'MUNITOR_PARTIAL_EOF'"
cat "${SRC}/templates/partials/${partial}.yml.tpl"
echo 'MUNITOR_PARTIAL_EOF'
+6
View File
@@ -0,0 +1,6 @@
description: >
Run Gradle build (compile + package, skip tests).
steps:
- run:
name: Gradle build
command: << include(scripts/gradle_build.sh) >>
+6
View File
@@ -0,0 +1,6 @@
description: >
Run Gradle Checkstyle analysis (skips gracefully if plugin not applied).
steps:
- run:
name: Gradle Checkstyle
command: << include(scripts/gradle_checkstyle.sh) >>
+13
View File
@@ -0,0 +1,13 @@
description: >
Generate JaCoCo coverage report and enforce minimum thresholds.
parameters:
min_instruction:
type: string
default: "70"
description: Minimum instruction coverage percentage.
steps:
- run:
name: Gradle JaCoCo coverage
environment:
MIN_INSTRUCTION: << parameters.min_instruction >>
command: << include(scripts/gradle_jacoco.sh) >>
+6
View File
@@ -0,0 +1,6 @@
description: >
Run Gradle PMD static analysis (skips gracefully if plugin not applied).
steps:
- run:
name: Gradle PMD
command: << include(scripts/gradle_pmd.sh) >>
+6
View File
@@ -0,0 +1,6 @@
description: >
Run Gradle SpotBugs static analysis (skips gracefully if plugin not applied).
steps:
- run:
name: Gradle SpotBugs
command: << include(scripts/gradle_spotbugs.sh) >>
+15
View File
@@ -0,0 +1,15 @@
description: >
Run Gradle tests and store JUnit XML results.
steps:
- run:
name: Gradle test
command: << include(scripts/gradle_test.sh) >>
- run:
name: Collect test results
command: << include(scripts/collect_gradle_test_results.sh) >>
when: always
- store_test_results:
path: /tmp/test-results
- store_artifacts:
path: /tmp/test-results
destination: test-results
+7
View File
@@ -0,0 +1,7 @@
description: >
Restore the Gradle caches based on wrapper and settings checksums.
steps:
- restore_cache:
keys:
- gradle-v1-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "settings.gradle.kts" }}
- gradle-v1-
+8
View File
@@ -0,0 +1,8 @@
description: >
Save the Gradle caches for future builds.
steps:
- save_cache:
key: gradle-v1-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "settings.gradle.kts" }}
paths:
- ~/.gradle/caches
- ~/.gradle/wrapper
+43
View File
@@ -0,0 +1,43 @@
description: >
Full Gradle build and test job. Installs Java, restores cache, calculates
version, builds, tests, saves cache, and persists the workspace for
downstream jobs.
parameters:
java_version:
type: string
default: "21"
description: Java major version to install.
resource_class:
type: enum
enum:
- medium
- large
- xlarge
default: large
description: Machine resource class.
executor:
name: machine
resource_class: << parameters.resource_class >>
steps:
- checkout
- install_java:
version: << parameters.java_version >>
- restore_gradle_cache
- calculate_version
- export_version_vars
- gradle_build
- gradle_test
- run:
name: Collect test results
command: << include(scripts/collect_gradle_test_results.sh) >>
when: always
- store_test_results:
path: /tmp/test-results
- run:
name: Persist version metadata
command: <<include(scripts/persist_version_metadata.sh)>>
- save_gradle_cache
- persist_to_workspace:
root: .
paths:
- "."
+27
View File
@@ -0,0 +1,27 @@
description: >
Run code quality checks: Checkstyle, SpotBugs, and PMD.
Requires workspace from gradle_build_and_test.
Skips individual tools gracefully if their Gradle plugin is not applied.
parameters:
java_version:
type: string
default: "21"
description: Java major version to install.
resource_class:
type: enum
enum:
- medium
- large
default: large
description: Docker resource class.
executor:
name: java
java_version: << parameters.java_version >>
resource_class: << parameters.resource_class >>
steps:
- attach_workspace:
at: .
- restore_gradle_cache
- gradle_checkstyle
- gradle_spotbugs
- gradle_pmd
+29
View File
@@ -0,0 +1,29 @@
description: >
Generate coverage report and enforce minimum thresholds.
Requires workspace from gradle_build_and_test.
parameters:
java_version:
type: string
default: "21"
description: Java major version to install.
min_instruction:
type: string
default: "70"
description: Minimum instruction coverage percentage.
resource_class:
type: enum
enum:
- medium
- large
default: large
description: Docker resource class.
executor:
name: java
java_version: << parameters.java_version >>
resource_class: << parameters.resource_class >>
steps:
- attach_workspace:
at: .
- restore_gradle_cache
- gradle_jacoco:
min_instruction: << parameters.min_instruction >>
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
# Source shared helpers
MUNITOR_HELPERS="${MUNITOR_HELPERS:-$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/munitor_helpers.sh}"
# shellcheck source=munitor_helpers.sh
if [[ -f "${MUNITOR_HELPERS}" ]]; then source "${MUNITOR_HELPERS}"
elif ! type munitor_header &>/dev/null; then
munitor_header() { echo "=== Munitor: ${1:-unknown} ==="; }
munitor_check_tool() { command -v "$1" &>/dev/null || { echo "ERROR: $1 not found"; exit 1; }; }
munitor_download_with_retry() { curl -fsSL --retry 3 "$1" -o "$2"; }
fi
munitor_header "collect_gradle_test_results"
DEST="/tmp/test-results"
mkdir -p "${DEST}"
find . -path "*/build/test-results/test/*.xml" -exec cp {} "${DEST}/" \;
COUNT=$(find "${DEST}" -name "*.xml" 2>/dev/null | wc -l | tr -d ' ')
echo "Collected ${COUNT} test result file(s) to ${DEST}/"
+13 -7
View File
@@ -13,17 +13,23 @@ extract_munitor_vars() {
fi
# --- Org defaults (overridable via .munitor.yml org section) ---
local ORG_DOCKER_REGISTRY=$(yq '.org.docker_registry // "ghcr.io/KofTwentyTwo"' "${config_file}")
local ORG_NPM_SCOPE=$(yq '.org.npm_scope // "@koftwentytwo"' "${config_file}")
local ORG_CI_EMAIL=$(yq '.org.ci_email // "munitor-ci@koftwentytwo.com"' "${config_file}")
local ORG_CI_NAME=$(yq '.org.ci_name // "Munitor CI"' "${config_file}")
local ORG_ORB_SLUG=$(yq '.org.orb_slug // "kof22/munitor"' "${config_file}")
local ORG_DOCKER_REGISTRY
ORG_DOCKER_REGISTRY=$(yq '.org.docker_registry // "ghcr.io/KofTwentyTwo"' "${config_file}")
local ORG_NPM_SCOPE
ORG_NPM_SCOPE=$(yq '.org.npm_scope // "@koftwentytwo"' "${config_file}")
local ORG_CI_EMAIL
ORG_CI_EMAIL=$(yq '.org.ci_email // "munitor-ci@koftwentytwo.com"' "${config_file}")
local ORG_CI_NAME
ORG_CI_NAME=$(yq '.org.ci_name // "Munitor CI"' "${config_file}")
local ORG_ORB_SLUG
ORG_ORB_SLUG=$(yq '.org.orb_slug // "kof22/munitor"' "${config_file}")
# Core pipeline settings
MUNITOR_PIPELINE=$(yq '.pipeline' "${config_file}")
MUNITOR_ORB_VERSION=$(yq '.orb_version // ""' "${config_file}")
MUNITOR_IMAGE_NAME=$(yq '.image_name // ""' "${config_file}")
MUNITOR_JAVA_VERSION=$(yq '.java_version // "21"' "${config_file}")
MUNITOR_SERVER_MODULE=$(yq '.server_module // ""' "${config_file}")
MUNITOR_NODE_VERSION=$(yq '.node_version // "20"' "${config_file}")
MUNITOR_SONAR_PROJECT_KEY=$(yq '.sonar.project_key // ""' "${config_file}")
MUNITOR_DOCKER_REGISTRY=$(yq '.docker.registry // ""' "${config_file}")
@@ -198,7 +204,7 @@ extract_munitor_vars() {
fi
# Export all variables
export MUNITOR_PIPELINE MUNITOR_ORB_VERSION MUNITOR_IMAGE_NAME MUNITOR_JAVA_VERSION MUNITOR_NODE_VERSION
export MUNITOR_PIPELINE MUNITOR_ORB_VERSION MUNITOR_IMAGE_NAME MUNITOR_JAVA_VERSION MUNITOR_NODE_VERSION MUNITOR_SERVER_MODULE
export MUNITOR_SONAR_PROJECT_KEY MUNITOR_DOCKER_REGISTRY MUNITOR_CD_REPO MUNITOR_CD_FORMAT
export MUNITOR_CD_ENV_DEVELOP MUNITOR_CD_ENV_STAGING MUNITOR_CD_ENV_PROD MUNITOR_CD_ENV_RELEASE
export MUNITOR_COVERAGE_MIN MUNITOR_E2E MUNITOR_SBOM MUNITOR_OWASP
@@ -223,5 +229,5 @@ extract_munitor_vars() {
# Build the envsubst variable list
get_envsubst_vars() {
# shellcheck disable=SC2016
echo '${MUNITOR_PIPELINE} ${MUNITOR_ORB_VERSION} ${MUNITOR_IMAGE_NAME} ${MUNITOR_JAVA_VERSION} ${MUNITOR_NODE_VERSION} ${MUNITOR_SONAR_PROJECT_KEY} ${MUNITOR_DOCKER_REGISTRY} ${MUNITOR_CD_REPO} ${MUNITOR_CD_FORMAT} ${MUNITOR_CD_ENV_DEVELOP} ${MUNITOR_CD_ENV_STAGING} ${MUNITOR_CD_ENV_PROD} ${MUNITOR_CD_ENV_RELEASE} ${MUNITOR_COVERAGE_MIN} ${MUNITOR_E2E} ${MUNITOR_SBOM} ${MUNITOR_OWASP} ${MUNITOR_CONTEXT_REGISTRY} ${MUNITOR_CONTEXT_GITHUB} ${MUNITOR_CONTEXT_SONAR} ${MUNITOR_CONTEXT_NVD} ${MUNITOR_NPM_AUTH} ${MUNITOR_NPM_SCOPES} ${MUNITOR_SERVICES_JSON} ${MUNITOR_TEST_SETUP_SCRIPT} ${MUNITOR_TEST_COMMANDS_JSON} ${MUNITOR_COVERAGE_TOOL} ${MUNITOR_COVERAGE_COMMAND} ${MUNITOR_GITHUB_RELEASE} ${MUNITOR_SAST} ${MUNITOR_SAST_FAIL_ON_FINDINGS} ${MUNITOR_HEALTH_PATH} ${MUNITOR_HEALTH_PORT} ${MUNITOR_HEALTH_DB} ${MUNITOR_HEALTH_MIGRATIONS} ${MUNITOR_HEALTH_SMOKE_PATHS} ${MUNITOR_TF_PATH} ${MUNITOR_TF_LIVE_PATH} ${MUNITOR_TF_ENVIRONMENTS} ${MUNITOR_CHECKOV_SKIP} ${MUNITOR_KUSTOMIZE_VERSION} ${MUNITOR_KUSTOMIZE_BASE_PATH} ${MUNITOR_KUSTOMIZE_LOAD_RESTRICTOR} ${MUNITOR_KUSTOMIZE_SCAN_OVERLAY} ${MUNITOR_KUSTOMIZE_OVERLAYS} ${MUNITOR_KUSTOMIZE_OVERLAY_DIR} ${MUNITOR_KUBE_LINTER_CONFIG} ${MUNITOR_YAMLLINT_PATHS} ${MUNITOR_CI_EMAIL} ${MUNITOR_CI_NAME} ${MUNITOR_NPM_DEFAULT_SCOPE} ${MUNITOR_ORB_SLUG} ${MUNITOR_PACKAGE_MANAGER} ${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
echo '${MUNITOR_PIPELINE} ${MUNITOR_ORB_VERSION} ${MUNITOR_IMAGE_NAME} ${MUNITOR_JAVA_VERSION} ${MUNITOR_NODE_VERSION} ${MUNITOR_SONAR_PROJECT_KEY} ${MUNITOR_DOCKER_REGISTRY} ${MUNITOR_CD_REPO} ${MUNITOR_CD_FORMAT} ${MUNITOR_CD_ENV_DEVELOP} ${MUNITOR_CD_ENV_STAGING} ${MUNITOR_CD_ENV_PROD} ${MUNITOR_CD_ENV_RELEASE} ${MUNITOR_COVERAGE_MIN} ${MUNITOR_E2E} ${MUNITOR_SBOM} ${MUNITOR_OWASP} ${MUNITOR_CONTEXT_REGISTRY} ${MUNITOR_CONTEXT_GITHUB} ${MUNITOR_CONTEXT_SONAR} ${MUNITOR_CONTEXT_NVD} ${MUNITOR_NPM_AUTH} ${MUNITOR_NPM_SCOPES} ${MUNITOR_SERVICES_JSON} ${MUNITOR_TEST_SETUP_SCRIPT} ${MUNITOR_TEST_COMMANDS_JSON} ${MUNITOR_COVERAGE_TOOL} ${MUNITOR_COVERAGE_COMMAND} ${MUNITOR_GITHUB_RELEASE} ${MUNITOR_SAST} ${MUNITOR_SAST_FAIL_ON_FINDINGS} ${MUNITOR_HEALTH_PATH} ${MUNITOR_HEALTH_PORT} ${MUNITOR_HEALTH_DB} ${MUNITOR_HEALTH_MIGRATIONS} ${MUNITOR_HEALTH_SMOKE_PATHS} ${MUNITOR_TF_PATH} ${MUNITOR_TF_LIVE_PATH} ${MUNITOR_TF_ENVIRONMENTS} ${MUNITOR_CHECKOV_SKIP} ${MUNITOR_KUSTOMIZE_VERSION} ${MUNITOR_KUSTOMIZE_BASE_PATH} ${MUNITOR_KUSTOMIZE_LOAD_RESTRICTOR} ${MUNITOR_KUSTOMIZE_SCAN_OVERLAY} ${MUNITOR_KUSTOMIZE_OVERLAYS} ${MUNITOR_KUSTOMIZE_OVERLAY_DIR} ${MUNITOR_KUBE_LINTER_CONFIG} ${MUNITOR_YAMLLINT_PATHS} ${MUNITOR_CI_EMAIL} ${MUNITOR_CI_NAME} ${MUNITOR_NPM_DEFAULT_SCOPE} ${MUNITOR_ORB_SLUG} ${MUNITOR_PACKAGE_MANAGER} ${MUNITOR_SUPPLEMENTAL_IMAGES_JSON} ${MUNITOR_SERVER_MODULE}'
}
+2 -2
View File
@@ -70,7 +70,7 @@ validate_required_fields() {
[[ -z "$(yq '.orb_version // ""' "${config}")" ]] && missing+=("orb_version")
case "${pipeline}" in
java-webapp|node-api|node-webapp)
java-webapp|gradle-webapp|node-api|node-webapp)
[[ -z "$(yq '.image_name // ""' "${config}")" ]] && missing+=("image_name")
[[ -z "$(yq '.docker.registry // ""' "${config}")" ]] && missing+=("docker.registry")
;;
@@ -94,7 +94,7 @@ validate_required_fields "${PIPELINE_TYPE}" "${CONFIG_FILE}"
# --------------------------------------------------------------------------
# Warn about unknown top-level keys (catches typos like java_verion)
# --------------------------------------------------------------------------
KNOWN_KEYS="pipeline orb_version image_name java_version node_version sonar docker cd coverage e2e sbom owasp contexts npm node services test terraform sast health kustomize kube_linter_config package_manager yamllint_paths supplemental_images"
KNOWN_KEYS="pipeline orb_version image_name server_module java_version node_version sonar docker cd coverage e2e sbom owasp contexts npm node services test terraform sast health kustomize kube_linter_config package_manager yamllint_paths supplemental_images"
ACTUAL_KEYS=$(yq 'keys | .[]' "${CONFIG_FILE}" 2>/dev/null || true)
for key in ${ACTUAL_KEYS}; do
if ! echo "${KNOWN_KEYS}" | grep -qw "${key}"; then
+48
View File
@@ -141,6 +141,54 @@ ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+ExitOnOut
EXPOSE ${MUNITOR_HEALTH_PORT}
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \\
CMD wget --no-verbose --tries=1 --spider http://localhost:${MUNITOR_HEALTH_PORT}${MUNITOR_HEALTH_PATH} || exit 1
ENTRYPOINT ["sh", "-c", "java \$JAVA_OPTS -jar app.jar"]
DOCKERFILE
;;
gradle-webapp)
echo "Generating Dockerfile for gradle-webapp (java ${MUNITOR_JAVA_VERSION})"
# Find the shadow JAR in the server module's build output
SEARCH_DIR="build/libs"
if [[ -n "${MUNITOR_SERVER_MODULE:-}" ]]; then
SEARCH_DIR="${MUNITOR_SERVER_MODULE}/build/libs"
fi
APP_JAR=$(find "${SEARCH_DIR}" -maxdepth 1 -name "*.jar" \
! -name "*-plain.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" \
2>/dev/null | head -1) || true
if [[ -z "${APP_JAR}" ]]; then
echo "ERROR: No application JAR found in ${SEARCH_DIR}/" >&2
echo "Ensure shadowJar or bootJar is configured in build.gradle.kts" >&2
exit 1
fi
echo "Using JAR: ${APP_JAR}"
cat > Dockerfile <<DOCKERFILE
FROM eclipse-temurin:${MUNITOR_JAVA_VERSION}-jre-alpine
RUN apk update && apk upgrade --no-cache
RUN addgroup -g 1001 -S appgroup && \\
adduser -u 1001 -S appuser -G appgroup
WORKDIR /app
COPY ${APP_JAR} app.jar
RUN chown -R appuser:appgroup /app
USER appuser
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+ExitOnOutOfMemoryError"
EXPOSE ${MUNITOR_HEALTH_PORT}
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \\
CMD wget --no-verbose --tries=1 --spider http://localhost:${MUNITOR_HEALTH_PORT}${MUNITOR_HEALTH_PATH} || exit 1
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail
# Source shared helpers
MUNITOR_HELPERS="${MUNITOR_HELPERS:-$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/munitor_helpers.sh}"
# shellcheck source=munitor_helpers.sh
if [[ -f "${MUNITOR_HELPERS}" ]]; then source "${MUNITOR_HELPERS}"
elif ! type munitor_header &>/dev/null; then
munitor_header() { echo "=== Munitor: ${1:-unknown} ==="; }
munitor_check_tool() { command -v "$1" &>/dev/null || { echo "ERROR: $1 not found"; exit 1; }; }
munitor_download_with_retry() { curl -fsSL --retry 3 "$1" -o "$2"; }
fi
munitor_header "gradle_build"
if [[ ! -f "./gradlew" ]]; then
echo "ERROR: ./gradlew not found. Ensure the Gradle wrapper is present."
exit 1
fi
if [[ ! -x "./gradlew" ]]; then
chmod +x ./gradlew
fi
munitor_check_tool java -version
echo "Running Gradle build..."
./gradlew build -x test --no-daemon --console=plain
echo "Gradle build complete."
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -euo pipefail
# Source shared helpers
MUNITOR_HELPERS="${MUNITOR_HELPERS:-$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/munitor_helpers.sh}"
# shellcheck source=munitor_helpers.sh
if [[ -f "${MUNITOR_HELPERS}" ]]; then source "${MUNITOR_HELPERS}"
elif ! type munitor_header &>/dev/null; then
munitor_header() { echo "=== Munitor: ${1:-unknown} ==="; }
munitor_check_tool() { command -v "$1" &>/dev/null || { echo "ERROR: $1 not found"; exit 1; }; }
munitor_download_with_retry() { curl -fsSL --retry 3 "$1" -o "$2"; }
fi
munitor_header "gradle_checkstyle"
if [[ ! -f "./gradlew" ]]; then
echo "ERROR: ./gradlew not found. Ensure the Gradle wrapper is present."
exit 1
fi
if [[ ! -x "./gradlew" ]]; then
chmod +x ./gradlew
fi
# Detect if checkstyle plugin is applied
if ! ./gradlew tasks --all 2>/dev/null | grep -q "checkstyleMain"; then
echo "Checkstyle plugin not applied, skipping."
exit 0
fi
echo "Running Checkstyle..."
./gradlew checkstyleMain checkstyleTest --no-daemon --console=plain
echo "Checkstyle passed."
+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/env bash
set -euo pipefail
# Source shared helpers
MUNITOR_HELPERS="${MUNITOR_HELPERS:-$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/munitor_helpers.sh}"
# shellcheck source=munitor_helpers.sh
if [[ -f "${MUNITOR_HELPERS}" ]]; then source "${MUNITOR_HELPERS}"
elif ! type munitor_header &>/dev/null; then
munitor_header() { echo "=== Munitor: ${1:-unknown} ==="; }
munitor_check_tool() { command -v "$1" &>/dev/null || { echo "ERROR: $1 not found"; exit 1; }; }
munitor_download_with_retry() { curl -fsSL --retry 3 "$1" -o "$2"; }
fi
MIN="${MIN_INSTRUCTION:-70}"
munitor_header "gradle_jacoco (min: ${MIN}%)"
# Validate MIN_INSTRUCTION is numeric
if ! [[ "${MIN}" =~ ^[0-9]+$ ]]; then
echo "ERROR: MIN_INSTRUCTION must be a whole number, got '${MIN}'."
exit 1
fi
if [[ ! -f "./gradlew" ]]; then
echo "ERROR: ./gradlew not found. Ensure the Gradle wrapper is present."
exit 1
fi
if [[ ! -x "./gradlew" ]]; then
chmod +x ./gradlew
fi
echo "Generating JaCoCo report..."
./gradlew jacocoTestReport --no-daemon --console=plain
# Show what JaCoCo produced for diagnostics
echo ""
echo "Searching for JaCoCo reports..."
find . -path "*/build/reports/jacoco" -type d 2>/dev/null | while read -r dir; do
echo " Found: ${dir}/"
for f in "${dir}"/test/*; do
[[ -e "${f}" ]] && echo " $(basename "${f}")"
done
done
# Find ALL CSV report files across all subprojects
CSV_FILES=$(find . -path "*/build/reports/jacoco/test/jacocoTestReport.csv" -type f)
if [[ -z "${CSV_FILES}" ]]; then
echo ""
echo "ERROR: No JaCoCo CSV reports found."
echo ""
echo "Gradle does not generate CSV reports by default. Add this to your"
echo "build.gradle.kts (in each module or via allprojects/subprojects):"
echo ""
echo ' tasks.jacocoTestReport {'
echo ' dependsOn(tasks.test)'
echo ' reports {'
echo ' csv.required.set(true)'
echo ' }'
echo ' }'
exit 1
fi
echo ""
echo "Found CSV reports:"
echo "${CSV_FILES}" | sed 's/^/ /'
# Parse CSV: sum INSTRUCTION_MISSED (col 4) and INSTRUCTION_COVERED (col 5) across ALL files
# CSV header: GROUP,PACKAGE,CLASS,INSTRUCTION_MISSED,INSTRUCTION_COVERED,...
read -r MISSED COVERED <<< "$(awk -F',' 'NR>1 { m+=$4; c+=$5 } END { print m, c }' ${CSV_FILES})"
TOTAL=$((MISSED + COVERED))
if [[ "${TOTAL}" -eq 0 ]]; then
echo "WARNING: No instruction data found in JaCoCo report (0 instructions total)."
echo "JaCoCo coverage: N/A (no code to measure)"
exit 0
fi
# Calculate percentage (integer arithmetic, truncated)
PCT=$((COVERED * 100 / TOTAL))
echo "JaCoCo coverage: ${PCT}% (${COVERED}/${TOTAL} instructions covered, minimum: ${MIN}%)"
if [[ "${PCT}" -lt "${MIN}" ]]; then
echo "FAILED: Coverage ${PCT}% is below minimum threshold of ${MIN}%."
exit 1
fi
echo "JaCoCo coverage passed (>= ${MIN}%)."
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -euo pipefail
# Source shared helpers
MUNITOR_HELPERS="${MUNITOR_HELPERS:-$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/munitor_helpers.sh}"
# shellcheck source=munitor_helpers.sh
if [[ -f "${MUNITOR_HELPERS}" ]]; then source "${MUNITOR_HELPERS}"
elif ! type munitor_header &>/dev/null; then
munitor_header() { echo "=== Munitor: ${1:-unknown} ==="; }
munitor_check_tool() { command -v "$1" &>/dev/null || { echo "ERROR: $1 not found"; exit 1; }; }
munitor_download_with_retry() { curl -fsSL --retry 3 "$1" -o "$2"; }
fi
munitor_header "gradle_pmd"
if [[ ! -f "./gradlew" ]]; then
echo "ERROR: ./gradlew not found. Ensure the Gradle wrapper is present."
exit 1
fi
if [[ ! -x "./gradlew" ]]; then
chmod +x ./gradlew
fi
# Detect if pmd plugin is applied
if ! ./gradlew tasks --all 2>/dev/null | grep -q "pmdMain"; then
echo "PMD plugin not applied, skipping."
exit 0
fi
echo "Running PMD..."
./gradlew pmdMain --no-daemon --console=plain
echo "PMD passed."
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -euo pipefail
# Source shared helpers
MUNITOR_HELPERS="${MUNITOR_HELPERS:-$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/munitor_helpers.sh}"
# shellcheck source=munitor_helpers.sh
if [[ -f "${MUNITOR_HELPERS}" ]]; then source "${MUNITOR_HELPERS}"
elif ! type munitor_header &>/dev/null; then
munitor_header() { echo "=== Munitor: ${1:-unknown} ==="; }
munitor_check_tool() { command -v "$1" &>/dev/null || { echo "ERROR: $1 not found"; exit 1; }; }
munitor_download_with_retry() { curl -fsSL --retry 3 "$1" -o "$2"; }
fi
munitor_header "gradle_spotbugs"
if [[ ! -f "./gradlew" ]]; then
echo "ERROR: ./gradlew not found. Ensure the Gradle wrapper is present."
exit 1
fi
if [[ ! -x "./gradlew" ]]; then
chmod +x ./gradlew
fi
# Detect if spotbugs plugin is applied
if ! ./gradlew tasks --all 2>/dev/null | grep -q "spotbugsMain"; then
echo "SpotBugs plugin not applied, skipping."
exit 0
fi
echo "Running SpotBugs..."
./gradlew spotbugsMain --no-daemon --console=plain
echo "SpotBugs passed."
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
# Source shared helpers
MUNITOR_HELPERS="${MUNITOR_HELPERS:-$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/munitor_helpers.sh}"
# shellcheck source=munitor_helpers.sh
if [[ -f "${MUNITOR_HELPERS}" ]]; then source "${MUNITOR_HELPERS}"
elif ! type munitor_header &>/dev/null; then
munitor_header() { echo "=== Munitor: ${1:-unknown} ==="; }
munitor_check_tool() { command -v "$1" &>/dev/null || { echo "ERROR: $1 not found"; exit 1; }; }
munitor_download_with_retry() { curl -fsSL --retry 3 "$1" -o "$2"; }
fi
munitor_header "gradle_test"
if [[ ! -f "./gradlew" ]]; then
echo "ERROR: ./gradlew not found. Ensure the Gradle wrapper is present."
exit 1
fi
if [[ ! -x "./gradlew" ]]; then
chmod +x ./gradlew
fi
echo "Running Gradle tests..."
./gradlew test --no-daemon --console=plain
echo "Gradle tests complete."
+496 -11
View File
@@ -110,17 +110,23 @@ extract_munitor_vars() {
fi
# --- Org defaults (overridable via .munitor.yml org section) ---
local ORG_DOCKER_REGISTRY=$(yq '.org.docker_registry // "ghcr.io/KofTwentyTwo"' "${config_file}")
local ORG_NPM_SCOPE=$(yq '.org.npm_scope // "@koftwentytwo"' "${config_file}")
local ORG_CI_EMAIL=$(yq '.org.ci_email // "munitor-ci@koftwentytwo.com"' "${config_file}")
local ORG_CI_NAME=$(yq '.org.ci_name // "Munitor CI"' "${config_file}")
local ORG_ORB_SLUG=$(yq '.org.orb_slug // "kof22/munitor"' "${config_file}")
local ORG_DOCKER_REGISTRY
ORG_DOCKER_REGISTRY=$(yq '.org.docker_registry // "ghcr.io/KofTwentyTwo"' "${config_file}")
local ORG_NPM_SCOPE
ORG_NPM_SCOPE=$(yq '.org.npm_scope // "@koftwentytwo"' "${config_file}")
local ORG_CI_EMAIL
ORG_CI_EMAIL=$(yq '.org.ci_email // "munitor-ci@koftwentytwo.com"' "${config_file}")
local ORG_CI_NAME
ORG_CI_NAME=$(yq '.org.ci_name // "Munitor CI"' "${config_file}")
local ORG_ORB_SLUG
ORG_ORB_SLUG=$(yq '.org.orb_slug // "kof22/munitor"' "${config_file}")
# Core pipeline settings
MUNITOR_PIPELINE=$(yq '.pipeline' "${config_file}")
MUNITOR_ORB_VERSION=$(yq '.orb_version // ""' "${config_file}")
MUNITOR_IMAGE_NAME=$(yq '.image_name // ""' "${config_file}")
MUNITOR_JAVA_VERSION=$(yq '.java_version // "21"' "${config_file}")
MUNITOR_SERVER_MODULE=$(yq '.server_module // ""' "${config_file}")
MUNITOR_NODE_VERSION=$(yq '.node_version // "20"' "${config_file}")
MUNITOR_SONAR_PROJECT_KEY=$(yq '.sonar.project_key // ""' "${config_file}")
MUNITOR_DOCKER_REGISTRY=$(yq '.docker.registry // ""' "${config_file}")
@@ -261,9 +267,13 @@ extract_munitor_vars() {
esac
fi
MUNITOR_HEALTH_DB=$(yq '.health.db // false' "${config_file}")
MUNITOR_HEALTH_MIGRATIONS=$(yq '.health.migrations // ""' "${config_file}")
if [[ "${MUNITOR_HEALTH_MIGRATIONS}" == "null" ]]; then MUNITOR_HEALTH_MIGRATIONS=""; fi
MUNITOR_HEALTH_SMOKE_PATHS=$(yq '.health.smoke_paths // [] | join(",")' "${config_file}")
if [[ "${MUNITOR_HEALTH_SMOKE_PATHS}" == "null" ]]; then MUNITOR_HEALTH_SMOKE_PATHS=""; fi
# Supplemental images (e.g., migrations, db-backup)
MUNITOR_SUPPLEMENTAL_IMAGES_JSON=$(yq '.supplemental_images // [] | tojson' "${config_file}")
MUNITOR_SUPPLEMENTAL_IMAGES_JSON=$(yq -o=json -I=0 '.supplemental_images // []' "${config_file}")
if [[ "${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}" == "[]" || "${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}" == "null" ]]; then
MUNITOR_SUPPLEMENTAL="false"
MUNITOR_SUPPLEMENTAL_IMAGES_JSON="[]"
@@ -291,7 +301,7 @@ extract_munitor_vars() {
fi
# Export all variables
export MUNITOR_PIPELINE MUNITOR_ORB_VERSION MUNITOR_IMAGE_NAME MUNITOR_JAVA_VERSION MUNITOR_NODE_VERSION
export MUNITOR_PIPELINE MUNITOR_ORB_VERSION MUNITOR_IMAGE_NAME MUNITOR_JAVA_VERSION MUNITOR_NODE_VERSION MUNITOR_SERVER_MODULE
export MUNITOR_SONAR_PROJECT_KEY MUNITOR_DOCKER_REGISTRY MUNITOR_CD_REPO MUNITOR_CD_FORMAT
export MUNITOR_CD_ENV_DEVELOP MUNITOR_CD_ENV_STAGING MUNITOR_CD_ENV_PROD MUNITOR_CD_ENV_RELEASE
export MUNITOR_COVERAGE_MIN MUNITOR_E2E MUNITOR_SBOM MUNITOR_OWASP
@@ -308,7 +318,7 @@ extract_munitor_vars() {
export MUNITOR_KUBE_LINTER_CONFIG MUNITOR_YAMLLINT_PATHS
export MUNITOR_NODE_FRAMEWORK MUNITOR_PACKAGE_MANAGER
export MUNITOR_CI_EMAIL MUNITOR_CI_NAME MUNITOR_NPM_DEFAULT_SCOPE MUNITOR_ORB_SLUG
export MUNITOR_HEALTH_PATH MUNITOR_HEALTH_PORT MUNITOR_HEALTH_DB
export MUNITOR_HEALTH_PATH MUNITOR_HEALTH_PORT MUNITOR_HEALTH_DB MUNITOR_HEALTH_MIGRATIONS MUNITOR_HEALTH_SMOKE_PATHS
export MUNITOR_SUPPLEMENTAL MUNITOR_SUPPLEMENTAL_IMAGES_JSON
export MUNITOR_SONAR MUNITOR_CD MUNITOR_NVD
}
@@ -316,7 +326,7 @@ extract_munitor_vars() {
# Build the envsubst variable list
get_envsubst_vars() {
# shellcheck disable=SC2016
echo '${MUNITOR_PIPELINE} ${MUNITOR_ORB_VERSION} ${MUNITOR_IMAGE_NAME} ${MUNITOR_JAVA_VERSION} ${MUNITOR_NODE_VERSION} ${MUNITOR_SONAR_PROJECT_KEY} ${MUNITOR_DOCKER_REGISTRY} ${MUNITOR_CD_REPO} ${MUNITOR_CD_FORMAT} ${MUNITOR_CD_ENV_DEVELOP} ${MUNITOR_CD_ENV_STAGING} ${MUNITOR_CD_ENV_PROD} ${MUNITOR_CD_ENV_RELEASE} ${MUNITOR_COVERAGE_MIN} ${MUNITOR_E2E} ${MUNITOR_SBOM} ${MUNITOR_OWASP} ${MUNITOR_CONTEXT_REGISTRY} ${MUNITOR_CONTEXT_GITHUB} ${MUNITOR_CONTEXT_SONAR} ${MUNITOR_CONTEXT_NVD} ${MUNITOR_NPM_AUTH} ${MUNITOR_NPM_SCOPES} ${MUNITOR_SERVICES_JSON} ${MUNITOR_TEST_SETUP_SCRIPT} ${MUNITOR_TEST_COMMANDS_JSON} ${MUNITOR_COVERAGE_TOOL} ${MUNITOR_COVERAGE_COMMAND} ${MUNITOR_GITHUB_RELEASE} ${MUNITOR_SAST} ${MUNITOR_SAST_FAIL_ON_FINDINGS} ${MUNITOR_HEALTH_PATH} ${MUNITOR_HEALTH_PORT} ${MUNITOR_HEALTH_DB} ${MUNITOR_TF_PATH} ${MUNITOR_TF_LIVE_PATH} ${MUNITOR_TF_ENVIRONMENTS} ${MUNITOR_CHECKOV_SKIP} ${MUNITOR_KUSTOMIZE_VERSION} ${MUNITOR_KUSTOMIZE_BASE_PATH} ${MUNITOR_KUSTOMIZE_LOAD_RESTRICTOR} ${MUNITOR_KUSTOMIZE_SCAN_OVERLAY} ${MUNITOR_KUSTOMIZE_OVERLAYS} ${MUNITOR_KUSTOMIZE_OVERLAY_DIR} ${MUNITOR_KUBE_LINTER_CONFIG} ${MUNITOR_YAMLLINT_PATHS} ${MUNITOR_CI_EMAIL} ${MUNITOR_CI_NAME} ${MUNITOR_NPM_DEFAULT_SCOPE} ${MUNITOR_ORB_SLUG} ${MUNITOR_PACKAGE_MANAGER} ${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
echo '${MUNITOR_PIPELINE} ${MUNITOR_ORB_VERSION} ${MUNITOR_IMAGE_NAME} ${MUNITOR_JAVA_VERSION} ${MUNITOR_NODE_VERSION} ${MUNITOR_SONAR_PROJECT_KEY} ${MUNITOR_DOCKER_REGISTRY} ${MUNITOR_CD_REPO} ${MUNITOR_CD_FORMAT} ${MUNITOR_CD_ENV_DEVELOP} ${MUNITOR_CD_ENV_STAGING} ${MUNITOR_CD_ENV_PROD} ${MUNITOR_CD_ENV_RELEASE} ${MUNITOR_COVERAGE_MIN} ${MUNITOR_E2E} ${MUNITOR_SBOM} ${MUNITOR_OWASP} ${MUNITOR_CONTEXT_REGISTRY} ${MUNITOR_CONTEXT_GITHUB} ${MUNITOR_CONTEXT_SONAR} ${MUNITOR_CONTEXT_NVD} ${MUNITOR_NPM_AUTH} ${MUNITOR_NPM_SCOPES} ${MUNITOR_SERVICES_JSON} ${MUNITOR_TEST_SETUP_SCRIPT} ${MUNITOR_TEST_COMMANDS_JSON} ${MUNITOR_COVERAGE_TOOL} ${MUNITOR_COVERAGE_COMMAND} ${MUNITOR_GITHUB_RELEASE} ${MUNITOR_SAST} ${MUNITOR_SAST_FAIL_ON_FINDINGS} ${MUNITOR_HEALTH_PATH} ${MUNITOR_HEALTH_PORT} ${MUNITOR_HEALTH_DB} ${MUNITOR_HEALTH_MIGRATIONS} ${MUNITOR_HEALTH_SMOKE_PATHS} ${MUNITOR_TF_PATH} ${MUNITOR_TF_LIVE_PATH} ${MUNITOR_TF_ENVIRONMENTS} ${MUNITOR_CHECKOV_SKIP} ${MUNITOR_KUSTOMIZE_VERSION} ${MUNITOR_KUSTOMIZE_BASE_PATH} ${MUNITOR_KUSTOMIZE_LOAD_RESTRICTOR} ${MUNITOR_KUSTOMIZE_SCAN_OVERLAY} ${MUNITOR_KUSTOMIZE_OVERLAYS} ${MUNITOR_KUSTOMIZE_OVERLAY_DIR} ${MUNITOR_KUBE_LINTER_CONFIG} ${MUNITOR_YAMLLINT_PATHS} ${MUNITOR_CI_EMAIL} ${MUNITOR_CI_NAME} ${MUNITOR_NPM_DEFAULT_SCOPE} ${MUNITOR_ORB_SLUG} ${MUNITOR_PACKAGE_MANAGER} ${MUNITOR_SUPPLEMENTAL_IMAGES_JSON} ${MUNITOR_SERVER_MODULE}'
}
MUNITOR_EXTRACT_EOF
@@ -500,6 +510,8 @@ workflows:
health_path: ${MUNITOR_HEALTH_PATH}
health_port: "${MUNITOR_HEALTH_PORT}"
health_db: "${MUNITOR_HEALTH_DB}"
health_migrations: "${MUNITOR_HEALTH_MIGRATIONS}"
health_smoke_paths: "${MUNITOR_HEALTH_SMOKE_PATHS}"
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
@@ -595,6 +607,330 @@ workflows:
health_path: ${MUNITOR_HEALTH_PATH}
health_port: "${MUNITOR_HEALTH_PORT}"
health_db: "${MUNITOR_HEALTH_DB}"
health_migrations: "${MUNITOR_HEALTH_MIGRATIONS}"
health_smoke_paths: "${MUNITOR_HEALTH_SMOKE_PATHS}"
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
requires:
- build-and-test
context:
- ${MUNITOR_CONTEXT_REGISTRY}
filters:
branches:
only: main
##IF_CD##
- munitor/update_cd_repo:
name: update-cd-repo
cd_repo: ${MUNITOR_CD_REPO}
environment: ${MUNITOR_CD_ENV_PROD}
cd_format: ${MUNITOR_CD_FORMAT}
cd_image_name: ${MUNITOR_DOCKER_REGISTRY}/${MUNITOR_IMAGE_NAME}
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
ci_git_email: '${MUNITOR_CI_EMAIL}'
ci_git_name: '${MUNITOR_CI_NAME}'
requires:
- docker-build-push
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only: main
##ENDIF_CD##
##IF_GITHUB_RELEASE##
- munitor/github_release:
name: github-release
requires:
- docker-build-push
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only: main
##ENDIF_GITHUB_RELEASE##
MUNITOR_TPL_EOF
cat > /tmp/munitor/templates/gradle-webapp.yml.tpl << 'MUNITOR_TPL_EOF'
version: 2.1
orbs:
munitor: ${MUNITOR_ORB_SLUG}@${MUNITOR_ORB_VERSION}
workflows:
pr-checks:
jobs:
- munitor/gradle_build_and_test:
name: build-and-test
java_version: "${MUNITOR_JAVA_VERSION}"
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
- munitor/secrets_scan:
name: secrets-scan
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
- munitor/sast_scan:
name: sast-scan
fail_on_findings: "${MUNITOR_SAST_FAIL_ON_FINDINGS}"
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
- munitor/gradle_code_quality:
name: code-quality
java_version: "${MUNITOR_JAVA_VERSION}"
requires:
- build-and-test
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
- munitor/gradle_coverage:
name: coverage
java_version: "${MUNITOR_JAVA_VERSION}"
min_instruction: "${MUNITOR_COVERAGE_MIN}"
requires:
- build-and-test
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
- munitor/security_scan:
name: security-scan
java_version: "${MUNITOR_JAVA_VERSION}"
owasp: ${MUNITOR_OWASP}
##IF_OWASP##
##IF_NVD##
context:
- ${MUNITOR_CONTEXT_NVD}
##ENDIF_NVD##
##ENDIF_OWASP##
requires:
- build-and-test
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
##IF_E2E##
- munitor/mvn_e2e_test:
name: e2e-tests
java_version: "${MUNITOR_JAVA_VERSION}"
requires:
- build-and-test
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
##ENDIF_E2E##
##INCLUDE_DEPLOY gradle-webapp-deploy develop develop ${MUNITOR_CD_ENV_DEVELOP}##
##INCLUDE_DEPLOY gradle-webapp-deploy staging staging ${MUNITOR_CD_ENV_STAGING}##
release-candidate:
jobs:
- munitor/gradle_build_and_test:
name: build-and-test
java_version: "${MUNITOR_JAVA_VERSION}"
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only:
- /release\/.*/
- munitor/secrets_scan:
name: secrets-scan
filters:
branches:
only:
- /release\/.*/
- munitor/sast_scan:
name: sast-scan
fail_on_findings: "${MUNITOR_SAST_FAIL_ON_FINDINGS}"
filters:
branches:
only:
- /release\/.*/
- munitor/gradle_code_quality:
name: code-quality
java_version: "${MUNITOR_JAVA_VERSION}"
requires:
- build-and-test
filters:
branches:
only:
- /release\/.*/
- munitor/gradle_coverage:
name: coverage
java_version: "${MUNITOR_JAVA_VERSION}"
min_instruction: "${MUNITOR_COVERAGE_MIN}"
requires:
- build-and-test
filters:
branches:
only:
- /release\/.*/
- munitor/security_scan:
name: security-scan
java_version: "${MUNITOR_JAVA_VERSION}"
owasp: ${MUNITOR_OWASP}
##IF_OWASP##
##IF_NVD##
context:
- ${MUNITOR_CONTEXT_NVD}
##ENDIF_NVD##
##ENDIF_OWASP##
requires:
- build-and-test
filters:
branches:
only:
- /release\/.*/
##IF_E2E##
- munitor/mvn_e2e_test:
name: e2e-tests
java_version: "${MUNITOR_JAVA_VERSION}"
requires:
- build-and-test
filters:
branches:
only:
- /release\/.*/
##ENDIF_E2E##
##IF_SONAR##
- munitor/sonar_scan:
name: sonar-scan
java_version: "${MUNITOR_JAVA_VERSION}"
sonar_project_key: ${MUNITOR_SONAR_PROJECT_KEY}
requires:
- build-and-test
- coverage
context:
- ${MUNITOR_CONTEXT_SONAR}
filters:
branches:
only:
- /release\/.*/
##ENDIF_SONAR##
- munitor/docker_build_push:
name: docker-build-push
image_name: ${MUNITOR_IMAGE_NAME}
registry: ${MUNITOR_DOCKER_REGISTRY}
health_path: ${MUNITOR_HEALTH_PATH}
health_port: "${MUNITOR_HEALTH_PORT}"
health_db: "${MUNITOR_HEALTH_DB}"
health_migrations: "${MUNITOR_HEALTH_MIGRATIONS}"
health_smoke_paths: "${MUNITOR_HEALTH_SMOKE_PATHS}"
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
requires:
- code-quality
- coverage
- secrets-scan
- sast-scan
- security-scan
##IF_SONAR##
- sonar-scan
##ENDIF_SONAR##
##IF_E2E##
- e2e-tests
##ENDIF_E2E##
context:
- ${MUNITOR_CONTEXT_REGISTRY}
filters:
branches:
only:
- /release\/.*/
##IF_SBOM##
- munitor/sbom:
name: sbom
java_version: "${MUNITOR_JAVA_VERSION}"
context:
- ${MUNITOR_CONTEXT_GITHUB}
requires:
- code-quality
- coverage
- secrets-scan
- sast-scan
- security-scan
##IF_SONAR##
- sonar-scan
##ENDIF_SONAR##
##IF_E2E##
- e2e-tests
##ENDIF_E2E##
filters:
branches:
only:
- /release\/.*/
##ENDIF_SBOM##
##IF_CD##
- munitor/update_cd_repo:
name: update-cd-repo
cd_repo: ${MUNITOR_CD_REPO}
environment: ${MUNITOR_CD_ENV_RELEASE}
cd_format: ${MUNITOR_CD_FORMAT}
cd_image_name: ${MUNITOR_DOCKER_REGISTRY}/${MUNITOR_IMAGE_NAME}
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
ci_git_email: '${MUNITOR_CI_EMAIL}'
ci_git_name: '${MUNITOR_CI_NAME}'
requires:
- docker-build-push
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only:
- /release\/.*/
##ENDIF_CD##
##IF_GITHUB_RELEASE##
- munitor/github_release:
name: github-release
requires:
- docker-build-push
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only:
- /release\/.*/
##ENDIF_GITHUB_RELEASE##
production:
jobs:
- munitor/gradle_build_and_test:
name: build-and-test
java_version: "${MUNITOR_JAVA_VERSION}"
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only: main
- munitor/docker_build_push:
name: docker-build-push
image_name: ${MUNITOR_IMAGE_NAME}
registry: ${MUNITOR_DOCKER_REGISTRY}
health_path: ${MUNITOR_HEALTH_PATH}
health_port: "${MUNITOR_HEALTH_PORT}"
health_db: "${MUNITOR_HEALTH_DB}"
health_migrations: "${MUNITOR_HEALTH_MIGRATIONS}"
health_smoke_paths: "${MUNITOR_HEALTH_SMOKE_PATHS}"
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
@@ -1921,6 +2257,155 @@ cat > /tmp/munitor/templates/partials/java-webapp-deploy.yml.tpl << 'MUNITOR_PAR
##ENDIF_CD##
MUNITOR_PARTIAL_EOF
cat > /tmp/munitor/templates/partials/gradle-webapp-deploy.yml.tpl << 'MUNITOR_PARTIAL_EOF'
__WORKFLOW_NAME__:
jobs:
- munitor/gradle_build_and_test:
name: build-and-test
java_version: "${MUNITOR_JAVA_VERSION}"
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only: __BRANCH_FILTER__
- munitor/secrets_scan:
name: secrets-scan
filters:
branches:
only: __BRANCH_FILTER__
- munitor/sast_scan:
name: sast-scan
fail_on_findings: "${MUNITOR_SAST_FAIL_ON_FINDINGS}"
filters:
branches:
only: __BRANCH_FILTER__
- munitor/gradle_code_quality:
name: code-quality
java_version: "${MUNITOR_JAVA_VERSION}"
requires:
- build-and-test
filters:
branches:
only: __BRANCH_FILTER__
- munitor/gradle_coverage:
name: coverage
java_version: "${MUNITOR_JAVA_VERSION}"
min_instruction: "${MUNITOR_COVERAGE_MIN}"
requires:
- build-and-test
filters:
branches:
only: __BRANCH_FILTER__
- munitor/security_scan:
name: security-scan
java_version: "${MUNITOR_JAVA_VERSION}"
owasp: ${MUNITOR_OWASP}
##IF_OWASP##
##IF_NVD##
context:
- ${MUNITOR_CONTEXT_NVD}
##ENDIF_NVD##
##ENDIF_OWASP##
requires:
- build-and-test
filters:
branches:
only: __BRANCH_FILTER__
##IF_E2E##
- munitor/mvn_e2e_test:
name: e2e-tests
java_version: "${MUNITOR_JAVA_VERSION}"
requires:
- build-and-test
filters:
branches:
only: __BRANCH_FILTER__
##ENDIF_E2E##
##IF_SONAR##
- munitor/sonar_scan:
name: sonar-scan
java_version: "${MUNITOR_JAVA_VERSION}"
sonar_project_key: ${MUNITOR_SONAR_PROJECT_KEY}
requires:
- build-and-test
- coverage
context:
- ${MUNITOR_CONTEXT_SONAR}
filters:
branches:
only: __BRANCH_FILTER__
##ENDIF_SONAR##
- munitor/docker_build_push:
name: docker-build-push
image_name: ${MUNITOR_IMAGE_NAME}
registry: ${MUNITOR_DOCKER_REGISTRY}
health_path: ${MUNITOR_HEALTH_PATH}
health_port: "${MUNITOR_HEALTH_PORT}"
health_db: "${MUNITOR_HEALTH_DB}"
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
requires:
- code-quality
- coverage
- secrets-scan
- sast-scan
- security-scan
##IF_SONAR##
- sonar-scan
##ENDIF_SONAR##
##IF_E2E##
- e2e-tests
##ENDIF_E2E##
context:
- ${MUNITOR_CONTEXT_REGISTRY}
filters:
branches:
only: __BRANCH_FILTER__
##IF_SBOM##
- munitor/sbom:
name: sbom
java_version: "${MUNITOR_JAVA_VERSION}"
context:
- ${MUNITOR_CONTEXT_GITHUB}
requires:
- code-quality
- coverage
- secrets-scan
- sast-scan
- security-scan
##IF_SONAR##
- sonar-scan
##ENDIF_SONAR##
##IF_E2E##
- e2e-tests
##ENDIF_E2E##
filters:
branches:
only: __BRANCH_FILTER__
##ENDIF_SBOM##
##IF_CD##
- munitor/update_cd_repo:
name: update-cd-repo
cd_repo: ${MUNITOR_CD_REPO}
environment: __CD_ENVIRONMENT__
cd_format: ${MUNITOR_CD_FORMAT}
cd_image_name: ${MUNITOR_DOCKER_REGISTRY}/${MUNITOR_IMAGE_NAME}
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
ci_git_email: '${MUNITOR_CI_EMAIL}'
ci_git_name: '${MUNITOR_CI_NAME}'
requires:
- docker-build-push
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only: __BRANCH_FILTER__
##ENDIF_CD##
MUNITOR_PARTIAL_EOF
cat > /tmp/munitor/templates/partials/node-api-deploy.yml.tpl << 'MUNITOR_PARTIAL_EOF'
__WORKFLOW_NAME__:
jobs:
@@ -2298,7 +2783,7 @@ validate_required_fields() {
[[ -z "$(yq '.orb_version // ""' "${config}")" ]] && missing+=("orb_version")
case "${pipeline}" in
java-webapp|node-api|node-webapp)
java-webapp|gradle-webapp|node-api|node-webapp)
[[ -z "$(yq '.image_name // ""' "${config}")" ]] && missing+=("image_name")
[[ -z "$(yq '.docker.registry // ""' "${config}")" ]] && missing+=("docker.registry")
;;
@@ -2322,7 +2807,7 @@ validate_required_fields "${PIPELINE_TYPE}" "${CONFIG_FILE}"
# --------------------------------------------------------------------------
# Warn about unknown top-level keys (catches typos like java_verion)
# --------------------------------------------------------------------------
KNOWN_KEYS="pipeline orb_version image_name java_version node_version sonar docker cd coverage e2e sbom owasp contexts npm node services test terraform sast health kustomize kube_linter_config package_manager yamllint_paths supplemental_images"
KNOWN_KEYS="pipeline orb_version image_name server_module java_version node_version sonar docker cd coverage e2e sbom owasp contexts npm node services test terraform sast health kustomize kube_linter_config package_manager yamllint_paths supplemental_images"
ACTUAL_KEYS=$(yq 'keys | .[]' "${CONFIG_FILE}" 2>/dev/null || true)
for key in ${ACTUAL_KEYS}; do
if ! echo "${KNOWN_KEYS}" | grep -qw "${key}"; then
+67 -9
View File
@@ -110,17 +110,23 @@ extract_munitor_vars() {
fi
# --- Org defaults (overridable via .munitor.yml org section) ---
local ORG_DOCKER_REGISTRY=$(yq '.org.docker_registry // "ghcr.io/KofTwentyTwo"' "${config_file}")
local ORG_NPM_SCOPE=$(yq '.org.npm_scope // "@koftwentytwo"' "${config_file}")
local ORG_CI_EMAIL=$(yq '.org.ci_email // "munitor-ci@koftwentytwo.com"' "${config_file}")
local ORG_CI_NAME=$(yq '.org.ci_name // "Munitor CI"' "${config_file}")
local ORG_ORB_SLUG=$(yq '.org.orb_slug // "kof22/munitor"' "${config_file}")
local ORG_DOCKER_REGISTRY
ORG_DOCKER_REGISTRY=$(yq '.org.docker_registry // "ghcr.io/KofTwentyTwo"' "${config_file}")
local ORG_NPM_SCOPE
ORG_NPM_SCOPE=$(yq '.org.npm_scope // "@koftwentytwo"' "${config_file}")
local ORG_CI_EMAIL
ORG_CI_EMAIL=$(yq '.org.ci_email // "munitor-ci@koftwentytwo.com"' "${config_file}")
local ORG_CI_NAME
ORG_CI_NAME=$(yq '.org.ci_name // "Munitor CI"' "${config_file}")
local ORG_ORB_SLUG
ORG_ORB_SLUG=$(yq '.org.orb_slug // "kof22/munitor"' "${config_file}")
# Core pipeline settings
MUNITOR_PIPELINE=$(yq '.pipeline' "${config_file}")
MUNITOR_ORB_VERSION=$(yq '.orb_version // ""' "${config_file}")
MUNITOR_IMAGE_NAME=$(yq '.image_name // ""' "${config_file}")
MUNITOR_JAVA_VERSION=$(yq '.java_version // "21"' "${config_file}")
MUNITOR_SERVER_MODULE=$(yq '.server_module // ""' "${config_file}")
MUNITOR_NODE_VERSION=$(yq '.node_version // "20"' "${config_file}")
MUNITOR_SONAR_PROJECT_KEY=$(yq '.sonar.project_key // ""' "${config_file}")
MUNITOR_DOCKER_REGISTRY=$(yq '.docker.registry // ""' "${config_file}")
@@ -261,9 +267,13 @@ extract_munitor_vars() {
esac
fi
MUNITOR_HEALTH_DB=$(yq '.health.db // false' "${config_file}")
MUNITOR_HEALTH_MIGRATIONS=$(yq '.health.migrations // ""' "${config_file}")
if [[ "${MUNITOR_HEALTH_MIGRATIONS}" == "null" ]]; then MUNITOR_HEALTH_MIGRATIONS=""; fi
MUNITOR_HEALTH_SMOKE_PATHS=$(yq '.health.smoke_paths // [] | join(",")' "${config_file}")
if [[ "${MUNITOR_HEALTH_SMOKE_PATHS}" == "null" ]]; then MUNITOR_HEALTH_SMOKE_PATHS=""; fi
# Supplemental images (e.g., migrations, db-backup)
MUNITOR_SUPPLEMENTAL_IMAGES_JSON=$(yq '.supplemental_images // [] | tojson' "${config_file}")
MUNITOR_SUPPLEMENTAL_IMAGES_JSON=$(yq -o=json -I=0 '.supplemental_images // []' "${config_file}")
if [[ "${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}" == "[]" || "${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}" == "null" ]]; then
MUNITOR_SUPPLEMENTAL="false"
MUNITOR_SUPPLEMENTAL_IMAGES_JSON="[]"
@@ -291,7 +301,7 @@ extract_munitor_vars() {
fi
# Export all variables
export MUNITOR_PIPELINE MUNITOR_ORB_VERSION MUNITOR_IMAGE_NAME MUNITOR_JAVA_VERSION MUNITOR_NODE_VERSION
export MUNITOR_PIPELINE MUNITOR_ORB_VERSION MUNITOR_IMAGE_NAME MUNITOR_JAVA_VERSION MUNITOR_NODE_VERSION MUNITOR_SERVER_MODULE
export MUNITOR_SONAR_PROJECT_KEY MUNITOR_DOCKER_REGISTRY MUNITOR_CD_REPO MUNITOR_CD_FORMAT
export MUNITOR_CD_ENV_DEVELOP MUNITOR_CD_ENV_STAGING MUNITOR_CD_ENV_PROD MUNITOR_CD_ENV_RELEASE
export MUNITOR_COVERAGE_MIN MUNITOR_E2E MUNITOR_SBOM MUNITOR_OWASP
@@ -308,7 +318,7 @@ extract_munitor_vars() {
export MUNITOR_KUBE_LINTER_CONFIG MUNITOR_YAMLLINT_PATHS
export MUNITOR_NODE_FRAMEWORK MUNITOR_PACKAGE_MANAGER
export MUNITOR_CI_EMAIL MUNITOR_CI_NAME MUNITOR_NPM_DEFAULT_SCOPE MUNITOR_ORB_SLUG
export MUNITOR_HEALTH_PATH MUNITOR_HEALTH_PORT MUNITOR_HEALTH_DB
export MUNITOR_HEALTH_PATH MUNITOR_HEALTH_PORT MUNITOR_HEALTH_DB MUNITOR_HEALTH_MIGRATIONS MUNITOR_HEALTH_SMOKE_PATHS
export MUNITOR_SUPPLEMENTAL MUNITOR_SUPPLEMENTAL_IMAGES_JSON
export MUNITOR_SONAR MUNITOR_CD MUNITOR_NVD
}
@@ -316,7 +326,7 @@ extract_munitor_vars() {
# Build the envsubst variable list
get_envsubst_vars() {
# shellcheck disable=SC2016
echo '${MUNITOR_PIPELINE} ${MUNITOR_ORB_VERSION} ${MUNITOR_IMAGE_NAME} ${MUNITOR_JAVA_VERSION} ${MUNITOR_NODE_VERSION} ${MUNITOR_SONAR_PROJECT_KEY} ${MUNITOR_DOCKER_REGISTRY} ${MUNITOR_CD_REPO} ${MUNITOR_CD_FORMAT} ${MUNITOR_CD_ENV_DEVELOP} ${MUNITOR_CD_ENV_STAGING} ${MUNITOR_CD_ENV_PROD} ${MUNITOR_CD_ENV_RELEASE} ${MUNITOR_COVERAGE_MIN} ${MUNITOR_E2E} ${MUNITOR_SBOM} ${MUNITOR_OWASP} ${MUNITOR_CONTEXT_REGISTRY} ${MUNITOR_CONTEXT_GITHUB} ${MUNITOR_CONTEXT_SONAR} ${MUNITOR_CONTEXT_NVD} ${MUNITOR_NPM_AUTH} ${MUNITOR_NPM_SCOPES} ${MUNITOR_SERVICES_JSON} ${MUNITOR_TEST_SETUP_SCRIPT} ${MUNITOR_TEST_COMMANDS_JSON} ${MUNITOR_COVERAGE_TOOL} ${MUNITOR_COVERAGE_COMMAND} ${MUNITOR_GITHUB_RELEASE} ${MUNITOR_SAST} ${MUNITOR_SAST_FAIL_ON_FINDINGS} ${MUNITOR_HEALTH_PATH} ${MUNITOR_HEALTH_PORT} ${MUNITOR_HEALTH_DB} ${MUNITOR_TF_PATH} ${MUNITOR_TF_LIVE_PATH} ${MUNITOR_TF_ENVIRONMENTS} ${MUNITOR_CHECKOV_SKIP} ${MUNITOR_KUSTOMIZE_VERSION} ${MUNITOR_KUSTOMIZE_BASE_PATH} ${MUNITOR_KUSTOMIZE_LOAD_RESTRICTOR} ${MUNITOR_KUSTOMIZE_SCAN_OVERLAY} ${MUNITOR_KUSTOMIZE_OVERLAYS} ${MUNITOR_KUSTOMIZE_OVERLAY_DIR} ${MUNITOR_KUBE_LINTER_CONFIG} ${MUNITOR_YAMLLINT_PATHS} ${MUNITOR_CI_EMAIL} ${MUNITOR_CI_NAME} ${MUNITOR_NPM_DEFAULT_SCOPE} ${MUNITOR_ORB_SLUG} ${MUNITOR_PACKAGE_MANAGER} ${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
echo '${MUNITOR_PIPELINE} ${MUNITOR_ORB_VERSION} ${MUNITOR_IMAGE_NAME} ${MUNITOR_JAVA_VERSION} ${MUNITOR_NODE_VERSION} ${MUNITOR_SONAR_PROJECT_KEY} ${MUNITOR_DOCKER_REGISTRY} ${MUNITOR_CD_REPO} ${MUNITOR_CD_FORMAT} ${MUNITOR_CD_ENV_DEVELOP} ${MUNITOR_CD_ENV_STAGING} ${MUNITOR_CD_ENV_PROD} ${MUNITOR_CD_ENV_RELEASE} ${MUNITOR_COVERAGE_MIN} ${MUNITOR_E2E} ${MUNITOR_SBOM} ${MUNITOR_OWASP} ${MUNITOR_CONTEXT_REGISTRY} ${MUNITOR_CONTEXT_GITHUB} ${MUNITOR_CONTEXT_SONAR} ${MUNITOR_CONTEXT_NVD} ${MUNITOR_NPM_AUTH} ${MUNITOR_NPM_SCOPES} ${MUNITOR_SERVICES_JSON} ${MUNITOR_TEST_SETUP_SCRIPT} ${MUNITOR_TEST_COMMANDS_JSON} ${MUNITOR_COVERAGE_TOOL} ${MUNITOR_COVERAGE_COMMAND} ${MUNITOR_GITHUB_RELEASE} ${MUNITOR_SAST} ${MUNITOR_SAST_FAIL_ON_FINDINGS} ${MUNITOR_HEALTH_PATH} ${MUNITOR_HEALTH_PORT} ${MUNITOR_HEALTH_DB} ${MUNITOR_HEALTH_MIGRATIONS} ${MUNITOR_HEALTH_SMOKE_PATHS} ${MUNITOR_TF_PATH} ${MUNITOR_TF_LIVE_PATH} ${MUNITOR_TF_ENVIRONMENTS} ${MUNITOR_CHECKOV_SKIP} ${MUNITOR_KUSTOMIZE_VERSION} ${MUNITOR_KUSTOMIZE_BASE_PATH} ${MUNITOR_KUSTOMIZE_LOAD_RESTRICTOR} ${MUNITOR_KUSTOMIZE_SCAN_OVERLAY} ${MUNITOR_KUSTOMIZE_OVERLAYS} ${MUNITOR_KUSTOMIZE_OVERLAY_DIR} ${MUNITOR_KUBE_LINTER_CONFIG} ${MUNITOR_YAMLLINT_PATHS} ${MUNITOR_CI_EMAIL} ${MUNITOR_CI_NAME} ${MUNITOR_NPM_DEFAULT_SCOPE} ${MUNITOR_ORB_SLUG} ${MUNITOR_PACKAGE_MANAGER} ${MUNITOR_SUPPLEMENTAL_IMAGES_JSON} ${MUNITOR_SERVER_MODULE}'
}
MUNITOR_EXTRACT_EOF
@@ -466,6 +476,54 @@ ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+ExitOnOut
EXPOSE ${MUNITOR_HEALTH_PORT}
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \\
CMD wget --no-verbose --tries=1 --spider http://localhost:${MUNITOR_HEALTH_PORT}${MUNITOR_HEALTH_PATH} || exit 1
ENTRYPOINT ["sh", "-c", "java \$JAVA_OPTS -jar app.jar"]
DOCKERFILE
;;
gradle-webapp)
echo "Generating Dockerfile for gradle-webapp (java ${MUNITOR_JAVA_VERSION})"
# Find the shadow JAR in the server module's build output
SEARCH_DIR="build/libs"
if [[ -n "${MUNITOR_SERVER_MODULE:-}" ]]; then
SEARCH_DIR="${MUNITOR_SERVER_MODULE}/build/libs"
fi
APP_JAR=$(find "${SEARCH_DIR}" -maxdepth 1 -name "*.jar" \
! -name "*-plain.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" \
2>/dev/null | head -1) || true
if [[ -z "${APP_JAR}" ]]; then
echo "ERROR: No application JAR found in ${SEARCH_DIR}/" >&2
echo "Ensure shadowJar or bootJar is configured in build.gradle.kts" >&2
exit 1
fi
echo "Using JAR: ${APP_JAR}"
cat > Dockerfile <<DOCKERFILE
FROM eclipse-temurin:${MUNITOR_JAVA_VERSION}-jre-alpine
RUN apk update && apk upgrade --no-cache
RUN addgroup -g 1001 -S appgroup && \\
adduser -u 1001 -S appuser -G appgroup
WORKDIR /app
COPY ${APP_JAR} app.jar
RUN chown -R appuser:appgroup /app
USER appuser
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+ExitOnOutOfMemoryError"
EXPOSE ${MUNITOR_HEALTH_PORT}
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \\
CMD wget --no-verbose --tries=1 --spider http://localhost:${MUNITOR_HEALTH_PORT}${MUNITOR_HEALTH_PATH} || exit 1
+319
View File
@@ -0,0 +1,319 @@
version: 2.1
orbs:
munitor: ${MUNITOR_ORB_SLUG}@${MUNITOR_ORB_VERSION}
workflows:
pr-checks:
jobs:
- munitor/gradle_build_and_test:
name: build-and-test
java_version: "${MUNITOR_JAVA_VERSION}"
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
- munitor/secrets_scan:
name: secrets-scan
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
- munitor/sast_scan:
name: sast-scan
fail_on_findings: "${MUNITOR_SAST_FAIL_ON_FINDINGS}"
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
- munitor/gradle_code_quality:
name: code-quality
java_version: "${MUNITOR_JAVA_VERSION}"
requires:
- build-and-test
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
- munitor/gradle_coverage:
name: coverage
java_version: "${MUNITOR_JAVA_VERSION}"
min_instruction: "${MUNITOR_COVERAGE_MIN}"
requires:
- build-and-test
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
- munitor/security_scan:
name: security-scan
java_version: "${MUNITOR_JAVA_VERSION}"
owasp: ${MUNITOR_OWASP}
##IF_OWASP##
##IF_NVD##
context:
- ${MUNITOR_CONTEXT_NVD}
##ENDIF_NVD##
##ENDIF_OWASP##
requires:
- build-and-test
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
##IF_E2E##
- munitor/mvn_e2e_test:
name: e2e-tests
java_version: "${MUNITOR_JAVA_VERSION}"
requires:
- build-and-test
filters:
branches:
only:
- /feature\/.*/
- /hotfix\/.*/
##ENDIF_E2E##
##INCLUDE_DEPLOY gradle-webapp-deploy develop develop ${MUNITOR_CD_ENV_DEVELOP}##
##INCLUDE_DEPLOY gradle-webapp-deploy staging staging ${MUNITOR_CD_ENV_STAGING}##
release-candidate:
jobs:
- munitor/gradle_build_and_test:
name: build-and-test
java_version: "${MUNITOR_JAVA_VERSION}"
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only:
- /release\/.*/
- munitor/secrets_scan:
name: secrets-scan
filters:
branches:
only:
- /release\/.*/
- munitor/sast_scan:
name: sast-scan
fail_on_findings: "${MUNITOR_SAST_FAIL_ON_FINDINGS}"
filters:
branches:
only:
- /release\/.*/
- munitor/gradle_code_quality:
name: code-quality
java_version: "${MUNITOR_JAVA_VERSION}"
requires:
- build-and-test
filters:
branches:
only:
- /release\/.*/
- munitor/gradle_coverage:
name: coverage
java_version: "${MUNITOR_JAVA_VERSION}"
min_instruction: "${MUNITOR_COVERAGE_MIN}"
requires:
- build-and-test
filters:
branches:
only:
- /release\/.*/
- munitor/security_scan:
name: security-scan
java_version: "${MUNITOR_JAVA_VERSION}"
owasp: ${MUNITOR_OWASP}
##IF_OWASP##
##IF_NVD##
context:
- ${MUNITOR_CONTEXT_NVD}
##ENDIF_NVD##
##ENDIF_OWASP##
requires:
- build-and-test
filters:
branches:
only:
- /release\/.*/
##IF_E2E##
- munitor/mvn_e2e_test:
name: e2e-tests
java_version: "${MUNITOR_JAVA_VERSION}"
requires:
- build-and-test
filters:
branches:
only:
- /release\/.*/
##ENDIF_E2E##
##IF_SONAR##
- munitor/sonar_scan:
name: sonar-scan
java_version: "${MUNITOR_JAVA_VERSION}"
sonar_project_key: ${MUNITOR_SONAR_PROJECT_KEY}
requires:
- build-and-test
- coverage
context:
- ${MUNITOR_CONTEXT_SONAR}
filters:
branches:
only:
- /release\/.*/
##ENDIF_SONAR##
- munitor/docker_build_push:
name: docker-build-push
image_name: ${MUNITOR_IMAGE_NAME}
registry: ${MUNITOR_DOCKER_REGISTRY}
health_path: ${MUNITOR_HEALTH_PATH}
health_port: "${MUNITOR_HEALTH_PORT}"
health_db: "${MUNITOR_HEALTH_DB}"
health_migrations: "${MUNITOR_HEALTH_MIGRATIONS}"
health_smoke_paths: "${MUNITOR_HEALTH_SMOKE_PATHS}"
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
requires:
- code-quality
- coverage
- secrets-scan
- sast-scan
- security-scan
##IF_SONAR##
- sonar-scan
##ENDIF_SONAR##
##IF_E2E##
- e2e-tests
##ENDIF_E2E##
context:
- ${MUNITOR_CONTEXT_REGISTRY}
filters:
branches:
only:
- /release\/.*/
##IF_SBOM##
- munitor/sbom:
name: sbom
java_version: "${MUNITOR_JAVA_VERSION}"
context:
- ${MUNITOR_CONTEXT_GITHUB}
requires:
- code-quality
- coverage
- secrets-scan
- sast-scan
- security-scan
##IF_SONAR##
- sonar-scan
##ENDIF_SONAR##
##IF_E2E##
- e2e-tests
##ENDIF_E2E##
filters:
branches:
only:
- /release\/.*/
##ENDIF_SBOM##
##IF_CD##
- munitor/update_cd_repo:
name: update-cd-repo
cd_repo: ${MUNITOR_CD_REPO}
environment: ${MUNITOR_CD_ENV_RELEASE}
cd_format: ${MUNITOR_CD_FORMAT}
cd_image_name: ${MUNITOR_DOCKER_REGISTRY}/${MUNITOR_IMAGE_NAME}
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
ci_git_email: '${MUNITOR_CI_EMAIL}'
ci_git_name: '${MUNITOR_CI_NAME}'
requires:
- docker-build-push
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only:
- /release\/.*/
##ENDIF_CD##
##IF_GITHUB_RELEASE##
- munitor/github_release:
name: github-release
requires:
- docker-build-push
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only:
- /release\/.*/
##ENDIF_GITHUB_RELEASE##
production:
jobs:
- munitor/gradle_build_and_test:
name: build-and-test
java_version: "${MUNITOR_JAVA_VERSION}"
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only: main
- munitor/docker_build_push:
name: docker-build-push
image_name: ${MUNITOR_IMAGE_NAME}
registry: ${MUNITOR_DOCKER_REGISTRY}
health_path: ${MUNITOR_HEALTH_PATH}
health_port: "${MUNITOR_HEALTH_PORT}"
health_db: "${MUNITOR_HEALTH_DB}"
health_migrations: "${MUNITOR_HEALTH_MIGRATIONS}"
health_smoke_paths: "${MUNITOR_HEALTH_SMOKE_PATHS}"
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
requires:
- build-and-test
context:
- ${MUNITOR_CONTEXT_REGISTRY}
filters:
branches:
only: main
##IF_CD##
- munitor/update_cd_repo:
name: update-cd-repo
cd_repo: ${MUNITOR_CD_REPO}
environment: ${MUNITOR_CD_ENV_PROD}
cd_format: ${MUNITOR_CD_FORMAT}
cd_image_name: ${MUNITOR_DOCKER_REGISTRY}/${MUNITOR_IMAGE_NAME}
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
ci_git_email: '${MUNITOR_CI_EMAIL}'
ci_git_name: '${MUNITOR_CI_NAME}'
requires:
- docker-build-push
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only: main
##ENDIF_CD##
##IF_GITHUB_RELEASE##
- munitor/github_release:
name: github-release
requires:
- docker-build-push
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only: main
##ENDIF_GITHUB_RELEASE##
@@ -0,0 +1,146 @@
__WORKFLOW_NAME__:
jobs:
- munitor/gradle_build_and_test:
name: build-and-test
java_version: "${MUNITOR_JAVA_VERSION}"
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only: __BRANCH_FILTER__
- munitor/secrets_scan:
name: secrets-scan
filters:
branches:
only: __BRANCH_FILTER__
- munitor/sast_scan:
name: sast-scan
fail_on_findings: "${MUNITOR_SAST_FAIL_ON_FINDINGS}"
filters:
branches:
only: __BRANCH_FILTER__
- munitor/gradle_code_quality:
name: code-quality
java_version: "${MUNITOR_JAVA_VERSION}"
requires:
- build-and-test
filters:
branches:
only: __BRANCH_FILTER__
- munitor/gradle_coverage:
name: coverage
java_version: "${MUNITOR_JAVA_VERSION}"
min_instruction: "${MUNITOR_COVERAGE_MIN}"
requires:
- build-and-test
filters:
branches:
only: __BRANCH_FILTER__
- munitor/security_scan:
name: security-scan
java_version: "${MUNITOR_JAVA_VERSION}"
owasp: ${MUNITOR_OWASP}
##IF_OWASP##
##IF_NVD##
context:
- ${MUNITOR_CONTEXT_NVD}
##ENDIF_NVD##
##ENDIF_OWASP##
requires:
- build-and-test
filters:
branches:
only: __BRANCH_FILTER__
##IF_E2E##
- munitor/mvn_e2e_test:
name: e2e-tests
java_version: "${MUNITOR_JAVA_VERSION}"
requires:
- build-and-test
filters:
branches:
only: __BRANCH_FILTER__
##ENDIF_E2E##
##IF_SONAR##
- munitor/sonar_scan:
name: sonar-scan
java_version: "${MUNITOR_JAVA_VERSION}"
sonar_project_key: ${MUNITOR_SONAR_PROJECT_KEY}
requires:
- build-and-test
- coverage
context:
- ${MUNITOR_CONTEXT_SONAR}
filters:
branches:
only: __BRANCH_FILTER__
##ENDIF_SONAR##
- munitor/docker_build_push:
name: docker-build-push
image_name: ${MUNITOR_IMAGE_NAME}
registry: ${MUNITOR_DOCKER_REGISTRY}
health_path: ${MUNITOR_HEALTH_PATH}
health_port: "${MUNITOR_HEALTH_PORT}"
health_db: "${MUNITOR_HEALTH_DB}"
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
requires:
- code-quality
- coverage
- secrets-scan
- sast-scan
- security-scan
##IF_SONAR##
- sonar-scan
##ENDIF_SONAR##
##IF_E2E##
- e2e-tests
##ENDIF_E2E##
context:
- ${MUNITOR_CONTEXT_REGISTRY}
filters:
branches:
only: __BRANCH_FILTER__
##IF_SBOM##
- munitor/sbom:
name: sbom
java_version: "${MUNITOR_JAVA_VERSION}"
context:
- ${MUNITOR_CONTEXT_GITHUB}
requires:
- code-quality
- coverage
- secrets-scan
- sast-scan
- security-scan
##IF_SONAR##
- sonar-scan
##ENDIF_SONAR##
##IF_E2E##
- e2e-tests
##ENDIF_E2E##
filters:
branches:
only: __BRANCH_FILTER__
##ENDIF_SBOM##
##IF_CD##
- munitor/update_cd_repo:
name: update-cd-repo
cd_repo: ${MUNITOR_CD_REPO}
environment: __CD_ENVIRONMENT__
cd_format: ${MUNITOR_CD_FORMAT}
cd_image_name: ${MUNITOR_DOCKER_REGISTRY}/${MUNITOR_IMAGE_NAME}
##IF_SUPPLEMENTAL##
supplemental_images: '${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}'
##ENDIF_SUPPLEMENTAL##
ci_git_email: '${MUNITOR_CI_EMAIL}'
ci_git_name: '${MUNITOR_CI_NAME}'
requires:
- docker-build-push
context:
- ${MUNITOR_CONTEXT_GITHUB}
filters:
branches:
only: __BRANCH_FILTER__
##ENDIF_CD##
+32
View File
@@ -0,0 +1,32 @@
pipeline: gradle-webapp
orb_version: "0.1"
image_name: concilium
java_version: "21"
server_module: "concilium-server"
docker:
registry: ghcr.io/koftwentytwo
cd:
repo: KofTwentyTwo/Concilium-CD
format: kustomize
env:
develop: dev
coverage:
min_instruction: "50"
sast:
fail_on_findings: true
sbom: true
owasp: false
health:
path: /api/health
port: "8000"
contexts:
registry: ghcr
github: github
+48
View File
@@ -287,6 +287,54 @@ extract_munitor_vars "${FIXTURES_DIR}/java-webapp.munitor.yml"
assert_eq "supplemental flag default" "false" "${MUNITOR_SUPPLEMENTAL}"
assert_eq "supplemental JSON default empty" "[]" "${MUNITOR_SUPPLEMENTAL_IMAGES_JSON}"
# --------------------------------------------------------------------------
# gradle-webapp fixture
# --------------------------------------------------------------------------
echo ""
echo "=== extract_munitor_vars: gradle-webapp fixture ==="
extract_munitor_vars "${FIXTURES_DIR}/gradle-webapp.munitor.yml"
assert_eq "pipeline type" "gradle-webapp" "${MUNITOR_PIPELINE}"
assert_eq "image_name" "concilium" "${MUNITOR_IMAGE_NAME}"
assert_eq "java_version" "21" "${MUNITOR_JAVA_VERSION}"
assert_eq "server_module" "concilium-server" "${MUNITOR_SERVER_MODULE}"
assert_eq "docker registry" "ghcr.io/koftwentytwo" "${MUNITOR_DOCKER_REGISTRY}"
assert_eq "cd repo" "KofTwentyTwo/Concilium-CD" "${MUNITOR_CD_REPO}"
assert_eq "cd flag" "true" "${MUNITOR_CD}"
assert_eq "cd format" "kustomize" "${MUNITOR_CD_FORMAT}"
assert_eq "cd env develop" "dev" "${MUNITOR_CD_ENV_DEVELOP}"
assert_eq "coverage min" "50" "${MUNITOR_COVERAGE_MIN}"
assert_eq "sbom" "true" "${MUNITOR_SBOM}"
assert_eq "owasp" "false" "${MUNITOR_OWASP}"
assert_eq "sast" "true" "${MUNITOR_SAST}"
assert_eq "sast fail_on_findings" "true" "${MUNITOR_SAST_FAIL_ON_FINDINGS}"
assert_eq "health_path" "/api/health" "${MUNITOR_HEALTH_PATH}"
assert_eq "health_port" "8000" "${MUNITOR_HEALTH_PORT}"
assert_eq "context registry" "ghcr" "${MUNITOR_CONTEXT_REGISTRY}"
assert_eq "context github" "github" "${MUNITOR_CONTEXT_GITHUB}"
assert_eq "github_release flag" "true" "${MUNITOR_GITHUB_RELEASE}"
# --------------------------------------------------------------------------
# server_module defaults (empty when not set)
# --------------------------------------------------------------------------
echo ""
echo "=== extract_munitor_vars: server_module default ==="
extract_munitor_vars "${FIXTURES_DIR}/java-webapp.munitor.yml"
assert_eq "server_module default (empty)" "" "${MUNITOR_SERVER_MODULE}"
# --------------------------------------------------------------------------
# get_envsubst_vars includes MUNITOR_SERVER_MODULE
# --------------------------------------------------------------------------
echo -n " TEST: get_envsubst_vars includes MUNITOR_SERVER_MODULE... "
VARS_CHECK=$(get_envsubst_vars)
if echo "${VARS_CHECK}" | grep -q 'MUNITOR_SERVER_MODULE'; then
echo "PASS"
PASS=$((PASS + 1))
else
echo "FAIL"
FAIL=$((FAIL + 1))
fi
# --------------------------------------------------------------------------
# Missing config file
# --------------------------------------------------------------------------
+82
View File
@@ -1131,6 +1131,88 @@ run_test "java-webapp-supplemental: has production workflow" \
"${FIXTURES_DIR}/java-webapp-supplemental.munitor.yml" \
"production:"
# ==========================================================================
# gradle-webapp template rendering
# ==========================================================================
echo ""
echo "=== gradle-webapp Template Rendering Tests ==="
run_test "gradle-webapp: renders image_name" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"concilium"
run_test "gradle-webapp: renders java_version" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"java_version: \"21\""
run_test "gradle-webapp: uses gradle_build_and_test job" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"munitor/gradle_build_and_test"
run_test "gradle-webapp: uses gradle_code_quality job" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"munitor/gradle_code_quality"
run_test "gradle-webapp: uses gradle_coverage job" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"munitor/gradle_coverage"
run_negative_test "gradle-webapp: does not use mvn_build_and_test" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"munitor/mvn_build_and_test"
run_negative_test "gradle-webapp: does not use mvn_code_quality" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"munitor/mvn_code_quality"
run_negative_test "gradle-webapp: does not use mvn_coverage" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"munitor/mvn_coverage"
run_test "gradle-webapp: includes sast-scan" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"sast-scan"
run_test "gradle-webapp: includes sbom when enabled" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"sbom"
run_test "gradle-webapp: owasp disabled when false" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"owasp: false"
run_test "gradle-webapp: has cd repo update" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"update-cd-repo"
run_test "gradle-webapp: has release-candidate workflow" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"release-candidate:"
run_test "gradle-webapp: has production workflow" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"production:"
run_test "gradle-webapp: has pr-checks workflow" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"pr-checks:"
run_test "gradle-webapp: coverage min is 50" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
'min_instruction: "50"'
run_test "gradle-webapp: health_port is 8000" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
'health_port: "8000"'
run_negative_test "gradle-webapp: no e2e block (e2e not enabled)" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"e2e-tests"
run_negative_test "gradle-webapp: no sonar block (sonar not configured)" \
"${FIXTURES_DIR}/gradle-webapp.munitor.yml" \
"sonar-scan"
echo ""
echo "=== Results: ${PASS} passed, ${FAIL} failed ==="
+102
View File
@@ -917,6 +917,108 @@ for entry in "${URLS[@]}"; do
fi
done
# =============================================================================
# Test: Gradle scripts - structural tests
# =============================================================================
echo ""
echo "=== Gradle Scripts Tests ==="
for GRADLE_SCRIPT in gradle_build gradle_test gradle_jacoco gradle_checkstyle gradle_spotbugs gradle_pmd; do
SCRIPT_FILE="${SRC_SCRIPTS}/${GRADLE_SCRIPT}.sh"
echo -n " TEST: ${GRADLE_SCRIPT}.sh exists... "
if [[ -f "${SCRIPT_FILE}" ]]; then
pass
else
fail "file not found"
continue
fi
echo -n " TEST: ${GRADLE_SCRIPT}.sh is executable... "
if [[ -x "${SCRIPT_FILE}" ]]; then
pass
else
fail "not executable"
fi
echo -n " TEST: ${GRADLE_SCRIPT}.sh sources munitor_helpers.sh... "
if grep -q 'munitor_helpers.sh' "${SCRIPT_FILE}"; then
pass
else
fail "should source munitor_helpers.sh"
fi
echo -n " TEST: ${GRADLE_SCRIPT}.sh uses set -euo pipefail... "
if grep -q 'set -euo pipefail' "${SCRIPT_FILE}"; then
pass
else
fail "should use set -euo pipefail"
fi
echo -n " TEST: ${GRADLE_SCRIPT}.sh uses gradlew... "
if grep -q 'gradlew' "${SCRIPT_FILE}"; then
pass
else
fail "should use ./gradlew"
fi
done
echo -n " TEST: gradle_build.sh uses --no-daemon... "
if grep -q '\-\-no-daemon' "${SRC_SCRIPTS}/gradle_build.sh"; then
pass
else
fail "should use --no-daemon"
fi
echo -n " TEST: gradle_build.sh skips tests (-x test)... "
if grep -q '\-x test' "${SRC_SCRIPTS}/gradle_build.sh"; then
pass
else
fail "should skip tests with -x test"
fi
echo -n " TEST: gradle_jacoco.sh validates MIN_INSTRUCTION... "
if grep -q 'MIN_INSTRUCTION' "${SRC_SCRIPTS}/gradle_jacoco.sh"; then
pass
else
fail "should use MIN_INSTRUCTION"
fi
echo -n " TEST: gradle_jacoco.sh aggregates multi-module CSVs... "
if grep -q 'find' "${SRC_SCRIPTS}/gradle_jacoco.sh" && grep -q 'jacocoTestReport.csv' "${SRC_SCRIPTS}/gradle_jacoco.sh"; then
pass
else
fail "should find jacocoTestReport.csv files"
fi
echo -n " TEST: gradle_checkstyle.sh detects plugin availability... "
if grep -q 'tasks --all' "${SRC_SCRIPTS}/gradle_checkstyle.sh" && grep -q 'checkstyleMain' "${SRC_SCRIPTS}/gradle_checkstyle.sh"; then
pass
else
fail "should detect checkstyleMain task"
fi
echo -n " TEST: gradle_checkstyle.sh skips gracefully... "
if grep -q 'skipping\|Skipping\|skip\|not applied' "${SRC_SCRIPTS}/gradle_checkstyle.sh"; then
pass
else
fail "should skip gracefully when plugin not applied"
fi
echo -n " TEST: gradle_spotbugs.sh detects plugin availability... "
if grep -q 'tasks --all' "${SRC_SCRIPTS}/gradle_spotbugs.sh" && grep -q 'spotbugsMain' "${SRC_SCRIPTS}/gradle_spotbugs.sh"; then
pass
else
fail "should detect spotbugsMain task"
fi
echo -n " TEST: gradle_pmd.sh detects plugin availability... "
if grep -q 'tasks --all' "${SRC_SCRIPTS}/gradle_pmd.sh" && grep -q 'pmdMain' "${SRC_SCRIPTS}/gradle_pmd.sh"; then
pass
else
fail "should detect pmdMain task"
fi
# =============================================================================
# Summary
# =============================================================================