Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
6141140beb | |||
c62ee5f699 | |||
cd59236473 | |||
18f77530ec | |||
f21d05f404 |
@@ -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
|
||||||
@@ -25,44 +25,44 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.version.outputs.version }}
|
version: ${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITEA_TOKEN }}
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Get version
|
- name: Get version
|
||||||
id: version
|
id: version
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||||
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
|
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Generate changelog
|
- name: Generate changelog
|
||||||
id: changelog
|
id: changelog
|
||||||
run: |
|
run: |
|
||||||
# Generate changelog from git commits
|
# Generate changelog from git commits
|
||||||
echo "## Changes" > CHANGELOG.md
|
echo "## Changes" > CHANGELOG.md
|
||||||
git log --pretty=format:"- %s" $(git describe --tags --abbrev=0 HEAD^)..HEAD >> CHANGELOG.md || echo "- Initial release" >> CHANGELOG.md
|
git log --pretty=format:"- %s" $(git describe --tags --abbrev=0 HEAD^)..HEAD >> CHANGELOG.md || echo "- Initial release" >> CHANGELOG.md
|
||||||
echo "" >> CHANGELOG.md
|
echo "" >> CHANGELOG.md
|
||||||
echo "## Docker Images" >> CHANGELOG.md
|
echo "## Docker Images" >> CHANGELOG.md
|
||||||
echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}\`" >> CHANGELOG.md
|
echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}\`" >> CHANGELOG.md
|
||||||
echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest\`" >> CHANGELOG.md
|
echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest\`" >> CHANGELOG.md
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: actions/create-release@v1
|
uses: actions/create-release@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ steps.version.outputs.version }}
|
tag_name: ${{ steps.version.outputs.version }}
|
||||||
release_name: Northern Thailand Ping River Monitor ${{ steps.version.outputs.version }}
|
release_name: Northern Thailand Ping River Monitor ${{ steps.version.outputs.version }}
|
||||||
body_path: CHANGELOG.md
|
body_path: CHANGELOG.md
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
|
||||||
# Build and test for release
|
# Build and test for release
|
||||||
test-release:
|
test-release:
|
||||||
@@ -71,97 +71,95 @@ 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
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITEA_TOKEN }}
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip --root-user-action=ignore
|
python -m pip install --upgrade pip --root-user-action=ignore
|
||||||
pip install --root-user-action=ignore -r requirements.txt
|
pip install --root-user-action=ignore -r requirements.txt
|
||||||
pip install --root-user-action=ignore -r requirements-dev.txt
|
pip install --root-user-action=ignore -r requirements-dev.txt
|
||||||
|
|
||||||
- name: Run full test suite
|
- name: Run full test suite
|
||||||
run: |
|
run: |
|
||||||
python tests/test_integration.py
|
python tests/test_integration.py
|
||||||
python tests/test_station_management.py
|
python tests/test_station_management.py
|
||||||
python run.py --test
|
python run.py --test
|
||||||
|
|
||||||
- name: Build Python package
|
- name: Build Python package
|
||||||
run: |
|
run: |
|
||||||
pip install --root-user-action=ignore build
|
pip install --root-user-action=ignore build
|
||||||
python -m build
|
python -m build
|
||||||
|
|
||||||
- name: Upload Python package
|
- name: Upload Python package
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: python-package-${{ matrix.python-version }}
|
name: python-package-${{ matrix.python-version }}
|
||||||
path: dist/
|
path: dist/
|
||||||
|
|
||||||
# Build release Docker images
|
# Build release Docker images
|
||||||
build-release:
|
build-release:
|
||||||
name: Build Release Images
|
name: Build Release Images
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [create-release, test-release]
|
needs: [create-release, test-release]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITEA_TOKEN }}
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Log in to Container Registry
|
- name: Log in to Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ vars.WORKER_USERNAME}}
|
username: ${{ vars.WORKER_USERNAME}}
|
||||||
password: ${{ secrets.CI_BOT_TOKEN }}
|
password: ${{ secrets.CI_BOT_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push release images
|
- name: Build and push release images
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
labels: |
|
labels: |
|
||||||
org.opencontainers.image.title=Northern Thailand Ping River Monitor
|
org.opencontainers.image.title=Northern Thailand Ping River Monitor
|
||||||
org.opencontainers.image.description=Real-time water level monitoring for Ping River Basin
|
org.opencontainers.image.description=Real-time water level monitoring for Ping River Basin
|
||||||
org.opencontainers.image.version=${{ needs.create-release.outputs.version }}
|
org.opencontainers.image.version=${{ needs.create-release.outputs.version }}
|
||||||
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
||||||
org.opencontainers.image.revision=${{ github.sha }}
|
org.opencontainers.image.revision=${{ github.sha }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
# Security scan for release
|
# Security scan for release
|
||||||
security-scan:
|
security-scan:
|
||||||
name: Security Scan
|
name: Security Scan
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build-release
|
needs: build-release
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITEA_TOKEN}}
|
|
||||||
|
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN}}
|
||||||
|
|
||||||
# Test release deployment locally
|
# Test release deployment locally
|
||||||
deploy-release:
|
deploy-release:
|
||||||
@@ -171,78 +169,97 @@ jobs:
|
|||||||
environment:
|
environment:
|
||||||
name: testing
|
name: testing
|
||||||
url: http://localhost:8080
|
url: http://localhost:8080
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITEA_TOKEN }}
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Deploy to production (Local Test)
|
|
||||||
run: |
|
|
||||||
echo "🚀 Testing ${{ needs.create-release.outputs.version }} deployment locally..."
|
|
||||||
|
|
||||||
# Pull the built image
|
|
||||||
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}
|
|
||||||
|
|
||||||
# Stop any existing containers
|
|
||||||
docker stop ping-river-monitor-test || true
|
|
||||||
docker rm ping-river-monitor-test || true
|
|
||||||
|
|
||||||
# Start the container for testing
|
|
||||||
docker run -d \
|
|
||||||
--name ping-river-monitor-test \
|
|
||||||
-p 8080:8000 \
|
|
||||||
-e LOG_LEVEL=INFO \
|
|
||||||
-e DB_TYPE=sqlite \
|
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}
|
|
||||||
|
|
||||||
echo "✅ Container started for testing"
|
|
||||||
|
|
||||||
- name: Health check after deployment
|
|
||||||
run: |
|
|
||||||
echo "⏳ Waiting for application to start..."
|
|
||||||
sleep 30
|
|
||||||
|
|
||||||
echo "🔍 Running health checks against local container..."
|
|
||||||
|
|
||||||
# Wait for the application to be ready
|
|
||||||
for i in {1..12}; do
|
|
||||||
if curl -f http://localhost:8080/health; then
|
|
||||||
echo "✅ Health endpoint responding"
|
|
||||||
break
|
|
||||||
else
|
|
||||||
echo "⏳ Waiting for health endpoint... (attempt $i/12)"
|
|
||||||
sleep 10
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Test API endpoints
|
|
||||||
echo "🧪 Testing API endpoints..."
|
|
||||||
curl -f http://localhost:8080/health || exit 1
|
|
||||||
curl -f http://localhost:8080/docs || exit 1
|
|
||||||
curl -f http://localhost:8080/stations || exit 1
|
|
||||||
curl -f http://localhost:8080/metrics || exit 1
|
|
||||||
|
|
||||||
echo "✅ All health checks passed!"
|
|
||||||
|
|
||||||
- name: Container logs and cleanup
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
echo "📋 Container logs:"
|
|
||||||
docker logs ping-river-monitor-test || true
|
|
||||||
|
|
||||||
echo "🧹 Cleaning up test container..."
|
|
||||||
docker stop ping-river-monitor-test || true
|
|
||||||
docker rm ping-river-monitor-test || true
|
|
||||||
|
|
||||||
echo "📊 Deployment Test Summary:"
|
|
||||||
echo "Version: ${{ needs.create-release.outputs.version }}"
|
|
||||||
echo "Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}"
|
|
||||||
echo "Status: Container tested successfully"
|
|
||||||
echo "Ready for production deployment"
|
|
||||||
|
|
||||||
|
- name: Log in to Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ vars.WORKER_USERNAME}}
|
||||||
|
password: ${{ secrets.CI_BOT_TOKEN }}
|
||||||
|
|
||||||
|
- name: Deploy to production (Local Test)
|
||||||
|
run: |
|
||||||
|
echo "🚀 Testing ${{ needs.create-release.outputs.version }} deployment locally..."
|
||||||
|
|
||||||
|
# Pull the built image
|
||||||
|
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}
|
||||||
|
|
||||||
|
# Stop any existing containers
|
||||||
|
docker stop ping-river-monitor-test || true
|
||||||
|
docker rm ping-river-monitor-test || true
|
||||||
|
|
||||||
|
# Start the container for testing
|
||||||
|
docker run -d \
|
||||||
|
--name ping-river-monitor-test \
|
||||||
|
-p 8080:8000 \
|
||||||
|
-e LOG_LEVEL=INFO \
|
||||||
|
-e DB_TYPE=sqlite \
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}
|
||||||
|
|
||||||
|
echo "✅ Container started for testing"
|
||||||
|
|
||||||
|
- name: Health check after deployment
|
||||||
|
run: |
|
||||||
|
echo "⏳ Waiting for application to start..."
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
echo "🔍 Running health checks against local container..."
|
||||||
|
|
||||||
|
# Check if container is running
|
||||||
|
docker ps | grep ping-river-monitor-test || echo "⚠️ Container not found in docker ps"
|
||||||
|
|
||||||
|
# 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..."
|
||||||
|
|
||||||
|
# Use curl with more verbose output and longer timeout
|
||||||
|
if curl -f -s --max-time 10 --connect-timeout 5 http://127.0.0.1:8080/health; then
|
||||||
|
echo "✅ Health endpoint responding successfully!"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "❌ Health check failed, waiting 15 seconds..."
|
||||||
|
# Show what's happening with the container
|
||||||
|
echo "Container status:"
|
||||||
|
docker ps | grep ping-river-monitor-test || echo "Container not found"
|
||||||
|
sleep 15
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Test API endpoints
|
||||||
|
echo "🧪 Testing API endpoints..."
|
||||||
|
curl -f http://127.0.0.1:8080/health || exit 1
|
||||||
|
curl -f http://127.0.0.1:8080/docs || exit 1
|
||||||
|
curl -f http://127.0.0.1:8080/stations || exit 1
|
||||||
|
curl -f http://127.0.0.1:8080/metrics || exit 1
|
||||||
|
|
||||||
|
echo "✅ All health checks passed!"
|
||||||
|
|
||||||
|
- name: Container logs and cleanup
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
echo "📋 Container logs:"
|
||||||
|
docker logs ping-river-monitor-test || true
|
||||||
|
|
||||||
|
echo "🧹 Cleaning up test container..."
|
||||||
|
docker stop ping-river-monitor-test || true
|
||||||
|
docker rm ping-river-monitor-test || true
|
||||||
|
|
||||||
|
echo "📊 Deployment Test Summary:"
|
||||||
|
echo "Version: ${{ needs.create-release.outputs.version }}"
|
||||||
|
echo "Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}"
|
||||||
|
echo "Status: Container tested successfully"
|
||||||
|
echo "Ready for production deployment"
|
||||||
|
|
||||||
# Notify stakeholders
|
# Notify stakeholders
|
||||||
notify:
|
notify:
|
||||||
@@ -250,28 +267,28 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [create-release, deploy-release]
|
needs: [create-release, deploy-release]
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Notify success
|
- name: Notify success
|
||||||
if: needs.deploy-release.result == 'success'
|
if: needs.deploy-release.result == 'success'
|
||||||
run: |
|
run: |
|
||||||
echo "🎉 Release ${{ needs.create-release.outputs.version }} tested successfully!"
|
echo "🎉 Release ${{ needs.create-release.outputs.version }} tested successfully!"
|
||||||
echo "🧪 Local Test: Passed all health checks"
|
echo "🧪 Local Test: Passed all health checks"
|
||||||
echo "<22> GDocker Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}"
|
echo "<22> GDocker Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}"
|
||||||
echo "✅ Ready for production deployment"
|
echo "✅ Ready for production deployment"
|
||||||
|
|
||||||
# Add notification to Slack, Discord, email, etc.
|
# Add notification to Slack, Discord, email, etc.
|
||||||
# curl -X POST -H 'Content-type: application/json' \
|
# curl -X POST -H 'Content-type: application/json' \
|
||||||
# --data '{"text":"🎉 Northern Thailand Ping River Monitor ${{ needs.create-release.outputs.version }} tested and ready for deployment!"}' \
|
# --data '{"text":"🎉 Northern Thailand Ping River Monitor ${{ needs.create-release.outputs.version }} tested and ready for deployment!"}' \
|
||||||
# ${{ secrets.SLACK_WEBHOOK_URL }}
|
# ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||||
|
|
||||||
- name: Notify failure
|
- name: Notify failure
|
||||||
if: needs.deploy-release.result == 'failure'
|
if: needs.deploy-release.result == 'failure'
|
||||||
run: |
|
run: |
|
||||||
echo "❌ Release ${{ needs.create-release.outputs.version }} testing failed!"
|
echo "❌ Release ${{ needs.create-release.outputs.version }} testing failed!"
|
||||||
echo "Please check the logs and fix issues before production deployment."
|
echo "Please check the logs and fix issues before production deployment."
|
||||||
|
|
||||||
# Add failure notification
|
# Add failure notification
|
||||||
# curl -X POST -H 'Content-type: application/json' \
|
# curl -X POST -H 'Content-type: application/json' \
|
||||||
# --data '{"text":"❌ Northern Thailand Ping River Monitor ${{ needs.create-release.outputs.version }} testing failed!"}' \
|
# --data '{"text":"❌ Northern Thailand Ping River Monitor ${{ needs.create-release.outputs.version }} testing failed!"}' \
|
||||||
# ${{ secrets.SLACK_WEBHOOK_URL }}
|
# ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||||
|
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