Skip to content

Commit 073cf77

Browse files
Merge pull request #1 from turabbb/feat/containerization
Container created for different services
2 parents b22ff46 + ebb0cb3 commit 073cf77

File tree

8 files changed

+618
-243
lines changed

8 files changed

+618
-243
lines changed

.env.example

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
name: Healthcare DevOps Pipeline
2+
3+
# When to run this pipeline
4+
on:
5+
push:
6+
branches: [main, devops-pipeline]
7+
pull_request:
8+
branches: [main]
9+
10+
# Environment variables used across all jobs
11+
env:
12+
DOCKER_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/healthcare-fastapi
13+
PYTHON_VERSION: '3.11'
14+
15+
# Jobs run in order (each job = one stage)
16+
jobs:
17+
18+
# =====================================================
19+
# STAGE 1: BUILD & INSTALL DEPENDENCIES
20+
# =====================================================
21+
build-and-install:
22+
name: 📦 Build & Install Dependencies
23+
runs-on: ubuntu-latest
24+
25+
steps:
26+
- name: 📥 Checkout code
27+
uses: actions/checkout@v4
28+
29+
- name: 🐍 Set up Python ${{ env.PYTHON_VERSION }}
30+
uses: actions/setup-python@v5
31+
with:
32+
python-version: ${{ env.PYTHON_VERSION }}
33+
34+
- name: 💾 Cache pip dependencies
35+
uses: actions/cache@v3
36+
with:
37+
path: ~/.cache/pip
38+
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
39+
restore-keys: |
40+
${{ runner.os }}-pip-
41+
42+
- name: ⬇️ Install dependencies
43+
run: |
44+
python -m pip install --upgrade pip
45+
pip install -r requirements.txt
46+
echo "✅ Dependencies installed successfully"
47+
48+
- name: 📋 List installed packages
49+
run: pip list
50+
51+
# =====================================================
52+
# STAGE 2: CODE QUALITY & SECURITY CHECKS
53+
# =====================================================
54+
lint-and-security:
55+
name: 🔍 Lint & Security Scan
56+
runs-on: ubuntu-latest
57+
needs: build-and-install # Wait for stage 1 to complete
58+
59+
steps:
60+
- name: 📥 Checkout code
61+
uses: actions/checkout@v4
62+
63+
- name: 🐍 Set up Python ${{ env.PYTHON_VERSION }}
64+
uses: actions/setup-python@v5
65+
with:
66+
python-version: ${{ env.PYTHON_VERSION }}
67+
68+
- name: ⬇️ Install linting tools
69+
run: |
70+
pip install flake8 bandit safety pylint black
71+
72+
- name: 🎨 Check code formatting with Black
73+
run: |
74+
black --check app || echo "⚠️ Code needs formatting"
75+
continue-on-error: true
76+
77+
- name: 🔎 Lint with Flake8
78+
run: |
79+
flake8 app --count --select=E9,F63,F7,F82 --show-source --statistics
80+
flake8 app --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
81+
continue-on-error: true
82+
83+
- name: 🛡️ Security scan with Bandit
84+
run: |
85+
bandit -r app -ll -f json -o bandit-report.json || true
86+
bandit -r app -ll
87+
continue-on-error: true
88+
89+
- name: 🔐 Check dependency vulnerabilities
90+
run: |
91+
safety check --json || echo "⚠️ Vulnerabilities found (non-blocking)"
92+
continue-on-error: true
93+
94+
- name: 📊 Upload Bandit results
95+
if: always()
96+
uses: actions/upload-artifact@v4
97+
with:
98+
name: bandit-security-report
99+
path: bandit-report.json
100+
101+
# =====================================================
102+
# STAGE 3: AUTOMATED TESTING WITH DATABASE
103+
# =====================================================
104+
test:
105+
name: 🧪 Run Tests with Database Services
106+
runs-on: ubuntu-latest
107+
needs: lint-and-security
108+
109+
# These run as separate containers (like docker-compose)
110+
services:
111+
postgres:
112+
image: postgres:15-alpine
113+
env:
114+
POSTGRES_USER: test_user
115+
POSTGRES_PASSWORD: test_password
116+
POSTGRES_DB: test_healthcare_db
117+
ports:
118+
- 5432:5432
119+
options: >-
120+
--health-cmd pg_isready
121+
--health-interval 10s
122+
--health-timeout 5s
123+
--health-retries 5
124+
125+
redis:
126+
image: redis:7-alpine
127+
ports:
128+
- 6379:6379
129+
options: >-
130+
--health-cmd "redis-cli ping"
131+
--health-interval 10s
132+
--health-timeout 5s
133+
--health-retries 5
134+
135+
steps:
136+
- name: 📥 Checkout code
137+
uses: actions/checkout@v4
138+
139+
- name: 🐍 Set up Python ${{ env.PYTHON_VERSION }}
140+
uses: actions/setup-python@v5
141+
with:
142+
python-version: ${{ env.PYTHON_VERSION }}
143+
144+
- name: ⬇️ Install dependencies
145+
run: |
146+
pip install -r requirements.txt
147+
pip install pytest pytest-cov pytest-asyncio httpx
148+
149+
- name: 🗃️ Wait for PostgreSQL
150+
run: |
151+
until pg_isready -h localhost -p 5432 -U test_user; do
152+
echo "Waiting for PostgreSQL..."
153+
sleep 2
154+
done
155+
echo "✅ PostgreSQL is ready"
156+
157+
- name: 🧪 Run tests with coverage
158+
env:
159+
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_healthcare_db
160+
REDIS_URL: redis://localhost:6379/0
161+
SECRET_KEY: test_secret_key_for_ci_minimum_32_characters_long
162+
ENVIRONMENT: testing
163+
run: |
164+
pytest tests/ -v --cov=app --cov-report=xml --cov-report=html --cov-report=term
165+
echo "✅ All tests passed!"
166+
167+
- name: 📊 Upload coverage reports
168+
uses: codecov/codecov-action@v3
169+
with:
170+
file: ./coverage.xml
171+
flags: unittests
172+
name: codecov-healthcare
173+
174+
- name: 📁 Upload coverage HTML
175+
uses: actions/upload-artifact@v4
176+
with:
177+
name: coverage-report
178+
path: htmlcov/
179+
180+
# =====================================================
181+
# STAGE 4: BUILD & PUSH DOCKER IMAGE
182+
# =====================================================
183+
build-docker:
184+
name: 🐳 Build & Push Docker Image
185+
runs-on: ubuntu-latest
186+
needs: test # Only runs if tests pass
187+
188+
steps:
189+
- name: 📥 Checkout code
190+
uses: actions/checkout@v4
191+
192+
- name: 🔧 Set up Docker Buildx
193+
uses: docker/setup-buildx-action@v3
194+
195+
- name: 🔑 Login to DockerHub
196+
uses: docker/login-action@v3
197+
with:
198+
username: ${{ secrets.DOCKERHUB_USERNAME }}
199+
password: ${{ secrets.DOCKERHUB_TOKEN }}
200+
201+
- name: 🏷️ Extract metadata
202+
id: meta
203+
uses: docker/metadata-action@v5
204+
with:
205+
images: ${{ env.DOCKER_IMAGE }}
206+
tags: |
207+
type=ref,event=branch
208+
type=sha,prefix={{branch}}-
209+
type=raw,value=latest,enable={{is_default_branch}}
210+
211+
- name: 🏗️ Build and push Docker image
212+
uses: docker/build-push-action@v5
213+
with:
214+
context: .
215+
push: true
216+
tags: ${{ steps.meta.outputs.tags }}
217+
labels: ${{ steps.meta.outputs.labels }}
218+
cache-from: type=registry,ref=${{ env.DOCKER_IMAGE }}:buildcache
219+
cache-to: type=registry,ref=${{ env.DOCKER_IMAGE }}:buildcache,mode=max
220+
build-args: |
221+
BUILDTIME=${{ github.event.head_commit.timestamp }}
222+
VERSION=${{ github.sha }}
223+
224+
- name: 📋 Image Details
225+
run: |
226+
echo "🐳 Docker Image: ${{ env.DOCKER_IMAGE }}"
227+
echo "🏷️ Tags: ${{ steps.meta.outputs.tags }}"
228+
echo "📅 Build Time: $(date)"
229+
echo "🔗 View on Docker Hub: https://hub.docker.com/r/${{ secrets.DOCKERHUB_USERNAME }}/healthcare-fastapi"
230+
231+
# =====================================================
232+
# STAGE 5: DEPLOY TO PRODUCTION (CONDITIONAL)
233+
# =====================================================
234+
deploy:
235+
name: 🚀 Deploy to Production
236+
runs-on: ubuntu-latest
237+
needs: build-docker
238+
# Only deploy when pushing to main branch
239+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
240+
241+
steps:
242+
- name: 📥 Checkout code
243+
uses: actions/checkout@v4
244+
245+
- name: 🚀 Deploy to Render
246+
run: |
247+
echo "🔄 Triggering deployment to Render..."
248+
response=$(curl -s -o /dev/null -w "%{http_code}" -X POST "${{ secrets.RENDER_DEPLOY_HOOK }}")
249+
if [ $response -eq 200 ] || [ $response -eq 201 ]; then
250+
echo "✅ Deployment triggered successfully!"
251+
else
252+
echo "❌ Deployment failed with status code: $response"
253+
exit 1
254+
fi
255+
256+
- name: 📊 Deployment Summary
257+
run: |
258+
echo "### 🎉 Deployment Successful!" >> $GITHUB_STEP_SUMMARY
259+
echo "" >> $GITHUB_STEP_SUMMARY
260+
echo "- 🐳 *Image*: ${{ env.DOCKER_IMAGE }}:${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
261+
echo "- 📅 *Time*: $(date)" >> $GITHUB_STEP_SUMMARY
262+
echo "- 🔗 *Docker Hub*: [View Image](https://hub.docker.com/r/${{ secrets.DOCKERHUB_USERNAME }}/healthcare-fastapi)" >> $GITHUB_STEP_SUMMARY
263+
echo "- ✅ *Status*: Deployed to Production" >> $GITHUB_STEP_SUMMARY
264+
265+
- name: 🔔 Notify deployment
266+
if: always()
267+
run: |
268+
echo "Deployment completed at $(date)"
269+
echo "Check your Render dashboard for live status"
270+
271+
# =====================================================
272+
# SUMMARY JOB: OVERALL PIPELINE STATUS
273+
# =====================================================
274+
pipeline-summary:
275+
name: 📋 Pipeline Summary
276+
runs-on: ubuntu-latest
277+
needs: [build-and-install, lint-and-security, test, build-docker]
278+
if: always()
279+
280+
steps:
281+
- name: 📊 Generate Summary
282+
run: |
283+
echo "### 🏁 Pipeline Execution Summary" >> $GITHUB_STEP_SUMMARY
284+
echo "" >> $GITHUB_STEP_SUMMARY
285+
echo "| Stage | Status |" >> $GITHUB_STEP_SUMMARY
286+
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
287+
echo "| Build & Install | ${{ needs.build-and-install.result }} |" >> $GITHUB_STEP_SUMMARY
288+
echo "| Lint & Security | ${{ needs.lint-and-security.result }} |" >> $GITHUB_STEP_SUMMARY
289+
echo "| Tests | ${{ needs.test.result }} |" >> $GITHUB_STEP_SUMMARY
290+
echo "| Docker Build | ${{ needs.build-docker.result }} |" >> $GITHUB_STEP_SUMMARY
291+
echo "" >> $GITHUB_STEP_SUMMARY
292+
echo "*Commit*: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
293+
echo "*Branch*: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
294+
echo "*Triggered by*: ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY

0 commit comments

Comments
 (0)