Compare commits

..
3 Commits
Author SHA1 Message Date
james.maes 1e558c18af fix: correct step order — docker login must precede supplemental push
The previous fix moved build_push_supplemental before docker_push_ghcr,
but docker_push_ghcr contains the docker login to GHCR. Without it,
supplemental pushes fail with 'unauthorized'.

Correct order: docker_push_ghcr (login + push main) first, then
build_push_supplemental (build + push supplementals), then health_check
(supplementals now in local Docker cache).

Main image is pushed before health check, but Trivy has already scanned
it and a health check failure still prevents the CD update step.
2026-03-27 10:32:34 -05:00
james.maes e262339fa7 fix: build supplemental images before health check
Supplemental images (e.g. migrations) must be built locally before the
health check runs. Previously, build_push_supplemental ran AFTER
health_check, so on a first-release tag like 0.1.8 the health check
tried to pull a tag that didn't exist yet in GHCR and failed with
'unauthorized'.

New step order:
  docker_build → trivy → build_push_supplemental → health_check → docker_push_ghcr

The migrations image is now in the local Docker cache when health_check
runs, so no GHCR pull is needed. Main image is still only pushed after
the health check passes.
2026-03-27 10:13:34 -05:00
james.maes af150158ab feat: add gradle-webapp pipeline type (#2)
* feat: add gradle-webapp pipeline type

Add full Gradle build support mirroring the existing Maven pipeline.
New commands, scripts, jobs, and templates for Gradle projects with
multi-module JaCoCo aggregation, graceful code quality plugin detection,
and shadow JAR discovery via configurable server_module field.

* fix: extract inline test collection to included script

Move the multi-line mkdir/find command into
collect_gradle_test_results.sh to satisfy RC009 orb review
(commands >64 chars must use include syntax).

* fix: embed gradle templates in packed orb scripts

Add gradle-webapp.yml.tpl and gradle-webapp-deploy.yml.tpl to the
pack_generate_config.sh template/partial embed loops so they are
available at runtime when the orb executes on CircleCI.

* fix: improve gradle_jacoco.sh diagnostics and error messaging

Add directory listing of JaCoCo report output before CSV search to
help diagnose missing reports. Improve error message with exact
build.gradle.kts snippet needed to enable CSV report format.
2026-03-22 18:36:53 -05:00
31 changed files with 1802 additions and 37 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
+6 -6
View File
@@ -57,12 +57,6 @@ steps:
- docker_build:
image_name: << parameters.image_name >>
registry: << parameters.registry >>
- health_check:
health_path: << parameters.health_path >>
health_port: << parameters.health_port >>
health_db: << parameters.health_db >>
health_migrations: << parameters.health_migrations >>
health_smoke_paths: << parameters.health_smoke_paths >>
- restore_trivy_cache
- run_trivy_image:
image_tag: << parameters.registry >>/<< parameters.image_name >>:${DOCKER_TAG}
@@ -72,3 +66,9 @@ steps:
supplemental_images: << parameters.supplemental_images >>
image_name: << parameters.image_name >>
registry: << parameters.registry >>
- health_check:
health_path: << parameters.health_path >>
health_port: << parameters.health_port >>
health_db: << parameters.health_db >>
health_migrations: << parameters.health_migrations >>
health_smoke_paths: << parameters.health_smoke_paths >>
+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
# =============================================================================