Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
21b821a
Merge pull request #44 from Cognition-Partner-Workshops/epic-6/cross-…
devin-ai-integration[bot] May 26, 2026
9c28176
feat: add deployment & cutover artifacts for Spring Boot migration
devin-ai-integration[bot] May 26, 2026
7c6dc8e
fix: correct table names in CUTOVER.md data integrity queries
devin-ai-integration[bot] May 26, 2026
56760ec
NM-167: Create Dockerfile for Spring Boot application
devin-ai-integration[bot] May 26, 2026
b3e64e0
Merge NM-167: Create Dockerfile for Spring Boot application
devin-ai-integration[bot] May 26, 2026
aa0ec1b
NM-168: Create GitHub Actions CI/CD workflow for Azure VM deployment
devin-ai-integration[bot] May 26, 2026
50ee48c
Merge NM-168: Create GitHub Actions CI/CD workflow for Azure VM deplo…
devin-ai-integration[bot] May 26, 2026
049b962
NM-169: Create Azure VM deployment documentation and setup scripts
devin-ai-integration[bot] May 26, 2026
779ce6e
Merge NM-169: Create Azure VM deployment documentation and setup scripts
devin-ai-integration[bot] May 26, 2026
8425bb2
NM-170: Document rollback plan and cutover procedure
devin-ai-integration[bot] May 26, 2026
8d2020b
Merge NM-170: Document rollback plan and cutover procedure
devin-ai-integration[bot] May 26, 2026
4b51155
fix: setup-vm.sh accepts environment argument (staging|production)
devin-ai-integration[bot] May 26, 2026
939042b
fix: add GHCR auth to deploy jobs and correct docker-compose docs
devin-ai-integration[bot] May 26, 2026
e3fd680
fix: add environment: staging to health-check job for secret access
devin-ai-integration[bot] May 26, 2026
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
197 changes: 197 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
name: Build & Deploy to Azure VM

on:
push:
branches: [main, migration/complete-java-migration]
pull_request:
branches: [main]

env:
JAVA_VERSION: '21'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/eshop-catalog
WORKING_DIR: eShopLegacyMVC-SpringBoot

jobs:
# ──────────────────────────────────────────────
# Job 1: Build, Test, and Verify
# ──────────────────────────────────────────────
build-and-test:
name: Build & Test
runs-on: ubuntu-latest
defaults:
run:
working-directory: ${{ env.WORKING_DIR }}
steps:
- uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}
cache: maven
cache-dependency-path: ${{ env.WORKING_DIR }}/pom.xml

- name: Build and run tests
run: ./mvnw clean verify -B

- name: Check code formatting (Spotless)
run: ./mvnw spotless:check -B

- name: Publish test results
uses: dorny/test-reporter@v1
if: always()
with:
name: JUnit Test Results
path: ${{ env.WORKING_DIR }}/target/surefire-reports/*.xml
reporter: java-junit

- name: Upload JaCoCo coverage report
uses: actions/upload-artifact@v4
if: always()
with:
name: jacoco-report
path: ${{ env.WORKING_DIR }}/target/site/jacoco/
retention-days: 14

# ──────────────────────────────────────────────
# Job 2: Build Docker Image and Push to GHCR
# ──────────────────────────────────────────────
docker-build-push:
name: Docker Build & Push
runs-on: ubuntu-latest
needs: build-and-test
if: github.event_name == 'push'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=${{ github.sha }}
type=ref,event=branch
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: ${{ env.WORKING_DIR }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

# ──────────────────────────────────────────────
# Job 3: Deploy to Staging (auto-deploy)
# ──────────────────────────────────────────────
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: docker-build-push
environment: staging
steps:
- name: Deploy to staging VM via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.STAGING_VM_HOST }}
username: ${{ secrets.VM_USERNAME }}
key: ${{ secrets.VM_SSH_KEY }}
envs: GHCR_TOKEN
script: |
echo "$GHCR_TOKEN" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
docker stop eshop-catalog || true
docker rm eshop-catalog || true
docker run -d \
--name eshop-catalog \
--restart unless-stopped \
-p 8080:8080 \
--env-file /opt/app/staging.env \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
env:
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# ──────────────────────────────────────────────
# Job 4: Health Check on Staging
# ──────────────────────────────────────────────
health-check-staging:
name: Staging Health Check
runs-on: ubuntu-latest
needs: deploy-staging
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
environment: staging
steps:
- name: Wait for application startup
run: sleep 30

- name: Verify health endpoint
run: |
for i in $(seq 1 10); do
STATUS=$(curl -sf "${{ secrets.STAGING_URL }}/actuator/health" | jq -r '.status') || true
if [ "$STATUS" = "UP" ]; then
echo "Health check passed: status is UP"
exit 0
fi
echo "Attempt $i: status=$STATUS — retrying in 10s..."
sleep 10
done
echo "Health check failed after 10 attempts"
exit 1

# ──────────────────────────────────────────────
# Job 5: Deploy to Production (manual approval)
# ──────────────────────────────────────────────
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: health-check-staging
environment:
name: production
url: ${{ secrets.PROD_URL }}
steps:
- name: Deploy to production VM via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.PROD_VM_HOST }}
username: ${{ secrets.VM_USERNAME }}
key: ${{ secrets.VM_SSH_KEY }}
envs: GHCR_TOKEN
script: |
echo "$GHCR_TOKEN" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
docker stop eshop-catalog || true
docker rm eshop-catalog || true
docker run -d \
--name eshop-catalog \
--restart unless-stopped \
-p 8080:8080 \
--env-file /opt/app/production.env \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
env:
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Verify production health
run: |
sleep 30
for i in $(seq 1 10); do
STATUS=$(curl -sf "${{ secrets.PROD_URL }}/actuator/health" | jq -r '.status') || true
if [ "$STATUS" = "UP" ]; then
echo "Production health check passed: status is UP"
exit 0
fi
echo "Attempt $i: status=$STATUS — retrying in 10s..."
sleep 10
done
echo "Production health check failed after 10 attempts"
exit 1
36 changes: 36 additions & 0 deletions eShopLegacyMVC-SpringBoot/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Build output
target/

# IDE files
.idea/
*.iml
.vscode/
*.swp
*.swo

# Git
.git/
.gitignore

# Docker
Dockerfile
docker-compose.yml
.dockerignore

# CI/CD
azure-pipelines.yml
.github/

# Kubernetes
k8s/

# Scripts
scripts/

# Documentation
*.md
LICENSE

# OS files
.DS_Store
Thumbs.db
Loading