Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# CD pipeline migrated from GitOps/Jenkinsfile.
# Updates the Kubernetes manifest with the new image tag and commits it back (GitOps),
# mirroring the original "Update: Kubernetes manifest" + "Git: Code update and push" stages.
name: CD

on:
# Chained from CI (Jenkins post { success { build job: "BankApp-CD" } }).
workflow_call:
inputs:
docker_tag:
description: "Docker tag of the image built by the CI job."
required: true
type: string
# Manual run (Jenkins parameterized build).
workflow_dispatch:
inputs:
docker_tag:
description: "Docker tag of the image to deploy."
required: true
default: ""
# Cross-repo / external trigger alternative.
repository_dispatch:
types: [deploy-bankapp]

# Required so the workflow can commit the updated manifest back to the repo.
permissions:
contents: write

jobs:
deploy:
runs-on: ubuntu-latest
env:
# Single source of truth for the tag across workflow_call / workflow_dispatch / repository_dispatch.
DOCKER_TAG: ${{ inputs.docker_tag || github.event.client_payload.docker_tag }}
# Job-level so it is readable from step `if:` conditions (gates the optional email step).
MAIL_SERVER: ${{ secrets.MAIL_SERVER }}
steps:
# Jenkins stage "Git: Code Checkout" (cleanWs() is not needed on fresh runners).
- name: Checkout
uses: actions/checkout@v4
with:
ref: DevOps
# Token with write access so the follow-up push is authenticated.
token: ${{ secrets.GITOPS_TOKEN || github.token }}

# Jenkins stage "Verify: Docker Image Tags".
- name: Verify Docker image tag
run: |
echo "DOCKER TAG RECEIVED: ${DOCKER_TAG}"
if [ -z "${DOCKER_TAG}" ]; then
echo "::error::No docker_tag provided to the CD workflow"
exit 1
fi

# Jenkins stage "Update: Kubernetes manifest" (sed on the deployment image tag).
- name: Update Kubernetes manifest
run: |
sed -i -e "s|trainwithshubham/bankapp-eks:.*|trainwithshubham/bankapp-eks:${DOCKER_TAG}|g" kubernetes/bankapp-deployment.yml
echo "Updated manifest:"
grep "image:" kubernetes/bankapp-deployment.yml

# Jenkins stage "Git: Code update and push to GitHub" (withCredentials git push).
- name: Commit and push manifest update
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
if git diff --quiet; then
echo "No manifest changes to commit."
exit 0
fi
git add kubernetes/bankapp-deployment.yml
git commit -m "Updated K8s Deployment Docker Image Version to ${DOCKER_TAG}"
git push origin DevOps

# Jenkins post { always { emailext ... } } -> send-mail action.
# Only runs when SMTP secrets are configured; otherwise skipped gracefully.
- name: Send deployment notification email
if: always() && env.MAIL_SERVER != ''
uses: dawidd6/action-send-mail@v3
with:
server_address: ${{ secrets.MAIL_SERVER }}
server_port: ${{ secrets.MAIL_PORT }}
username: ${{ secrets.MAIL_USERNAME }}
password: ${{ secrets.MAIL_PASSWORD }}
from: ${{ secrets.MAIL_FROM }}
to: ${{ secrets.MAIL_TO }}
subject: "BankApp Application has been updated and deployed - '${{ job.status }}'"
html_body: |
<html>
<body>
<div style="background-color: #FFA07A; padding: 10px; margin-bottom: 10px;">
<p style="color: black; font-weight: bold;">Project: ${{ github.repository }}</p>
</div>
<div style="background-color: #90EE90; padding: 10px; margin-bottom: 10px;">
<p style="color: black; font-weight: bold;">Run Number: ${{ github.run_number }}</p>
</div>
<div style="background-color: #87CEEB; padding: 10px; margin-bottom: 10px;">
<p style="color: black; font-weight: bold;">URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}</p>
</div>
</body>
</html>
221 changes: 221 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
# CI pipeline migrated from the root Jenkinsfile.
# Each Jenkins stage is mapped to a GitHub Actions job (see PR description for the mapping).
name: CI

on:
push:
branches: [DevOps]
pull_request:
branches: [DevOps]
workflow_dispatch:
inputs:
docker_tag:
description: "Docker image tag to build and push (Jenkins DOCKER_TAG parameter). Defaults to the commit short SHA when empty."
required: false
default: ""

# Mirrors the DockerHub image coordinates used by the Jenkins shared library (docker_build("bankapp", TAG, "madhupdevops")).
env:
IMAGE_NAME: bankapp
REGISTRY_NAMESPACE: ${{ vars.DOCKERHUB_NAMESPACE || 'madhupdevops' }}

permissions:
contents: read

jobs:
# Resolves the effective Docker tag once so every downstream job shares it.
# Replaces the Jenkins `DOCKER_TAG` parameter (empty default -> commit short SHA).
setup:
runs-on: ubuntu-latest
outputs:
docker_tag: ${{ steps.tag.outputs.value }}
steps:
- name: Resolve Docker tag
id: tag
run: |
TAG="${{ github.event.inputs.docker_tag }}"
if [ -z "$TAG" ]; then
TAG="${GITHUB_SHA::7}"
fi
echo "value=$TAG" >> "$GITHUB_OUTPUT"
echo "Using Docker tag: $TAG"

# Jenkins stage: "Git: Code Checkout" + Maven build (Dockerfile builds the jar with -DskipTests).
# `cleanWs()` is unnecessary because runners start fresh.
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
cache: maven

- name: Build with Maven (skip tests, mirrors Dockerfile)
run: |
chmod +x ./mvnw
./mvnw clean package -DskipTests -B

- name: Upload application jar
uses: actions/upload-artifact@v4
with:
name: bankapp-jar
path: target/*.jar
if-no-files-found: error

# Jenkins stage: "Trivy: Filesystem scan" (shared lib trivy_scan -> `trivy fs .`).
trivy-fs-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Trivy filesystem scan
uses: aquasecurity/trivy-action@0.35.0
with:
scan-type: fs
scan-ref: .
format: table
# Preserve Jenkins behaviour: the scan reports findings but does not fail the build.
exit-code: "0"
output: trivy-fs-report.txt

- name: Upload Trivy report
if: always()
uses: actions/upload-artifact@v4
with:
name: trivy-fs-report
path: trivy-fs-report.txt

# Jenkins stage: "OWASP: Dependency check" (shared lib owasp_dependency -> dependencyCheck --scan ./).
owasp-dependency-check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: OWASP Dependency-Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: bankapp
path: .
format: ALL
# NVD API key avoids slow/throttled database updates (optional but recommended).
args: >-
${{ secrets.NVD_API_KEY != '' && format('--nvdApiKey {0}', secrets.NVD_API_KEY) || '' }}

# Equivalent of Jenkins post { success { archiveArtifacts artifacts: '*.xml' } }.
- name: Upload Dependency-Check report
if: always()
uses: actions/upload-artifact@v4
with:
name: dependency-check-report
path: reports/

# Jenkins stages: "SonarQube: Code Analysis" + "SonarQube: Code Quality Gates".
sonarqube:
runs-on: ubuntu-latest
needs: build
# Job-level so steps can detect whether the SonarQube secrets are configured.
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history improves SonarQube blame/new-code detection.

- name: Notify if SonarQube is not configured
if: env.SONAR_TOKEN == ''
run: echo "::notice::SONAR_TOKEN / SONAR_HOST_URL not configured — skipping SonarQube analysis and quality gate. Add the secrets to enable this stage."

- name: Set up JDK 17
if: env.SONAR_TOKEN != ''
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
cache: maven

# Jenkins stage "SonarQube: Code Analysis" (sonarqube_analysis("Sonar","bankapp","bankapp")).
- name: SonarQube Scan
if: env.SONAR_TOKEN != ''
uses: SonarSource/sonarqube-scan-action@v4
with:
args: >
-Dsonar.projectName=bankapp
-Dsonar.projectKey=bankapp

# Jenkins stage "SonarQube: Code Quality Gates". The original used
# `waitForQualityGate abortPipeline: false` (non-blocking), so we mirror that
# with continue-on-error. Flip to a hard gate by removing continue-on-error.
- name: SonarQube Quality Gate
if: env.SONAR_TOKEN != ''
uses: SonarSource/sonarqube-quality-gate-action@v1
continue-on-error: true
timeout-minutes: 5

# Jenkins stages: "Docker: Build Images" + "Docker: Push to DockerHub" + Docker Scout image scan.
docker:
runs-on: ubuntu-latest
needs: [setup, build, trivy-fs-scan, owasp-dependency-check, sonarqube]
# Avoid pushing images for pull requests (no credentials / not desired on PRs).
if: github.event_name != 'pull_request'
permissions:
contents: read
outputs:
image: ${{ steps.meta.outputs.image }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

# Jenkins shared lib docker_push performed `docker login` with the 'docker' credential.
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Compute image reference
id: meta
run: echo "image=${REGISTRY_NAMESPACE}/${IMAGE_NAME}:${{ needs.setup.outputs.docker_tag }}" >> "$GITHUB_OUTPUT"

- name: Build and push image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.image }}
cache-from: type=gha
cache-to: type=gha,mode=max

# Docker Scout image vulnerability scan (requested addition to the Jenkins flow).
- name: Docker Scout CVE scan
uses: docker/scout-action@v1
with:
command: cves
image: ${{ steps.meta.outputs.image }}
only-severities: critical,high
# Report only; set to true to fail the build on critical/high CVEs.
exit-code: false

# Jenkins post { success { build job: "BankApp-CD", parameters: [DOCKER_TAG] } }.
# Chained via reusable workflow (workflow_call) so it works with the default GITHUB_TOKEN.
deploy:
needs: [setup, docker]
if: github.event_name != 'pull_request'
# Reusable CD workflow commits the manifest back, so it needs write access.
permissions:
contents: write
uses: ./.github/workflows/cd.yml
with:
docker_tag: ${{ needs.setup.outputs.docker_tag }}
secrets: inherit
Loading