From 31dd053d4236309c15c6dab2e5291b7a1de0e7f5 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:28:50 +0000 Subject: [PATCH 1/4] feature: migrate Jenkins CI/CD pipelines to GitHub Actions Co-Authored-By: Achal Channarasappa --- .github/workflows/cd.yml | 101 ++++++++++++++++++ .github/workflows/ci.yml | 214 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 315 insertions(+) create mode 100644 .github/workflows/cd.yml create mode 100644 .github/workflows/ci.yml 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..982c2fbc --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,214 @@ +# 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: ./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.28.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 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Full history improves SonarQube blame/new-code detection. + + - name: Set up JDK 17 + 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 + uses: SonarSource/sonarqube-scan-action@v4 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + 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 + uses: SonarSource/sonarqube-quality-gate-action@v1 + continue-on-error: true + timeout-minutes: 5 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + + # 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 From cc82ec4f55a8a036d2fb89b3adf6f686a17cd0bc Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:29:51 +0000 Subject: [PATCH 2/4] bug: pin trivy-action to existing release tag 0.35.0 Co-Authored-By: Achal Channarasappa --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 982c2fbc..d2b903ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,7 +73,7 @@ jobs: uses: actions/checkout@v4 - name: Trivy filesystem scan - uses: aquasecurity/trivy-action@0.28.0 + uses: aquasecurity/trivy-action@0.35.0 with: scan-type: fs scan-ref: . From 91f660cadc8daa667b65dc7d2323d6e2a06c0bc2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:31:04 +0000 Subject: [PATCH 3/4] bug: make mvnw executable in CI build job Co-Authored-By: Achal Channarasappa --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2b903ac..4494858b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,9 @@ jobs: cache: maven - name: Build with Maven (skip tests, mirrors Dockerfile) - run: ./mvnw clean package -DskipTests -B + run: | + chmod +x ./mvnw + ./mvnw clean package -DskipTests -B - name: Upload application jar uses: actions/upload-artifact@v4 From 224c74063cb2af4bc18efae33e5cb8d84c5a1c51 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:32:40 +0000 Subject: [PATCH 4/4] bug: skip SonarQube stage when secrets are not configured Co-Authored-By: Achal Channarasappa --- .github/workflows/ci.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4494858b..07503763 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,13 +120,22 @@ jobs: 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 @@ -135,10 +144,8 @@ jobs: # Jenkins stage "SonarQube: Code Analysis" (sonarqube_analysis("Sonar","bankapp","bankapp")). - name: SonarQube Scan + if: env.SONAR_TOKEN != '' uses: SonarSource/sonarqube-scan-action@v4 - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} with: args: > -Dsonar.projectName=bankapp @@ -148,12 +155,10 @@ jobs: # `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 - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} # Jenkins stages: "Docker: Build Images" + "Docker: Push to DockerHub" + Docker Scout image scan. docker: