diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
new file mode 100644
index 00000000..d5d543c7
--- /dev/null
+++ b/.github/workflows/cd.yml
@@ -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: |
+
+
+
+
Project: ${{ github.repository }}
+
+
+
Run Number: ${{ github.run_number }}
+
+
+
URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+
+
+
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..07503763
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -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