Saltar al contenido
mypipelines
Pipelines Actions Gradle Buscar
Java (Spring Boot)· Reusable workflow ·on: workflow_call

Java Main Pipeline

Java - Main Pipeline

.github/workflows/java-main-pipeline.yml

.github/workflows/java-main-pipeline.yml
name: Java - Main Pipeline
on:
workflow_call:
inputs:
# Runner configuration
runner:
description: 'Runner type'
required: false
type: string
default: 'ubuntu-latest'
# Java configuration
java_version:
description: 'Java version'
required: false
type: string
default: '21'
java_distribution:
description: 'Java distribution'
required: false
type: string
default: 'temurin'
build_tool:
description: 'Build tool: gradle or maven'
required: false
type: string
default: 'gradle'
# Pipeline steps control
run_commit_lint:
description: 'Run commit message validation'
required: false
type: boolean
default: false
run_trufflehog:
description: 'Run TruffleHog secret scanning'
required: false
type: boolean
default: false
trufflehog_only_verified:
description: 'Only report verified secrets'
required: false
type: boolean
default: true
trufflehog_fail_on_findings:
description: 'Fail the workflow if secrets are found'
required: false
type: boolean
default: true
run_dependency_review:
description: 'Run dependency review (PR only)'
required: false
type: boolean
default: false
dependency_review_severity:
description: 'Minimum severity to fail: low, moderate, high, critical'
required: false
type: string
default: 'high'
run_build:
description: 'Run build job'
required: false
type: boolean
default: true
run_test:
description: 'Run test job'
required: false
type: boolean
default: false
run_coverage:
description: 'Run JaCoCo coverage report'
required: false
type: boolean
default: false
run_code_analysis:
description: 'Run code analysis'
required: false
type: boolean
default: false
code_analysis_tool:
description: 'Code analysis tool: sonar or qodana'
required: false
type: string
default: 'sonar'
skip_quality_gate:
description: 'Skip Quality Gate check'
required: false
type: boolean
default: false
run_owasp:
description: 'Run OWASP Dependency Check'
required: false
type: boolean
default: false
owasp_fail_on_cvss:
description: 'CVSS score threshold to fail (7 = HIGH+CRITICAL, 9 = CRITICAL only)'
required: false
type: number
default: 7
run_architecture:
description: 'Run architecture validation (ArchUnit + diagrams)'
required: false
type: boolean
default: false
# Artifact options
run_artifact:
description: 'Build and push Docker image'
required: false
type: boolean
default: false
artifact_registry:
description: 'Artifact registry: ecr, github, packages'
required: false
type: string
default: 'ecr'
publish_task:
description: 'Gradle publish task for packages registry'
required: false
type: string
default: 'publish'
# Test options
coverage_instruction_threshold:
description: 'Minimum instruction coverage percentage (0-100). Requires jacocoLogCodeCoverage task.'
required: false
type: number
default: 0
coverage_branch_threshold:
description: 'Minimum branch coverage percentage (0-100). Requires jacocoLogCodeCoverage task.'
required: false
type: number
default: 0
coverage_line_threshold:
description: 'Minimum line coverage percentage (0-100). Set to 0 to disable.'
required: false
type: number
default: 0
# Artifact & Deploy options
image_tag:
description: 'Docker image tag (empty = commit SHA)'
required: false
type: string
default: ''
docker_platform:
description: 'Docker platform (linux/amd64, linux/arm64)'
required: false
type: string
default: 'linux/arm64'
# Deploy options
run_deploy:
description: 'Deploy after artifact build'
required: false
type: boolean
default: false
deploy_target:
description: 'Deploy target: ec2, ec2-vpn, or eks'
required: false
type: string
default: 'ec2'
environment:
description: 'GitHub environment (develop, prod)'
required: false
type: string
default: 'develop'
memory_limit:
description: 'Container memory limit (e.g., 512m/512Mi)'
required: false
type: string
default: '800m'
memory_reservation:
description: 'Container memory reservation (e.g., 256m/256Mi)'
required: false
type: string
default: '256m'
spring_profiles:
description: 'Additional Spring profiles (e.g., awsparamstore)'
required: false
type: string
default: ''
extra_volumes:
description: 'Additional docker-compose volume mounts (one per line, including the leading "- "). Example: "- ./certs:/etc/ssl/app:ro"'
required: false
type: string
default: ''
# EKS-specific options
eks_cluster_name:
description: 'EKS cluster name (required when deploy_target: eks)'
required: false
type: string
default: ''
eks_namespace:
description: 'Kubernetes namespace'
required: false
type: string
default: 'default'
eks_use_helm:
description: 'Use Helm for EKS deployment'
required: false
type: boolean
default: false
eks_helm_chart_path:
description: 'Path to Helm chart'
required: false
type: string
default: './helm'
eks_replicas:
description: 'Number of EKS replicas'
required: false
type: number
default: 1
# Issue tracking
run_create_issue_on_failure:
description: 'Create GitHub issue on pipeline failure'
required: false
type: boolean
default: false
issue_labels:
description: 'Labels for the failure issue (comma-separated)'
required: false
type: string
default: 'bug,pipeline-failure'
# Notifications (Slack Incoming Webhook)
notify_on_failure:
description: 'Send Slack notification on pipeline failure'
required: false
type: boolean
default: false
notify_failure_channel:
description: 'Slack channel for failure notifications'
required: false
type: string
default: 'pipeline-errors'
notify_failure_mention:
description: 'Slack mention on failure (e.g., @channel, @here)'
required: false
type: string
default: '@channel'
notify_on_deploy:
description: 'Send Slack notification on deploy success'
required: false
type: boolean
default: false
notify_deploy_channel:
description: 'Slack channel for deploy notifications'
required: false
type: string
default: 'pipeline-deployments'
notify_on_release:
description: 'Send Slack notification when release PR is created'
required: false
type: boolean
default: false
notify_release_channel:
description: 'Slack channel for release PR notifications'
required: false
type: string
default: 'pr-review'
# Cleanup & Release
run_cleanup:
description: 'Delete merged branch after deploy'
required: false
type: boolean
default: false
run_release:
description: 'Create release PR after deploy'
required: false
type: boolean
default: false
release_target_branch:
description: 'Target branch for release PR'
required: false
type: string
default: 'main'
release_strict_flow:
description: 'Enforce GitFlow on release PR (base must be develop, target must be main)'
required: false
type: boolean
default: true
run_tag:
description: 'Create git tag and GitHub Release after deploy'
required: false
type: boolean
default: false
outputs:
image_tag:
description: 'Docker image tag pushed'
value: ${{ jobs.artifact-ecr.outputs.image_tag || jobs.artifact-github.outputs.image_tag }}
image_uri:
description: 'Full Docker image URI'
value: ${{ jobs.artifact-ecr.outputs.image_uri || jobs.artifact-github.outputs.image_uri }}
coverage:
description: 'Code coverage percentage (line)'
value: ${{ jobs.test.outputs.coverage_percentage }}
coverage_instruction:
description: 'Instruction coverage percentage'
value: ${{ jobs.test.outputs.coverage_instruction }}
coverage_branch:
description: 'Branch coverage percentage'
value: ${{ jobs.test.outputs.coverage_branch }}
quality_gate:
description: 'SonarQube Quality Gate status'
value: ${{ jobs.test.outputs.quality_gate_status }}
owasp_vulnerabilities:
description: 'Number of OWASP vulnerabilities found'
value: ${{ jobs.owasp.outputs.vulnerabilities }}
deleted_branch:
description: 'Name of deleted branch'
value: ${{ jobs.cleanup.outputs.deleted_branch }}
release_version:
description: 'Release version created'
value: ${{ jobs.release.outputs.version }}
release_pr_url:
description: 'Release PR URL'
value: ${{ jobs.release.outputs.pr_url }}
release_changelog:
description: 'Release changelog'
value: ${{ jobs.release.outputs.changelog }}
arch_test_result:
description: 'Architecture test result'
value: ${{ jobs.architecture.outputs.arch_test_result }}
diagrams_artifact:
description: 'Architecture diagrams artifact name'
value: ${{ jobs.architecture.outputs.diagrams_artifact }}
package_version:
description: 'Published package version'
value: ${{ jobs.artifact-packages.outputs.version }}
package_group:
description: 'Published package group'
value: ${{ jobs.artifact-packages.outputs.group }}
package_artifact_name:
description: 'Published package artifact name'
value: ${{ jobs.artifact-packages.outputs.artifact_name }}
jobs:
# ============================================
# COMMIT LINT (parallel, no deps)
# ============================================
commit-lint:
name: Commit Lint
if: inputs.run_commit_lint
uses: ./.github/workflows/shared-commit-lint.yml
with:
runner: ${{ inputs.runner }}
# ============================================
# SECURITY - TruffleHog (parallel, no deps)
# ============================================
security:
name: Security Scan
if: inputs.run_trufflehog
uses: ./.github/workflows/security-trufflehog.yml
with:
runner: ${{ inputs.runner }}
only_verified: ${{ inputs.trufflehog_only_verified }}
fail_on_findings: ${{ inputs.trufflehog_fail_on_findings }}
secrets: inherit
# ============================================
# SECURITY - Dependency Review (parallel, no deps)
# ============================================
dependency-review:
name: Dependency Review
# dependency-review-action only works on pull_request events; auto-skip on push.
if: inputs.run_dependency_review && github.event_name == 'pull_request'
uses: ./.github/workflows/security-dependency-review.yml
with:
runner: ${{ inputs.runner }}
fail_on_severity: ${{ inputs.dependency_review_severity }}
secrets: inherit
# ============================================
# BUILD
# ============================================
build:
name: Build
if: inputs.run_build
uses: ./.github/workflows/java-build.yml
with:
runner: ${{ inputs.runner }}
java_version: ${{ inputs.java_version }}
java_distribution: ${{ inputs.java_distribution }}
build_tool: ${{ inputs.build_tool }}
secrets: inherit
# ============================================
# TEST (needs: build)
# ============================================
test:
name: Test
if: inputs.run_test
needs: build
uses: ./.github/workflows/java-test.yml
with:
runner: ${{ inputs.runner }}
java_version: ${{ inputs.java_version }}
java_distribution: ${{ inputs.java_distribution }}
run_coverage: ${{ inputs.run_coverage }}
coverage_instruction_threshold: ${{ inputs.coverage_instruction_threshold }}
coverage_branch_threshold: ${{ inputs.coverage_branch_threshold }}
coverage_line_threshold: ${{ inputs.coverage_line_threshold }}
run_code_analysis: ${{ inputs.run_code_analysis }}
code_analysis_tool: ${{ inputs.code_analysis_tool }}
skip_quality_gate: ${{ inputs.skip_quality_gate }}
run_owasp: ${{ inputs.run_owasp }}
secrets: inherit
# ============================================
# QODANA (parallel with test, needs: build)
# ============================================
qodana:
name: Qodana
if: inputs.run_code_analysis && inputs.code_analysis_tool == 'qodana'
needs: build
uses: ./.github/workflows/java-qodana.yml
with:
runner: ${{ inputs.runner }}
secrets: inherit
# ============================================
# OWASP (parallel with test, needs: build)
# ============================================
owasp:
name: OWASP
if: inputs.run_owasp
needs: build
uses: ./.github/workflows/java-owasp.yml
with:
runner: ${{ inputs.runner }}
java_version: ${{ inputs.java_version }}
java_distribution: ${{ inputs.java_distribution }}
owasp_fail_on_cvss: ${{ inputs.owasp_fail_on_cvss }}
build_tool: ${{ inputs.build_tool }}
secrets: inherit
# ============================================
# ARCHITECTURE (parallel with test, needs: build)
# ============================================
architecture:
name: Architecture
if: inputs.run_architecture
needs: build
uses: ./.github/workflows/java-architecture.yml
with:
runner: ${{ inputs.runner }}
java_version: ${{ inputs.java_version }}
java_distribution: ${{ inputs.java_distribution }}
secrets: inherit
# ============================================
# ARTIFACT - ECR
# ============================================
artifact-ecr:
name: Build & Push ECR
if: |
inputs.run_artifact &&
inputs.artifact_registry == 'ecr' &&
always() &&
needs.build.result == 'success' &&
(needs.test.result == 'success' || needs.test.result == 'skipped') &&
(needs.qodana.result == 'success' || needs.qodana.result == 'skipped') &&
(needs.owasp.result == 'success' || needs.owasp.result == 'skipped') &&
(needs.architecture.result == 'success' || needs.architecture.result == 'skipped')
needs: [build, test, qodana, owasp, architecture]
uses: ./.github/workflows/shared-artifact-docker-ecr.yml
with:
runner: ${{ inputs.runner }}
image_tag: ${{ inputs.image_tag }}
docker_platform: ${{ inputs.docker_platform }}
environment: ${{ inputs.environment }}
build_from_source: true
java_version: ${{ inputs.java_version }}
java_distribution: ${{ inputs.java_distribution }}
build_tool: ${{ inputs.build_tool }}
secrets: inherit
# ============================================
# ARTIFACT - GitHub Registry
# ============================================
artifact-github:
name: Build & Push GitHub Registry
if: |
inputs.run_artifact &&
inputs.artifact_registry == 'github' &&
always() &&
needs.build.result == 'success' &&
(needs.test.result == 'success' || needs.test.result == 'skipped') &&
(needs.architecture.result == 'success' || needs.architecture.result == 'skipped')
needs: [build, test, architecture]
uses: ./.github/workflows/java-artifact-docker-github.yml
with:
runner: ${{ inputs.runner }}
java_version: ${{ inputs.java_version }}
java_distribution: ${{ inputs.java_distribution }}
image_tag: ${{ inputs.image_tag }}
docker_platform: ${{ inputs.docker_platform }}
artifact_name: ${{ needs.test.outputs.artifact_name }}
environment: ${{ inputs.environment }}
secrets: inherit
# ============================================
# ARTIFACT - GitHub Packages
# ============================================
artifact-packages:
name: Publish to GitHub Packages
if: |
inputs.run_artifact &&
inputs.artifact_registry == 'packages' &&
always() &&
needs.build.result == 'success' &&
(needs.test.result == 'success' || needs.test.result == 'skipped') &&
(needs.architecture.result == 'success' || needs.architecture.result == 'skipped')
needs: [build, test, architecture]
uses: ./.github/workflows/java-artifact-dependency-github.yml
with:
runner: ${{ inputs.runner }}
java_version: ${{ inputs.java_version }}
java_distribution: ${{ inputs.java_distribution }}
publish_task: ${{ inputs.publish_task }}
secrets: inherit
# ============================================
# DEPLOY - EC2
# ============================================
deploy-ec2:
name: Deploy EC2
if: |
inputs.run_deploy &&
inputs.deploy_target == 'ec2' &&
always() &&
(needs.artifact-ecr.result == 'success' || needs.artifact-github.result == 'success' || needs.artifact-packages.result == 'success')
needs: [artifact-ecr, artifact-github, artifact-packages]
uses: ./.github/workflows/shared-deploy-ec2.yml
with:
runner: ${{ inputs.runner }}
image_tag: ${{ needs.artifact-ecr.outputs.image_tag || needs.artifact-github.outputs.image_tag }}
docker_platform: ${{ inputs.docker_platform }}
environment: ${{ inputs.environment }}
memory_limit: ${{ inputs.memory_limit }}
memory_reservation: ${{ inputs.memory_reservation }}
container_env_vars: |
SPRING_PROFILES_ACTIVE=${{ inputs.spring_profiles != '' && format('{0},{1}', inputs.spring_profiles, inputs.environment) || inputs.environment }}
SERVER_CONTEXT_PATH=/${{ github.event.repository.name }}
extra_volumes: ${{ inputs.extra_volumes }}
secrets: inherit
# ============================================
# DEPLOY - EC2 via WireGuard VPN
# ============================================
deploy-ec2-vpn:
name: Deploy EC2 (VPN)
if: |
inputs.run_deploy &&
inputs.deploy_target == 'ec2-vpn' &&
always() &&
(needs.artifact-ecr.result == 'success' || needs.artifact-github.result == 'success' || needs.artifact-packages.result == 'success')
needs: [artifact-ecr, artifact-github, artifact-packages]
uses: ./.github/workflows/shared-deploy-ec2-vpn.yml
with:
runner: ${{ inputs.runner }}
image_tag: ${{ needs.artifact-ecr.outputs.image_tag || needs.artifact-github.outputs.image_tag }}
docker_platform: ${{ inputs.docker_platform }}
environment: ${{ inputs.environment }}
memory_limit: ${{ inputs.memory_limit }}
memory_reservation: ${{ inputs.memory_reservation }}
container_env_vars: |
SPRING_PROFILES_ACTIVE=${{ inputs.spring_profiles != '' && format('{0},{1}', inputs.spring_profiles, inputs.environment) || inputs.environment }}
SERVER_CONTEXT_PATH=/${{ github.event.repository.name }}
extra_volumes: ${{ inputs.extra_volumes }}
secrets: inherit
# ============================================
# DEPLOY - EKS
# ============================================
deploy-eks:
name: Deploy EKS
if: |
inputs.run_deploy &&
inputs.deploy_target == 'eks' &&
always() &&
(needs.artifact-ecr.result == 'success' || needs.artifact-github.result == 'success')
needs: [artifact-ecr, artifact-github, artifact-packages]
uses: ./.github/workflows/shared-deploy-eks.yml
with:
runner: ${{ inputs.runner }}
image_tag: ${{ needs.artifact-ecr.outputs.image_tag || needs.artifact-github.outputs.image_tag }}
environment: ${{ inputs.environment }}
cluster_name: ${{ inputs.eks_cluster_name }}
namespace: ${{ inputs.eks_namespace }}
replicas: ${{ inputs.eks_replicas }}
use_helm: ${{ inputs.eks_use_helm }}
helm_chart_path: ${{ inputs.eks_helm_chart_path }}
memory_limit: ${{ inputs.memory_limit }}
memory_request: ${{ inputs.memory_reservation }}
container_env_vars: |
SPRING_PROFILES_ACTIVE=${{ inputs.spring_profiles != '' && format('{0},{1}', inputs.spring_profiles, inputs.environment) || inputs.environment }}
SERVER_CONTEXT_PATH=/${{ github.event.repository.name }}
secrets: inherit
# ============================================
# RELEASE - Create release PR
# ============================================
release:
name: Create Release
if: |
inputs.run_release &&
always() &&
(needs.deploy-ec2.result == 'success' || needs.deploy-ec2-vpn.result == 'success' || needs.deploy-eks.result == 'success' ||
(needs.deploy-ec2.result == 'skipped' && needs.deploy-ec2-vpn.result == 'skipped' && needs.deploy-eks.result == 'skipped' &&
(needs.artifact-ecr.result == 'success' || needs.artifact-github.result == 'success' || needs.artifact-packages.result == 'success' ||
(needs.artifact-ecr.result == 'skipped' && needs.artifact-github.result == 'skipped' && needs.artifact-packages.result == 'skipped' &&
needs.test.result == 'success'))))
needs: [test, artifact-ecr, artifact-github, artifact-packages, deploy-ec2, deploy-ec2-vpn, deploy-eks]
uses: ./.github/workflows/shared-release.yml
with:
base_branch: ${{ github.ref_name }}
target_branch: ${{ inputs.release_target_branch }}
strict_flow: ${{ inputs.release_strict_flow }}
secrets: inherit
# ============================================
# CLEANUP - Delete merged branch (runs after release PR created)
# ============================================
cleanup:
name: Delete Branch
if: |
inputs.run_cleanup &&
always() &&
(needs.deploy-ec2.result == 'success' || needs.deploy-ec2-vpn.result == 'success' || needs.deploy-eks.result == 'success' ||
(needs.deploy-ec2.result == 'skipped' && needs.deploy-ec2-vpn.result == 'skipped' && needs.deploy-eks.result == 'skipped' &&
(needs.artifact-ecr.result == 'success' || needs.artifact-github.result == 'success' || needs.artifact-packages.result == 'success' ||
(needs.artifact-ecr.result == 'skipped' && needs.artifact-github.result == 'skipped' && needs.artifact-packages.result == 'skipped' &&
needs.test.result == 'success')))) &&
(needs.release.result == 'success' || needs.release.result == 'skipped')
needs: [test, artifact-ecr, artifact-github, artifact-packages, deploy-ec2, deploy-ec2-vpn, deploy-eks, release]
uses: ./.github/workflows/shared-delete-branch.yml
secrets: inherit
# ============================================
# TAG - Create git tag and GitHub Release
# ============================================
tag:
name: Tag Release
if: |
inputs.run_tag &&
always() &&
(needs.deploy-ec2.result == 'success' || needs.deploy-ec2-vpn.result == 'success' || needs.deploy-eks.result == 'success' ||
(needs.deploy-ec2.result == 'skipped' && needs.deploy-ec2-vpn.result == 'skipped' && needs.deploy-eks.result == 'skipped' &&
(needs.artifact-ecr.result == 'success' || needs.artifact-github.result == 'success' || needs.artifact-packages.result == 'success' ||
(needs.test.result == 'success' && needs.artifact-ecr.result == 'skipped' && needs.artifact-github.result == 'skipped' && needs.artifact-packages.result == 'skipped'))))
needs: [build, test, artifact-ecr, artifact-github, artifact-packages, deploy-ec2, deploy-ec2-vpn, deploy-eks, cleanup, release]
uses: ./.github/workflows/shared-tag-release.yml
secrets: inherit
# ============================================
# NOTIFY FAILURE - Slack notification on pipeline failure
# ============================================
notify-failure:
name: Notify Failure
if: |
inputs.notify_on_failure &&
always() &&
(needs.build.result == 'failure' || needs.test.result == 'failure' ||
needs.artifact-ecr.result == 'failure' || needs.artifact-github.result == 'failure' || needs.artifact-packages.result == 'failure' ||
needs.deploy-ec2.result == 'failure' || needs.deploy-ec2-vpn.result == 'failure' || needs.deploy-eks.result == 'failure')
needs: [build, test, artifact-ecr, artifact-github, artifact-packages, deploy-ec2, deploy-ec2-vpn, deploy-eks, cleanup, release, tag]
uses: ./.github/workflows/shared-slack-notify.yml
with:
channel: ${{ inputs.notify_failure_channel }}
status: 'failure'
mention_on_failure: ${{ inputs.notify_failure_mention }}
environment: ${{ inputs.environment }}
secrets: inherit
# ============================================
# NOTIFY DEPLOY - Slack notification on deploy success
# ============================================
notify-deploy:
name: Notify Deploy
if: |
inputs.notify_on_deploy &&
always() &&
(needs.deploy-ec2.result == 'success' || needs.deploy-ec2-vpn.result == 'success' || needs.deploy-eks.result == 'success')
needs: [deploy-ec2, deploy-ec2-vpn, deploy-eks]
uses: ./.github/workflows/shared-slack-notify.yml
with:
channel: ${{ inputs.notify_deploy_channel }}
status: 'success'
title: 'Deploy successful — ${{ github.repository }}'
environment: ${{ inputs.environment }}
secrets: inherit
# ============================================
# NOTIFY RELEASE - Slack notification when release PR created
# ============================================
notify-release:
name: Notify Release PR
if: |
inputs.notify_on_release &&
always() &&
needs.release.result == 'success'
needs: [release]
uses: ./.github/workflows/shared-slack-notify.yml
with:
channel: ${{ inputs.notify_release_channel }}
status: 'info'
title: 'Release PR created — ${{ github.repository }}'
version: ${{ needs.release.outputs.version }}
pr_url: ${{ needs.release.outputs.pr_url }}
secrets: inherit
# ============================================
# CREATE ISSUE - On failure (runs last, always)
# ============================================
create-issue:
name: Create Issue
if: |
inputs.run_create_issue_on_failure &&
always()
needs: [build, test, artifact-ecr, artifact-github, artifact-packages, deploy-ec2, deploy-ec2-vpn, deploy-eks, cleanup, release, tag]
uses: ./.github/workflows/shared-create-issue-on-failure.yml
with:
status: ${{ (needs.build.result == 'failure' || needs.test.result == 'failure' || needs.artifact-ecr.result == 'failure' || needs.artifact-github.result == 'failure' || needs.artifact-packages.result == 'failure' || needs.deploy-ec2.result == 'failure' || needs.deploy-ec2-vpn.result == 'failure' || needs.deploy-eks.result == 'failure') && 'failure' || 'success' }}
environment: ${{ inputs.environment }}
version: ${{ needs.release.outputs.version }}
changelog: ${{ needs.release.outputs.changelog }}
labels: ${{ inputs.issue_labels }}