Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
d8709c0849 | |||
b753866b98 | |||
6141140beb | |||
c62ee5f699 | |||
cd59236473 | |||
18f77530ec |
@@ -3,16 +3,16 @@ name: Release - Northern Thailand Ping River Monitor
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- 'v*.*.*'
|
- "v*.*.*"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
version:
|
||||||
description: 'Release version (e.g., v3.1.3)'
|
description: "Release version (e.g., v3.1.3)"
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PYTHON_VERSION: '3.11'
|
PYTHON_VERSION: "3.11"
|
||||||
REGISTRY: git.b4l.co.th
|
REGISTRY: git.b4l.co.th
|
||||||
IMAGE_NAME: b4l/northern-thailand-ping-river-monitor
|
IMAGE_NAME: b4l/northern-thailand-ping-river-monitor
|
||||||
# GitHub token for better rate limits and authentication
|
# GitHub token for better rate limits and authentication
|
||||||
@@ -71,7 +71,7 @@ jobs:
|
|||||||
needs: create-release
|
needs: create-release
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ['3.9', '3.10', '3.11', '3.12']
|
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -161,8 +161,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITEA_TOKEN}}
|
token: ${{ secrets.GITEA_TOKEN}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Test release deployment locally
|
# Test release deployment locally
|
||||||
deploy-release:
|
deploy-release:
|
||||||
name: Test Release Deployment
|
name: Test Release Deployment
|
||||||
@@ -213,23 +211,81 @@ jobs:
|
|||||||
|
|
||||||
echo "🔍 Running health checks against local container..."
|
echo "🔍 Running health checks against local container..."
|
||||||
|
|
||||||
# Wait for the application to be ready
|
# Check if container is running
|
||||||
for i in {1..12}; do
|
docker ps | grep ping-river-monitor-test || echo "⚠️ Container not found in docker ps"
|
||||||
if curl -f http://localhost:8080/health; then
|
|
||||||
echo "✅ Health endpoint responding"
|
# Check container logs for any startup issues
|
||||||
|
echo "📋 Recent container logs:"
|
||||||
|
docker logs --tail 10 ping-river-monitor-test || true
|
||||||
|
|
||||||
|
# Wait for the application to be ready with more robust checking
|
||||||
|
echo "🔍 Testing application readiness..."
|
||||||
|
for i in {1..15}; do
|
||||||
|
echo "⏳ Attempt $i/15: Testing health endpoint..."
|
||||||
|
|
||||||
|
# Test health endpoint with container networking
|
||||||
|
echo "Testing health endpoint..."
|
||||||
|
|
||||||
|
# Get the container's IP address for direct communication
|
||||||
|
CONTAINER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ping-river-monitor-test)
|
||||||
|
echo "Container IP: $CONTAINER_IP"
|
||||||
|
|
||||||
|
# Test using container IP directly (port 8000 inside container)
|
||||||
|
if [ -n "$CONTAINER_IP" ]; then
|
||||||
|
response=$(curl -s --max-time 10 --connect-timeout 5 -w "HTTP_CODE:%{http_code}" http://$CONTAINER_IP:8000/health)
|
||||||
|
else
|
||||||
|
# Fallback to localhost if IP detection fails
|
||||||
|
response=$(curl -s --max-time 10 --connect-timeout 5 -w "HTTP_CODE:%{http_code}" http://127.0.0.1:8080/health)
|
||||||
|
fi
|
||||||
|
|
||||||
|
http_code=$(echo "$response" | grep -o "HTTP_CODE:[0-9]*" | cut -d: -f2)
|
||||||
|
response_body=$(echo "$response" | sed 's/HTTP_CODE:[0-9]*$//')
|
||||||
|
|
||||||
|
echo "HTTP Code: $http_code"
|
||||||
|
echo "Response Body: $response_body"
|
||||||
|
|
||||||
|
if [ "$http_code" = "200" ] && [ -n "$response_body" ]; then
|
||||||
|
echo "✅ Health endpoint responding successfully!"
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
echo "⏳ Waiting for health endpoint... (attempt $i/12)"
|
echo "❌ Health check failed (HTTP: $http_code), waiting 15 seconds..."
|
||||||
sleep 10
|
# Show what's happening with the container
|
||||||
|
echo "Container status:"
|
||||||
|
docker ps | grep ping-river-monitor-test || echo "Container not found"
|
||||||
|
echo "Recent container logs:"
|
||||||
|
docker logs --tail 5 ping-river-monitor-test || true
|
||||||
|
sleep 15
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Test API endpoints
|
# Test API endpoints with container networking
|
||||||
echo "🧪 Testing API endpoints..."
|
echo "🧪 Testing API endpoints..."
|
||||||
curl -f http://localhost:8080/health || exit 1
|
|
||||||
curl -f http://localhost:8080/docs || exit 1
|
# Get container IP for direct communication
|
||||||
curl -f http://localhost:8080/stations || exit 1
|
CONTAINER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ping-river-monitor-test)
|
||||||
curl -f http://localhost:8080/metrics || exit 1
|
echo "Using container IP: $CONTAINER_IP"
|
||||||
|
|
||||||
|
endpoints=("health" "docs" "stations" "metrics")
|
||||||
|
for endpoint in "${endpoints[@]}"; do
|
||||||
|
echo "Testing /$endpoint..."
|
||||||
|
|
||||||
|
# Use container IP if available, otherwise fallback to localhost
|
||||||
|
if [ -n "$CONTAINER_IP" ]; then
|
||||||
|
response=$(curl -s --max-time 10 -w "HTTP_CODE:%{http_code}" http://$CONTAINER_IP:8000/$endpoint)
|
||||||
|
else
|
||||||
|
response=$(curl -s --max-time 10 -w "HTTP_CODE:%{http_code}" http://127.0.0.1:8080/$endpoint)
|
||||||
|
fi
|
||||||
|
|
||||||
|
http_code=$(echo "$response" | grep -o "HTTP_CODE:[0-9]*" | cut -d: -f2)
|
||||||
|
|
||||||
|
if [ "$http_code" = "200" ]; then
|
||||||
|
echo "✅ /$endpoint: OK (HTTP $http_code)"
|
||||||
|
else
|
||||||
|
echo "❌ /$endpoint: FAILED (HTTP $http_code)"
|
||||||
|
echo "Response: $(echo "$response" | sed 's/HTTP_CODE:[0-9]*$//')"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
echo "✅ All health checks passed!"
|
echo "✅ All health checks passed!"
|
||||||
|
|
||||||
@@ -249,8 +305,6 @@ jobs:
|
|||||||
echo "Status: Container tested successfully"
|
echo "Status: Container tested successfully"
|
||||||
echo "Ready for production deployment"
|
echo "Ready for production deployment"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Notify stakeholders
|
# Notify stakeholders
|
||||||
notify:
|
notify:
|
||||||
name: Notify Release
|
name: Notify Release
|
||||||
|
11
Dockerfile
11
Dockerfile
@@ -22,26 +22,27 @@ FROM python:3.11-slim
|
|||||||
# Set working directory
|
# Set working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install runtime dependencies
|
# Install runtime dependencies and create user
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
wget \
|
wget \
|
||||||
curl \
|
curl \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& groupadd -r appuser && useradd -r -g appuser appuser
|
&& groupadd -r appuser && useradd -r -g appuser appuser \
|
||||||
|
&& mkdir -p /home/appuser/.local
|
||||||
|
|
||||||
# Copy Python packages from builder stage
|
# Copy Python packages from builder stage
|
||||||
COPY --from=builder /root/.local /root/.local
|
COPY --from=builder /root/.local /home/appuser/.local
|
||||||
|
|
||||||
# Copy application code
|
# Copy application code
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Create logs directory and set permissions
|
# Create logs directory and set permissions
|
||||||
RUN mkdir -p logs && chown -R appuser:appuser /app
|
RUN mkdir -p logs && chown -R appuser:appuser /app /home/appuser/.local
|
||||||
|
|
||||||
# Set environment variables
|
# Set environment variables
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
ENV TZ=Asia/Bangkok
|
ENV TZ=Asia/Bangkok
|
||||||
ENV PATH=/root/.local/bin:$PATH
|
ENV PATH=/home/appuser/.local/bin:$PATH
|
||||||
|
|
||||||
# Switch to non-root user
|
# Switch to non-root user
|
||||||
USER appuser
|
USER appuser
|
||||||
|
Reference in New Issue
Block a user