Skip to content

Commit 50a138d

Browse files
authored
Refactor CI/CD workflow with multi-stage Docker build
Refactor CI/CD workflow to use multi-stage Docker build and improve security checks.
1 parent 1ff2e5a commit 50a138d

1 file changed

Lines changed: 31 additions & 210 deletions

File tree

.github/workflows/ci-cd.yml

Lines changed: 31 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -1,222 +1,43 @@
1-
name: Reservation Service CI/CD
1+
# Multi-stage Dockerfile for Reservation Service
22

3-
on:
4-
push:
5-
branches: [ main ]
6-
pull_request:
7-
branches: [ main ]
8-
workflow_dispatch:
3+
# Stage 1: Build stage
4+
FROM maven:3.9-eclipse-temurin-17 AS build
5+
WORKDIR /app
96

10-
env:
11-
JAVA_VERSION: '17'
12-
MAVEN_CLI_OPTS: '-B --no-transfer-progress'
13-
DOCKER_IMAGE_NAME: reservation-service
14-
REGISTRY: docker.io
7+
# Copy Maven wrapper and pom.xml first for dependency caching
8+
COPY mvnw .
9+
COPY .mvn .mvn
10+
COPY pom.xml .
1511

16-
jobs:
17-
# Job 1: Code Quality & Security
18-
code-quality:
19-
name: Code Quality & Security Checks
20-
runs-on: ubuntu-latest
21-
22-
steps:
23-
- name: Checkout code
24-
uses: actions/checkout@v4
25-
with:
26-
fetch-depth: 0 # Shallow clones should be disabled for better analysis
12+
# Download dependencies (cached if pom.xml hasn't changed)
13+
RUN mvn dependency:go-offline -B
2714

28-
- name: Set up JDK ${{ env.JAVA_VERSION }}
29-
uses: actions/setup-java@v4
30-
with:
31-
java-version: ${{ env.JAVA_VERSION }}
32-
distribution: 'temurin'
33-
cache: 'maven'
15+
# Copy source code
16+
COPY src ./src
3417

35-
- name: Cache SonarCloud packages
36-
uses: actions/cache@v3
37-
with:
38-
path: ~/.sonar/cache
39-
key: ${{ runner.os }}-sonar
40-
restore-keys: ${{ runner.os }}-sonar
18+
# Build the application (skip tests for faster builds)
19+
RUN mvn clean package -DskipTests
4120

42-
- name: Run code style checks
43-
run: mvn ${{ env.MAVEN_CLI_OPTS }} checkstyle:check
44-
continue-on-error: true
21+
# Stage 2: Runtime stage
22+
FROM eclipse-temurin:17-jre
23+
WORKDIR /app
4524

46-
- name: Run SpotBugs analysis
47-
run: mvn ${{ env.MAVEN_CLI_OPTS }} compile spotbugs:check
48-
continue-on-error: true
25+
# Add non-root user for security
26+
RUN groupadd -r spring && useradd -r -g spring spring
27+
USER spring:spring
4928

50-
# Job 2: Build
51-
build:
52-
name: Build
53-
runs-on: ubuntu-latest
54-
needs: code-quality
29+
# Copy the built artifact from build stage
30+
COPY --from=build /app/target/reservation-service-*.jar app.jar
5531

56-
steps:
57-
- name: Checkout code
58-
uses: actions/checkout@v4
32+
# Expose the application port
33+
EXPOSE 8084
5934

60-
- name: Set up JDK ${{ env.JAVA_VERSION }}
61-
uses: actions/setup-java@v4
62-
with:
63-
java-version: ${{ env.JAVA_VERSION }}
64-
distribution: 'temurin'
65-
cache: 'maven'
35+
# Set JVM options for containerized environment
36+
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC"
6637

67-
- name: Build with Maven
68-
run: mvn ${{ env.MAVEN_CLI_OPTS }} clean compile
38+
# Health check
39+
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
40+
CMD curl -f http://localhost:8084/actuator/health || exit 1
6941

70-
- name: Package application
71-
run: mvn ${{ env.MAVEN_CLI_OPTS }} package -DskipTests
72-
73-
- name: Upload build artifact
74-
uses: actions/upload-artifact@v4
75-
with:
76-
name: reservation-service-jar
77-
path: target/*.jar
78-
retention-days: 5
79-
80-
# Job 3: Docker Build & Push
81-
docker-build-push:
82-
name: Build & Push Docker Image
83-
runs-on: ubuntu-latest
84-
needs: build
85-
if: github.event_name == 'push'
86-
permissions:
87-
contents: read
88-
packages: write
89-
90-
steps:
91-
- name: Checkout code
92-
uses: actions/checkout@v4
93-
94-
- name: Set up Docker Buildx
95-
uses: docker/setup-buildx-action@v3
96-
97-
- name: Log in to Docker Hub
98-
uses: docker/login-action@v3
99-
with:
100-
username: ${{ secrets.DOCKER_HUB_USERNAME }}
101-
password: ${{ secrets.DOCKER_HUB_TOKEN }}
102-
103-
- name: Extract metadata for Docker
104-
id: meta
105-
uses: docker/metadata-action@v5
106-
with:
107-
images: ${{ secrets.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}
108-
tags: |
109-
type=ref,event=branch
110-
type=ref,event=pr
111-
type=sha,prefix={{branch}}-
112-
type=semver,pattern={{version}}
113-
type=semver,pattern={{major}}.{{minor}}
114-
type=raw,value=latest,enable={{is_default_branch}}
115-
116-
- name: Build and push Docker image
117-
uses: docker/build-push-action@v5
118-
with:
119-
context: .
120-
file: ./Dockerfile
121-
push: true
122-
tags: ${{ steps.meta.outputs.tags }}
123-
labels: ${{ steps.meta.outputs.labels }}
124-
cache-from: type=gha
125-
cache-to: type=gha,mode=max
126-
platforms: linux/amd64,linux/arm64
127-
128-
- name: Generate SBOM
129-
uses: anchore/sbom-action@v0
130-
with:
131-
image: ${{ secrets.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:${{ github.sha }}
132-
format: spdx-json
133-
output-file: sbom.spdx.json
134-
135-
- name: Upload SBOM
136-
uses: actions/upload-artifact@v4
137-
with:
138-
name: sbom
139-
path: sbom.spdx.json
140-
141-
# Job 4: Security Scan
142-
security-scan:
143-
name: Security Vulnerability Scan
144-
runs-on: ubuntu-latest
145-
needs: docker-build-push
146-
if: github.event_name == 'push'
147-
permissions:
148-
contents: read
149-
security-events: write
150-
151-
steps:
152-
- name: Checkout code
153-
uses: actions/checkout@v4
154-
155-
- name: Log in to Docker Hub
156-
uses: docker/login-action@v3
157-
with:
158-
username: ${{ secrets.DOCKER_HUB_USERNAME }}
159-
password: ${{ secrets.DOCKER_HUB_TOKEN }}
160-
161-
- name: Run Trivy vulnerability scanner
162-
uses: aquasecurity/trivy-action@master
163-
with:
164-
image-ref: ${{ secrets.DOCKER_HUB_USERNAME }}/${{ env.DOCKER_IMAGE_NAME }}:${{ github.sha }}
165-
format: 'sarif'
166-
output: 'trivy-results.sarif'
167-
severity: 'CRITICAL,HIGH'
168-
169-
- name: Upload Trivy results to GitHub Security
170-
uses: github/codeql-action/upload-sarif@v3
171-
if: always()
172-
with:
173-
sarif_file: 'trivy-results.sarif'
174-
175-
- name: Dependency Check
176-
uses: dependency-check/Dependency-Check_Action@main
177-
with:
178-
project: 'reservation-service'
179-
path: '.'
180-
format: 'HTML'
181-
args: >
182-
--failOnCVSS 7
183-
--enableRetired
184-
185-
- name: Upload Dependency Check report
186-
uses: actions/upload-artifact@v4
187-
if: always()
188-
with:
189-
name: dependency-check-report
190-
path: reports/
191-
192-
# Job 5: Deploy to Development
193-
deploy-dev:
194-
name: Deploy to Development
195-
runs-on: ubuntu-latest
196-
needs: [docker-build-push, security-scan]
197-
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/dev-update'
198-
environment:
199-
name: development
200-
url: https://dev-reservation-service.exhibitflow.com
201-
202-
steps:
203-
- name: Checkout code
204-
uses: actions/checkout@v4
205-
206-
- name: Deploy to Development Server
207-
uses: appleboy/ssh-action@v1.0.0
208-
with:
209-
host: ${{ secrets.DEV_SERVER_HOST }}
210-
username: ${{ secrets.DEV_SERVER_USER }}
211-
key: ${{ secrets.DEV_SERVER_SSH_KEY }}
212-
port: ${{ secrets.DEV_SERVER_PORT }}
213-
script: |
214-
cd /opt/reservation-service
215-
docker compose pull
216-
docker compose up -d
217-
docker system prune -f
218-
219-
- name: Verify deployment
220-
run: |
221-
sleep 10
222-
curl -f ${{ secrets.DEV_SERVER_URL }}/actuator/health || exit 1
42+
# Run the application
43+
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

0 commit comments

Comments
 (0)