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
89 changes: 89 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: CD Pipeline

on:
workflow_run:
workflows: [CI Pipeline]
types: [completed]
branches: [DevOps, main]
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
type: choice
options:
- staging
- production
image-tag:
description: 'Docker image tag to deploy'
required: true
type: string

permissions:
contents: write
packages: read

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository_owner }}/bankapp

jobs:
prepare:
name: Prepare Deployment
runs-on: ubuntu-latest
if: >-
github.event_name == 'workflow_dispatch' ||
github.event.workflow_run.conclusion == 'success'
outputs:
image-tag: ${{ steps.resolve-tag.outputs.tag }}
environment: ${{ steps.resolve-env.outputs.environment }}
steps:
- name: Resolve image tag
id: resolve-tag
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "tag=${{ inputs.image-tag }}" >> "$GITHUB_OUTPUT"
else
echo "tag=${{ github.event.workflow_run.head_sha }}" >> "$GITHUB_OUTPUT"
fi

- name: Resolve environment
id: resolve-env
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "environment=${{ inputs.environment }}" >> "$GITHUB_OUTPUT"
elif [[ "${{ github.event.workflow_run.head_branch }}" == "main" ]]; then
echo "environment=production" >> "$GITHUB_OUTPUT"
else
echo "environment=staging" >> "$GITHUB_OUTPUT"
fi

deploy-staging:
name: Deploy to Staging
needs: prepare
if: needs.prepare.outputs.environment == 'staging'
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: staging
image-tag: ${{ needs.prepare.outputs.image-tag }}
image-name: ghcr.io/${{ github.repository_owner }}/bankapp
namespace: bankapp-staging
helm-release-name: bankapp-staging
helm-values-file: helm/bankapp/values-staging.yaml
secrets:
kube-config: ${{ secrets.KUBE_CONFIG_STAGING }}

deploy-production:
name: Deploy to Production
needs: prepare
if: needs.prepare.outputs.environment == 'production'
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: production
image-tag: ${{ needs.prepare.outputs.image-tag }}
image-name: ghcr.io/${{ github.repository_owner }}/bankapp
namespace: bankapp-production
helm-release-name: bankapp-production
helm-values-file: helm/bankapp/values-production.yaml
secrets:
kube-config: ${{ secrets.KUBE_CONFIG_PRODUCTION }}
80 changes: 80 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: CI Pipeline

on:
push:
branches: [DevOps, main]
pull_request:
branches: [DevOps, main]

permissions:
contents: read
packages: write
security-events: write
checks: write

env:
JAVA_VERSION: '17'
IMAGE_NAME: ${{ github.repository_owner }}/bankapp

jobs:
build:
name: Build
uses: ./.github/workflows/reusable-build.yml
with:
java-version: '17'
maven-goals: 'clean compile'

test:
name: Test
needs: build
uses: ./.github/workflows/reusable-test.yml
with:
java-version: '17'

security-scan:
name: Security Scan
needs: build
uses: ./.github/workflows/reusable-security-scan.yml
with:
java-version: '17'
trivy-severity: 'CRITICAL,HIGH'
fail-on-vulnerabilities: false
secrets:
nvd-api-key: ${{ secrets.NVD_API_KEY }}

docker-build:
name: Docker Build & Push
needs: [test]
if: always() && needs.test.result == 'success'
uses: ./.github/workflows/reusable-docker.yml
with:
image-name: ${{ github.repository_owner }}/bankapp
image-tag: ${{ github.sha }}
push: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/DevOps' || github.ref == 'refs/heads/main') }}
registry: ghcr.io
trivy-scan: true
trivy-severity: 'CRITICAL,HIGH'
secrets:
registry-username: ${{ github.actor }}
registry-password: ${{ secrets.GITHUB_TOKEN }}

ci-status:
name: CI Status
runs-on: ubuntu-latest
needs: [build, test, security-scan, docker-build]
if: always()
steps:
- name: Check CI results
run: |
echo "Build: ${{ needs.build.result }}"
echo "Test: ${{ needs.test.result }}"
echo "Security Scan: ${{ needs.security-scan.result }}"
echo "Docker Build: ${{ needs.docker-build.result }}"

if [[ "${{ needs.build.result }}" == "failure" || \
"${{ needs.test.result }}" == "failure" ]]; then
echo "CI pipeline failed"
exit 1
fi

echo "CI pipeline completed successfully"
41 changes: 41 additions & 0 deletions .github/workflows/reusable-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Reusable Build

on:
workflow_call:
inputs:
java-version:
description: 'Java version to use'
required: false
type: string
default: '17'
maven-goals:
description: 'Maven goals to execute'
required: false
type: string
default: 'clean compile'

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up JDK ${{ inputs.java-version }}
uses: actions/setup-java@v4
with:
java-version: ${{ inputs.java-version }}
distribution: 'temurin'
cache: 'maven'

- name: Build with Maven
run: |
chmod +x ./mvnw
./mvnw ${{ inputs.maven-goals }} -DskipTests -B

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: target/
retention-days: 1
96 changes: 96 additions & 0 deletions .github/workflows/reusable-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Reusable Deploy

on:
workflow_call:
inputs:
environment:
description: 'Deployment environment'
required: true
type: string
image-tag:
description: 'Docker image tag to deploy'
required: true
type: string
image-name:
description: 'Full image name (registry/repo)'
required: true
type: string
namespace:
description: 'Kubernetes namespace'
required: false
type: string
default: 'bankapp-namespace'
helm-release-name:
description: 'Helm release name'
required: false
type: string
default: 'bankapp'
helm-values-file:
description: 'Path to Helm values file'
required: false
type: string
default: 'helm/bankapp/values.yaml'
deployment-name:
description: 'Kubernetes Deployment resource name (as defined in Helm template)'
required: false
type: string
default: 'bankapp'
secrets:
kube-config:
description: 'Kubernetes config for cluster access'
required: true

jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up kubectl
uses: azure/setup-kubectl@v4

- name: Set up Helm
uses: azure/setup-helm@v4
with:
version: 'latest'

- name: Configure kubeconfig
run: |
mkdir -p $HOME/.kube
echo "${{ secrets.kube-config }}" | base64 -d > $HOME/.kube/config
chmod 600 $HOME/.kube/config

- name: Validate Helm chart
run: |
helm lint helm/bankapp/ \
--set image.app=${{ inputs.image-name }}:${{ inputs.image-tag }}

- name: Deploy with Helm
run: |
helm upgrade --install ${{ inputs.helm-release-name }} helm/bankapp/ \
--namespace ${{ inputs.namespace }} \
--create-namespace \
--set image.app=${{ inputs.image-name }}:${{ inputs.image-tag }} \
--values ${{ inputs.helm-values-file }} \
--wait \
--timeout 300s

- name: Verify deployment
run: |
kubectl rollout status deployment/${{ inputs.deployment-name }} \
--namespace ${{ inputs.namespace }} \
--timeout=300s
Comment on lines +80 to +84

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Deployment verification uses helm-release-name instead of actual Kubernetes Deployment name

The verify step at reusable-deploy.yml:77 runs kubectl rollout status deployment/${{ inputs.helm-release-name }}, but the actual Kubernetes Deployment resource name is determined by the Helm template at helm/bankapp/templates/deployment.yml:4 which uses {{ .Values.app_deployment.name }}. In both values-staging.yaml:14 and values-production.yaml:14, this is set to bankapp. However, cd.yml:71 passes helm-release-name: bankapp-staging and cd.yml:86 passes helm-release-name: bankapp-production. The verify step will look for deployment/bankapp-staging or deployment/bankapp-production, which don't exist — the actual deployment is named deployment/bankapp. This causes the verification to always fail with a 'not found' error, even when the Helm deploy itself succeeded.

Prompt for agents
The problem is in .github/workflows/reusable-deploy.yml at the 'Verify deployment' step. The kubectl command uses `inputs.helm-release-name` (which is 'bankapp-staging' or 'bankapp-production') as the deployment name, but the actual Kubernetes Deployment resource is named by the Helm template using `.Values.app_deployment.name` which is 'bankapp' in both values-staging.yaml and values-production.yaml.

Possible fixes:
1. Change the verify step to use a separate input for the actual deployment name, or derive it from the values file.
2. Add a new input like 'deployment-name' that defaults to 'bankapp' and use that in the kubectl command.
3. Update the Helm values files so that `app_deployment.name` matches the helm-release-name (e.g., 'bankapp-staging' and 'bankapp-production').
4. Use `kubectl get deployment -n <namespace> -l app=bankapp` to find the deployment dynamically instead of hardcoding the name.

Option 3 is the simplest but changes the Kubernetes resource names. Option 2 keeps the reusable workflow flexible without coupling it to Helm values.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 3a38770. Added a separate deployment-name input (defaults to bankapp) to the reusable deploy workflow, so kubectl rollout status checks the correct K8s Deployment resource name instead of using the Helm release name.


- name: Display deployment info
if: always()
run: |
echo "=== Deployment Status ==="
kubectl get deployments -n ${{ inputs.namespace }}
echo ""
echo "=== Pods ==="
kubectl get pods -n ${{ inputs.namespace }}
echo ""
echo "=== Services ==="
kubectl get services -n ${{ inputs.namespace }}
Loading
Loading