Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
c62ee5f699 | |||
cd59236473 | |||
18f77530ec | |||
f21d05f404 | |||
ff447292f0 | |||
da4545c6d8 | |||
e0ff8c89fb | |||
5579637995 | |||
1816b6e14a | |||
8dedc9303b | |||
94c6db9b72 | |||
0afb57789b | |||
02a0f479dc | |||
841a5a492c | |||
17a716fcd0 | |||
7c04871fdd | |||
af53f68d2c | |||
985f9754c4 | |||
4ed5f2ccad | |||
123ec13896 | |||
4a30af60e8 | |||
e5d5284ee3 | |||
cd74cd6d10 | |||
9c6fedc149 | |||
40aef686af |
@@ -28,6 +28,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
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
|
||||||
@@ -98,6 +100,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@@ -134,6 +138,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
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
|
||||||
@@ -193,6 +199,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Wait for VictoriaMetrics
|
- name: Wait for VictoriaMetrics
|
||||||
run: |
|
run: |
|
||||||
@@ -244,6 +252,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Deploy to staging
|
- name: Deploy to staging
|
||||||
run: |
|
run: |
|
||||||
@@ -269,6 +279,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Deploy to production
|
- name: Deploy to production
|
||||||
run: |
|
run: |
|
||||||
@@ -296,6 +308,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Install Apache Bench
|
- name: Install Apache Bench
|
||||||
run: |
|
run: |
|
||||||
|
@@ -27,6 +27,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@@ -127,6 +129,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@@ -224,6 +228,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@@ -248,8 +254,8 @@ jobs:
|
|||||||
project = 'Northern Thailand Ping River Monitor'
|
project = 'Northern Thailand Ping River Monitor'
|
||||||
copyright = '2025, Ping River Monitor Team'
|
copyright = '2025, Ping River Monitor Team'
|
||||||
author = 'Ping River Monitor Team'
|
author = 'Ping River Monitor Team'
|
||||||
version = '3.1.1'
|
version = '3.1.3'
|
||||||
release = '3.1.1'
|
release = '3.1.3'
|
||||||
|
|
||||||
extensions = [
|
extensions = [
|
||||||
'sphinx.ext.autodoc',
|
'sphinx.ext.autodoc',
|
||||||
|
@@ -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.1)'
|
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,43 +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:
|
||||||
fetch-depth: 0
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
fetch-depth: 0
|
||||||
- name: Get version
|
|
||||||
id: version
|
- name: Get version
|
||||||
run: |
|
id: version
|
||||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
run: |
|
||||||
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||||
else
|
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
|
||||||
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
else
|
||||||
fi
|
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
- name: Generate changelog
|
|
||||||
id: changelog
|
- name: Generate changelog
|
||||||
run: |
|
id: changelog
|
||||||
# Generate changelog from git commits
|
run: |
|
||||||
echo "## Changes" > CHANGELOG.md
|
# Generate changelog from git commits
|
||||||
git log --pretty=format:"- %s" $(git describe --tags --abbrev=0 HEAD^)..HEAD >> CHANGELOG.md || echo "- Initial release" >> CHANGELOG.md
|
echo "## Changes" > CHANGELOG.md
|
||||||
echo "" >> CHANGELOG.md
|
git log --pretty=format:"- %s" $(git describe --tags --abbrev=0 HEAD^)..HEAD >> CHANGELOG.md || echo "- Initial release" >> CHANGELOG.md
|
||||||
echo "## Docker Images" >> CHANGELOG.md
|
echo "" >> CHANGELOG.md
|
||||||
echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}\`" >> CHANGELOG.md
|
echo "## Docker Images" >> CHANGELOG.md
|
||||||
echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest\`" >> CHANGELOG.md
|
echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}\`" >> CHANGELOG.md
|
||||||
|
echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest\`" >> CHANGELOG.md
|
||||||
- name: Create Release
|
|
||||||
uses: actions/create-release@v1
|
- name: Create Release
|
||||||
env:
|
uses: actions/create-release@v1
|
||||||
GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
env:
|
||||||
with:
|
GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
tag_name: ${{ steps.version.outputs.version }}
|
with:
|
||||||
release_name: Northern Thailand Ping River Monitor ${{ steps.version.outputs.version }}
|
tag_name: ${{ steps.version.outputs.version }}
|
||||||
body_path: CHANGELOG.md
|
release_name: Northern Thailand Ping River Monitor ${{ steps.version.outputs.version }}
|
||||||
draft: false
|
body_path: CHANGELOG.md
|
||||||
prerelease: false
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
# Build and test for release
|
# Build and test for release
|
||||||
test-release:
|
test-release:
|
||||||
@@ -70,227 +71,217 @@ 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:
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
python-version: ${{ matrix.python-version }}
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
- name: Install dependencies
|
python-version: ${{ matrix.python-version }}
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip --root-user-action=ignore
|
- name: Install dependencies
|
||||||
pip install --root-user-action=ignore -r requirements.txt
|
run: |
|
||||||
pip install --root-user-action=ignore -r requirements-dev.txt
|
python -m pip install --upgrade pip --root-user-action=ignore
|
||||||
|
pip install --root-user-action=ignore -r requirements.txt
|
||||||
- name: Run full test suite
|
pip install --root-user-action=ignore -r requirements-dev.txt
|
||||||
run: |
|
|
||||||
python tests/test_integration.py
|
- name: Run full test suite
|
||||||
python tests/test_station_management.py
|
run: |
|
||||||
python run.py --test
|
python tests/test_integration.py
|
||||||
|
python tests/test_station_management.py
|
||||||
- name: Build Python package
|
python run.py --test
|
||||||
run: |
|
|
||||||
pip install --root-user-action=ignore build
|
- name: Build Python package
|
||||||
python -m build
|
run: |
|
||||||
|
pip install --root-user-action=ignore build
|
||||||
- name: Upload Python package
|
python -m build
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
- name: Upload Python package
|
||||||
name: python-package-${{ matrix.python-version }}
|
uses: actions/upload-artifact@v3
|
||||||
path: dist/
|
with:
|
||||||
|
name: python-package-${{ matrix.python-version }}
|
||||||
|
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:
|
||||||
- name: Set up Docker Buildx
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
- name: Log in to Container Registry
|
uses: docker/setup-buildx-action@v3
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
- name: Log in to Container Registry
|
||||||
registry: ${{ env.REGISTRY }}
|
uses: docker/login-action@v3
|
||||||
username: ${{ github.actor }}
|
with:
|
||||||
password: ${{ secrets.GITEA_TOKEN }}
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ vars.WORKER_USERNAME}}
|
||||||
- name: Build and push release images
|
password: ${{ secrets.CI_BOT_TOKEN }}
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
- name: Build and push release images
|
||||||
context: .
|
uses: docker/build-push-action@v5
|
||||||
platforms: linux/amd64,linux/arm64
|
with:
|
||||||
push: true
|
context: .
|
||||||
tags: |
|
platforms: linux/amd64,linux/arm64
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}
|
push: true
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
tags: |
|
||||||
labels: |
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}
|
||||||
org.opencontainers.image.title=Northern Thailand Ping River Monitor
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
org.opencontainers.image.description=Real-time water level monitoring for Ping River Basin
|
labels: |
|
||||||
org.opencontainers.image.version=${{ needs.create-release.outputs.version }}
|
org.opencontainers.image.title=Northern Thailand Ping River Monitor
|
||||||
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
org.opencontainers.image.description=Real-time water level monitoring for Ping River Basin
|
||||||
org.opencontainers.image.revision=${{ github.sha }}
|
org.opencontainers.image.version=${{ needs.create-release.outputs.version }}
|
||||||
cache-from: type=gha
|
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
||||||
cache-to: type=gha,mode=max
|
org.opencontainers.image.revision=${{ github.sha }}
|
||||||
env:
|
cache-from: type=gha
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
cache-to: type=gha,mode=max
|
||||||
|
env:
|
||||||
|
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
|
|
||||||
|
|
||||||
- name: Run Trivy vulnerability scanner
|
|
||||||
uses: aquasecurity/trivy-action@master
|
|
||||||
with:
|
|
||||||
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}
|
|
||||||
format: 'sarif'
|
|
||||||
output: 'trivy-results.sarif'
|
|
||||||
github-token: ${{ secrets.GH_TOKEN }}
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
|
||||||
|
|
||||||
- name: Upload Trivy scan results
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: security-scan-results
|
|
||||||
path: trivy-results.sarif
|
|
||||||
|
|
||||||
# Deploy release to production
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN}}
|
||||||
|
|
||||||
|
# Test release deployment locally
|
||||||
deploy-release:
|
deploy-release:
|
||||||
name: Deploy Release
|
name: Test Release Deployment
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [create-release, build-release, security-scan]
|
needs: [create-release, build-release, security-scan]
|
||||||
environment:
|
environment:
|
||||||
name: production
|
name: testing
|
||||||
url: https://ping-river-monitor.b4l.co.th
|
url: http://localhost:8080
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Deploy to production
|
|
||||||
run: |
|
|
||||||
echo "🚀 Deploying ${{ needs.create-release.outputs.version }} to production..."
|
|
||||||
|
|
||||||
# Example deployment commands (customize for your infrastructure)
|
|
||||||
# kubectl set image deployment/ping-river-monitor app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}
|
|
||||||
# docker-compose pull && docker-compose up -d
|
|
||||||
# Or webhook call to your deployment system
|
|
||||||
|
|
||||||
echo "✅ Deployment initiated"
|
|
||||||
|
|
||||||
- name: Health check after deployment
|
|
||||||
run: |
|
|
||||||
echo "⏳ Waiting for deployment to stabilize..."
|
|
||||||
sleep 60
|
|
||||||
|
|
||||||
echo "🔍 Running health checks..."
|
|
||||||
curl -f https://ping-river-monitor.b4l.co.th/health
|
|
||||||
curl -f https://ping-river-monitor.b4l.co.th/stations
|
|
||||||
|
|
||||||
echo "✅ Health checks passed!"
|
|
||||||
|
|
||||||
- name: Update deployment status
|
|
||||||
run: |
|
|
||||||
echo "📊 Deployment Summary:"
|
|
||||||
echo "Version: ${{ needs.create-release.outputs.version }}"
|
|
||||||
echo "Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}"
|
|
||||||
echo "URL: https://ping-river-monitor.b4l.co.th"
|
|
||||||
echo "Grafana: https://grafana.ping-river-monitor.b4l.co.th"
|
|
||||||
echo "API Docs: https://ping-river-monitor.b4l.co.th/docs"
|
|
||||||
|
|
||||||
# Post-release validation
|
|
||||||
validate-release:
|
|
||||||
name: Validate Release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: deploy-release
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Comprehensive API test
|
- name: Checkout code
|
||||||
run: |
|
uses: actions/checkout@v4
|
||||||
echo "🧪 Running comprehensive API tests..."
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
# Test all major endpoints
|
|
||||||
curl -f https://ping-river-monitor.b4l.co.th/health
|
- name: Log in to Container Registry
|
||||||
curl -f https://ping-river-monitor.b4l.co.th/metrics
|
uses: docker/login-action@v3
|
||||||
curl -f https://ping-river-monitor.b4l.co.th/stations
|
with:
|
||||||
curl -f https://ping-river-monitor.b4l.co.th/measurements/latest?limit=5
|
registry: ${{ env.REGISTRY }}
|
||||||
curl -f https://ping-river-monitor.b4l.co.th/scraping/status
|
username: ${{ vars.WORKER_USERNAME}}
|
||||||
|
password: ${{ secrets.CI_BOT_TOKEN }}
|
||||||
echo "✅ All API endpoints responding correctly"
|
|
||||||
|
- name: Deploy to production (Local Test)
|
||||||
- name: Performance validation
|
run: |
|
||||||
run: |
|
echo "🚀 Testing ${{ needs.create-release.outputs.version }} deployment locally..."
|
||||||
echo "⚡ Running performance validation..."
|
|
||||||
|
# Pull the built image
|
||||||
# Install Apache Bench
|
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}
|
||||||
sudo apt-get update && sudo apt-get install -y apache2-utils
|
|
||||||
|
# Stop any existing containers
|
||||||
# Test response times
|
docker stop ping-river-monitor-test || true
|
||||||
ab -n 10 -c 2 https://ping-river-monitor.b4l.co.th/health
|
docker rm ping-river-monitor-test || true
|
||||||
ab -n 10 -c 2 https://ping-river-monitor.b4l.co.th/stations
|
|
||||||
|
# Start the container for testing
|
||||||
echo "✅ Performance validation completed"
|
docker run -d \
|
||||||
|
--name ping-river-monitor-test \
|
||||||
- name: Data validation
|
-p 8080:8000 \
|
||||||
run: |
|
-e LOG_LEVEL=INFO \
|
||||||
echo "📊 Validating data collection..."
|
-e DB_TYPE=sqlite \
|
||||||
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}
|
||||||
# Check if recent data is available
|
|
||||||
response=$(curl -s https://ping-river-monitor.b4l.co.th/measurements/latest?limit=1)
|
echo "✅ Container started for testing"
|
||||||
echo "Latest measurement: $response"
|
|
||||||
|
- name: Health check after deployment
|
||||||
# Validate data structure (basic check)
|
run: |
|
||||||
if echo "$response" | grep -q "water_level"; then
|
echo "⏳ Waiting for application to start..."
|
||||||
echo "✅ Data structure validation passed"
|
sleep 30
|
||||||
else
|
|
||||||
echo "❌ Data structure validation failed"
|
echo "🔍 Running health checks against local container..."
|
||||||
exit 1
|
|
||||||
fi
|
# 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
|
||||||
|
for i in {1..12}; do
|
||||||
|
if curl -f http://127.0.0.1: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://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:
|
||||||
name: Notify Release
|
name: Notify Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [create-release, validate-release]
|
needs: [create-release, deploy-release]
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Notify success
|
- name: Notify success
|
||||||
if: needs.validate-release.result == 'success'
|
if: needs.deploy-release.result == 'success'
|
||||||
run: |
|
run: |
|
||||||
echo "🎉 Release ${{ needs.create-release.outputs.version }} deployed successfully!"
|
echo "🎉 Release ${{ needs.create-release.outputs.version }} tested successfully!"
|
||||||
echo "🌐 Production URL: https://ping-river-monitor.b4l.co.th"
|
echo "🧪 Local Test: Passed all health checks"
|
||||||
echo "📊 Grafana: https://grafana.ping-river-monitor.b4l.co.th"
|
echo "<EFBFBD> GDocker Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}"
|
||||||
echo "📚 API Docs: https://ping-river-monitor.b4l.co.th/docs"
|
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 }} deployed successfully!"}' \
|
# --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.validate-release.result == 'failure'
|
if: needs.deploy-release.result == 'failure'
|
||||||
run: |
|
run: |
|
||||||
echo "❌ Release ${{ needs.create-release.outputs.version }} deployment failed!"
|
echo "❌ Release ${{ needs.create-release.outputs.version }} testing failed!"
|
||||||
echo "Please check the logs and take corrective action."
|
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 }} deployment failed!"}' \
|
# --data '{"text":"❌ Northern Thailand Ping River Monitor ${{ needs.create-release.outputs.version }} testing failed!"}' \
|
||||||
# ${{ secrets.SLACK_WEBHOOK_URL }}
|
# ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||||
|
@@ -25,6 +25,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@@ -61,16 +63,16 @@ jobs:
|
|||||||
|
|
||||||
- name: Check for critical vulnerabilities
|
- name: Check for critical vulnerabilities
|
||||||
run: |
|
run: |
|
||||||
echo "🔍 Checking for critical vulnerabilities..."
|
echo "Checking for critical vulnerabilities..."
|
||||||
|
|
||||||
# Check Safety results
|
# Check Safety results
|
||||||
if [ -f safety-report.json ]; then
|
if [ -f safety-report.json ]; then
|
||||||
critical_count=$(jq '.vulnerabilities | length' safety-report.json 2>/dev/null || echo "0")
|
critical_count=$(jq '.vulnerabilities | length' safety-report.json 2>/dev/null || echo "0")
|
||||||
if [ "$critical_count" -gt 0 ]; then
|
if [ "$critical_count" -gt 0 ]; then
|
||||||
echo "⚠️ Found $critical_count dependency vulnerabilities"
|
echo "Found $critical_count dependency vulnerabilities"
|
||||||
jq '.vulnerabilities[] | "- \(.package_name) \(.installed_version): \(.vulnerability_id)"' safety-report.json
|
jq '.vulnerabilities[] | "- \(.package_name) \(.installed_version): \(.vulnerability_id)"' safety-report.json
|
||||||
else
|
else
|
||||||
echo "✅ No dependency vulnerabilities found"
|
echo "No dependency vulnerabilities found"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -78,86 +80,9 @@ jobs:
|
|||||||
if [ -f bandit-report.json ]; then
|
if [ -f bandit-report.json ]; then
|
||||||
high_severity=$(jq '.results[] | select(.issue_severity == "HIGH") | length' bandit-report.json 2>/dev/null | wc -l)
|
high_severity=$(jq '.results[] | select(.issue_severity == "HIGH") | length' bandit-report.json 2>/dev/null | wc -l)
|
||||||
if [ "$high_severity" -gt 0 ]; then
|
if [ "$high_severity" -gt 0 ]; then
|
||||||
echo "⚠️ Found $high_severity high-severity security issues"
|
echo "Found $high_severity high-severity security issues"
|
||||||
else
|
else
|
||||||
echo "✅ No high-severity security issues found"
|
echo "No high-severity security issues found"
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Docker image security scan
|
|
||||||
docker-security-scan:
|
|
||||||
name: Docker Security Scan
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Check GitHub token availability
|
|
||||||
run: |
|
|
||||||
if [ -z "${{ secrets.GH_TOKEN }}" ]; then
|
|
||||||
echo "⚠️ GH_TOKEN not configured. Trivy scans may fail due to rate limits."
|
|
||||||
echo "💡 To fix: Add GH_TOKEN secret in repository settings"
|
|
||||||
else
|
|
||||||
echo "✅ GH_TOKEN is configured"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Build Docker image for scanning
|
|
||||||
run: |
|
|
||||||
docker build -t ping-river-monitor:scan .
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
|
||||||
|
|
||||||
- name: Run Trivy vulnerability scanner
|
|
||||||
uses: aquasecurity/trivy-action@master
|
|
||||||
with:
|
|
||||||
image-ref: "ping-river-monitor:scan"
|
|
||||||
format: "json"
|
|
||||||
output: "trivy-report.json"
|
|
||||||
github-token: ${{ secrets.GH_TOKEN }}
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Run Trivy filesystem scan
|
|
||||||
uses: aquasecurity/trivy-action@master
|
|
||||||
with:
|
|
||||||
scan-type: "fs"
|
|
||||||
scan-ref: "."
|
|
||||||
format: "json"
|
|
||||||
output: "trivy-fs-report.json"
|
|
||||||
github-token: ${{ secrets.GH_TOKEN }}
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Upload Trivy reports
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: always()
|
|
||||||
with:
|
|
||||||
name: trivy-reports-${{ github.run_number }}
|
|
||||||
path: |
|
|
||||||
trivy-report.json
|
|
||||||
trivy-fs-report.json
|
|
||||||
|
|
||||||
- name: Check Trivy results
|
|
||||||
run: |
|
|
||||||
echo "🔍 Analyzing Docker security scan results..."
|
|
||||||
|
|
||||||
if [ -f trivy-report.json ]; then
|
|
||||||
critical_vulns=$(jq '.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL") | length' trivy-report.json 2>/dev/null | wc -l)
|
|
||||||
high_vulns=$(jq '.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH") | length' trivy-report.json 2>/dev/null | wc -l)
|
|
||||||
|
|
||||||
echo "Critical vulnerabilities: $critical_vulns"
|
|
||||||
echo "High vulnerabilities: $high_vulns"
|
|
||||||
|
|
||||||
if [ "$critical_vulns" -gt 0 ]; then
|
|
||||||
echo "❌ Critical vulnerabilities found in Docker image!"
|
|
||||||
exit 1
|
|
||||||
elif [ "$high_vulns" -gt 5 ]; then
|
|
||||||
echo "⚠️ Many high-severity vulnerabilities found"
|
|
||||||
else
|
|
||||||
echo "✅ Docker image security scan passed"
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -169,6 +94,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@@ -183,7 +110,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Check licenses
|
- name: Check licenses
|
||||||
run: |
|
run: |
|
||||||
echo "📄 Checking dependency licenses..."
|
echo "Checking dependency licenses..."
|
||||||
pip-licenses --format=json --output-file=licenses.json
|
pip-licenses --format=json --output-file=licenses.json
|
||||||
pip-licenses --format=markdown --output-file=licenses.md
|
pip-licenses --format=markdown --output-file=licenses.md
|
||||||
|
|
||||||
@@ -192,11 +119,11 @@ jobs:
|
|||||||
|
|
||||||
for license in "${problematic_licenses[@]}"; do
|
for license in "${problematic_licenses[@]}"; do
|
||||||
if grep -i "$license" licenses.json; then
|
if grep -i "$license" licenses.json; then
|
||||||
echo "⚠️ Found potentially problematic license: $license"
|
echo "Found potentially problematic license: $license"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "✅ License check completed"
|
echo "License check completed"
|
||||||
|
|
||||||
- name: Upload license report
|
- name: Upload license report
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
@@ -214,6 +141,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@@ -227,56 +156,15 @@ jobs:
|
|||||||
|
|
||||||
- name: Check for outdated packages
|
- name: Check for outdated packages
|
||||||
run: |
|
run: |
|
||||||
echo "📦 Checking for outdated packages..."
|
echo "Checking for outdated packages..."
|
||||||
pip install --root-user-action=ignore -r requirements.txt
|
pip install --root-user-action=ignore -r requirements.txt
|
||||||
pip list --outdated --format=json > outdated-packages.json || true
|
pip list --outdated --format=json > outdated-packages.json || true
|
||||||
|
|
||||||
if [ -s outdated-packages.json ]; then
|
if [ -s outdated-packages.json ]; then
|
||||||
echo "📋 Outdated packages found:"
|
echo "Outdated packages found:"
|
||||||
cat outdated-packages.json | jq -r '.[] | "- \(.name): \(.version) -> \(.latest_version)"'
|
cat outdated-packages.json | jq -r '.[] | "- \(.name): \(.version) -> \(.latest_version)"'
|
||||||
else
|
else
|
||||||
echo "✅ All packages are up to date"
|
echo "All packages are up to date"
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Create dependency update issue
|
|
||||||
if: github.event_name == 'schedule'
|
|
||||||
run: |
|
|
||||||
if [ -s outdated-packages.json ] && [ "$(cat outdated-packages.json)" != "[]" ]; then
|
|
||||||
echo "📝 Creating dependency update issue..."
|
|
||||||
|
|
||||||
# Create issue body
|
|
||||||
cat > issue-body.md << 'EOF'
|
|
||||||
## 📦 Dependency Updates Available
|
|
||||||
|
|
||||||
The following packages have updates available:
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat outdated-packages.json | jq -r '.[] | "- **\(.name)**: \(.version) → \(.latest_version)"' >> issue-body.md
|
|
||||||
|
|
||||||
cat >> issue-body.md << 'EOF'
|
|
||||||
|
|
||||||
## 🔍 Security Impact
|
|
||||||
|
|
||||||
Please review each update for:
|
|
||||||
- Security fixes
|
|
||||||
- Breaking changes
|
|
||||||
- Compatibility issues
|
|
||||||
|
|
||||||
## ✅ Action Items
|
|
||||||
|
|
||||||
- [ ] Review changelog for each package
|
|
||||||
- [ ] Test updates in development environment
|
|
||||||
- [ ] Update requirements.txt
|
|
||||||
- [ ] Run full test suite
|
|
||||||
- [ ] Deploy to staging for validation
|
|
||||||
|
|
||||||
---
|
|
||||||
*This issue was automatically created by the security workflow.*
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "Issue body created. In a real implementation, you would create a Gitea issue here."
|
|
||||||
cat issue-body.md
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Upload dependency reports
|
- name: Upload dependency reports
|
||||||
@@ -285,7 +173,6 @@ jobs:
|
|||||||
name: dependency-reports-${{ github.run_number }}
|
name: dependency-reports-${{ github.run_number }}
|
||||||
path: |
|
path: |
|
||||||
outdated-packages.json
|
outdated-packages.json
|
||||||
issue-body.md
|
|
||||||
|
|
||||||
# Code quality metrics
|
# Code quality metrics
|
||||||
code-quality:
|
code-quality:
|
||||||
@@ -295,6 +182,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@@ -309,24 +198,24 @@ jobs:
|
|||||||
|
|
||||||
- name: Calculate code complexity
|
- name: Calculate code complexity
|
||||||
run: |
|
run: |
|
||||||
echo "📊 Calculating code complexity..."
|
echo "Calculating code complexity..."
|
||||||
radon cc src/ --json > complexity-report.json
|
radon cc src/ --json > complexity-report.json
|
||||||
radon mi src/ --json > maintainability-report.json
|
radon mi src/ --json > maintainability-report.json
|
||||||
|
|
||||||
echo "🔍 Complexity Summary:"
|
echo "Complexity Summary:"
|
||||||
radon cc src/ --average
|
radon cc src/ --average
|
||||||
|
|
||||||
echo "🔧 Maintainability Summary:"
|
echo "Maintainability Summary:"
|
||||||
radon mi src/
|
radon mi src/
|
||||||
|
|
||||||
- name: Find dead code
|
- name: Find dead code
|
||||||
run: |
|
run: |
|
||||||
echo "🧹 Checking for dead code..."
|
echo "Checking for dead code..."
|
||||||
vulture src/ --json > dead-code-report.json || true
|
vulture src/ --json > dead-code-report.json || true
|
||||||
|
|
||||||
- name: Check for code smells
|
- name: Check for code smells
|
||||||
run: |
|
run: |
|
||||||
echo "👃 Checking for code smells..."
|
echo "Checking for code smells..."
|
||||||
xenon --max-absolute B --max-modules A --max-average A src/ || true
|
xenon --max-absolute B --max-modules A --max-average A src/ || true
|
||||||
|
|
||||||
- name: Upload quality reports
|
- name: Upload quality reports
|
||||||
@@ -342,7 +231,7 @@ jobs:
|
|||||||
security-summary:
|
security-summary:
|
||||||
name: Security Summary
|
name: Security Summary
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [dependency-scan, docker-security-scan, license-check, code-quality]
|
needs: [dependency-scan, license-check, code-quality]
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -351,51 +240,47 @@ jobs:
|
|||||||
|
|
||||||
- name: Generate security summary
|
- name: Generate security summary
|
||||||
run: |
|
run: |
|
||||||
echo "# 🔒 Security Scan Summary" > security-summary.md
|
echo "# Security Scan Summary" > security-summary.md
|
||||||
echo "" >> security-summary.md
|
echo "" >> security-summary.md
|
||||||
echo "**Scan Date:** $(date -u)" >> security-summary.md
|
echo "**Scan Date:** $(date -u)" >> security-summary.md
|
||||||
echo "**Repository:** ${{ github.repository }}" >> security-summary.md
|
echo "**Repository:** ${{ github.repository }}" >> security-summary.md
|
||||||
echo "**Commit:** ${{ github.sha }}" >> security-summary.md
|
echo "**Commit:** ${{ github.sha }}" >> security-summary.md
|
||||||
echo "" >> security-summary.md
|
echo "" >> security-summary.md
|
||||||
|
|
||||||
echo "## 📊 Results" >> security-summary.md
|
echo "## Results" >> security-summary.md
|
||||||
echo "" >> security-summary.md
|
echo "" >> security-summary.md
|
||||||
|
|
||||||
# Dependency scan results
|
# Dependency scan results
|
||||||
if [ -f security-reports-*/safety-report.json ]; then
|
if [ -f security-reports-*/safety-report.json ]; then
|
||||||
vuln_count=$(jq '.vulnerabilities | length' security-reports-*/safety-report.json 2>/dev/null || echo "0")
|
vuln_count=$(jq '.vulnerabilities | length' security-reports-*/safety-report.json 2>/dev/null || echo "0")
|
||||||
if [ "$vuln_count" -eq 0 ]; then
|
if [ "$vuln_count" -eq 0 ]; then
|
||||||
echo "- ✅ **Dependency Scan**: No vulnerabilities found" >> security-summary.md
|
echo "- Dependency Scan: No vulnerabilities found" >> security-summary.md
|
||||||
else
|
else
|
||||||
echo "- ⚠️ **Dependency Scan**: $vuln_count vulnerabilities found" >> security-summary.md
|
echo "- Dependency Scan: $vuln_count vulnerabilities found" >> security-summary.md
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "- ❓ **Dependency Scan**: Results not available" >> security-summary.md
|
echo "- Dependency Scan: Results not available" >> security-summary.md
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Docker scan results
|
# Docker scan results (removed Trivy)
|
||||||
if [ -f trivy-reports-*/trivy-report.json ]; then
|
echo "- Docker Scan: Skipped (Trivy removed)" >> security-summary.md
|
||||||
echo "- ✅ **Docker Scan**: Completed" >> security-summary.md
|
|
||||||
else
|
|
||||||
echo "- ❓ **Docker Scan**: Results not available" >> security-summary.md
|
|
||||||
fi
|
|
||||||
|
|
||||||
# License check results
|
# License check results
|
||||||
if [ -f license-report-*/licenses.json ]; then
|
if [ -f license-report-*/licenses.json ]; then
|
||||||
echo "- ✅ **License Check**: Completed" >> security-summary.md
|
echo "- License Check: Completed" >> security-summary.md
|
||||||
else
|
else
|
||||||
echo "- ❓ **License Check**: Results not available" >> security-summary.md
|
echo "- License Check: Results not available" >> security-summary.md
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Code quality results
|
# Code quality results
|
||||||
if [ -f code-quality-reports-*/complexity-report.json ]; then
|
if [ -f code-quality-reports-*/complexity-report.json ]; then
|
||||||
echo "- ✅ **Code Quality**: Analyzed" >> security-summary.md
|
echo "- Code Quality: Analyzed" >> security-summary.md
|
||||||
else
|
else
|
||||||
echo "- ❓ **Code Quality**: Results not available" >> security-summary.md
|
echo "- Code Quality: Results not available" >> security-summary.md
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "" >> security-summary.md
|
echo "" >> security-summary.md
|
||||||
echo "## 🔗 Detailed Reports" >> security-summary.md
|
echo "## Detailed Reports" >> security-summary.md
|
||||||
echo "" >> security-summary.md
|
echo "" >> security-summary.md
|
||||||
echo "Detailed reports are available in the workflow artifacts." >> security-summary.md
|
echo "Detailed reports are available in the workflow artifacts." >> security-summary.md
|
||||||
|
|
||||||
@@ -405,4 +290,4 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: security-summary-${{ github.run_number }}
|
name: security-summary-${{ github.run_number }}
|
||||||
path: security-summary.md
|
path: security-summary.md
|
@@ -259,7 +259,7 @@ make health-check
|
|||||||
|
|
||||||
**Deployment Date**: ___________
|
**Deployment Date**: ___________
|
||||||
**Deployed By**: ___________
|
**Deployed By**: ___________
|
||||||
**Version**: v3.1.1
|
**Version**: v3.1.3
|
||||||
**Environment**: ___________
|
**Environment**: ___________
|
||||||
|
|
||||||
**Sign-off**:
|
**Sign-off**:
|
||||||
|
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
|
||||||
|
@@ -222,12 +222,12 @@ Your repository is now equipped with:
|
|||||||
2. **Configure deployment environments** (staging/production)
|
2. **Configure deployment environments** (staging/production)
|
||||||
3. **Set up monitoring dashboards** for workflow metrics
|
3. **Set up monitoring dashboards** for workflow metrics
|
||||||
4. **Configure notifications** for team collaboration
|
4. **Configure notifications** for team collaboration
|
||||||
5. **Create your first release** with `git tag v3.1.1`
|
5. **Create your first release** with `git tag v3.1.3`
|
||||||
|
|
||||||
Your **Northern Thailand Ping River Monitor** is now ready for professional development and deployment! 🎊
|
Your **Northern Thailand Ping River Monitor** is now ready for professional development and deployment! 🎊
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Workflow Version**: v3.1.1
|
**Workflow Version**: v3.1.3
|
||||||
**Setup Date**: 2025-08-12
|
**Setup Date**: 2025-08-12
|
||||||
**Repository**: https://git.b4l.co.th/grabowski/Northern-Thailand-Ping-River-Monitor
|
**Repository**: https://git.b4l.co.th/grabowski/Northern-Thailand-Ping-River-Monitor
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
A comprehensive real-time water level monitoring system for the Ping River Basin in Northern Thailand, covering Royal Irrigation Department (RID) stations from Chiang Dao to Nakhon Sawan with advanced data collection, storage, and visualization capabilities.
|
A comprehensive real-time water level monitoring system for the Ping River Basin in Northern Thailand, covering Royal Irrigation Department (RID) stations from Chiang Dao to Nakhon Sawan with advanced data collection, storage, and visualization capabilities.
|
||||||
|
|
||||||
[](https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor/actions) [](https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor/actions) [](https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor/actions) [](https://python.org) [](https://fastapi.tiangolo.com) [](https://docker.com) [](LICENSE) [](https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor/releases)
|
[](https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor/actions) [](https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor/actions) [](https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor/actions) [](https://python.org) [](https://fastapi.tiangolo.com) [](https://docker.com) [](LICENSE) [](https://git.b4l.co.th/B4L/Northern-Thailand-Ping-River-Monitor/releases)
|
||||||
|
|
||||||
## 🌟 Features
|
## 🌟 Features
|
||||||
|
|
||||||
|
@@ -297,6 +297,6 @@ make validate-workflows
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Workflow Version**: v3.1.1
|
**Workflow Version**: v3.1.3
|
||||||
**Last Updated**: 2025-08-12
|
**Last Updated**: 2025-08-12
|
||||||
**Maintained By**: Ping River Monitor Team
|
**Maintained By**: Ping River Monitor Team
|
@@ -29,7 +29,7 @@ def main():
|
|||||||
"FastAPI": generate_badge_url("FastAPI", "0.104%2B", "green"),
|
"FastAPI": generate_badge_url("FastAPI", "0.104%2B", "green"),
|
||||||
"Docker": generate_badge_url("Docker", "Ready", "blue"),
|
"Docker": generate_badge_url("Docker", "Ready", "blue"),
|
||||||
"License": generate_badge_url("License", "MIT", "green"),
|
"License": generate_badge_url("License", "MIT", "green"),
|
||||||
"Version": generate_badge_url("Version", "v3.1.1", "blue"),
|
"Version": generate_badge_url("Version", "v3.1.3", "blue"),
|
||||||
}
|
}
|
||||||
|
|
||||||
print("# Status Badges")
|
print("# Status Badges")
|
||||||
|
@@ -13,7 +13,7 @@ REM Add all files
|
|||||||
git add .
|
git add .
|
||||||
|
|
||||||
REM Initial commit
|
REM Initial commit
|
||||||
git commit -m "Initial commit: Northern Thailand Ping River Monitor v3.1.1
|
git commit -m "Initial commit: Northern Thailand Ping River Monitor v3.1.3
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Real-time water level monitoring for Ping River Basin
|
- Real-time water level monitoring for Ping River Basin
|
||||||
|
@@ -66,7 +66,7 @@ fi
|
|||||||
git add .
|
git add .
|
||||||
|
|
||||||
# Initial commit
|
# Initial commit
|
||||||
git commit -m "Initial commit: Northern Thailand Ping River Monitor v3.1.1
|
git commit -m "Initial commit: Northern Thailand Ping River Monitor v3.1.3
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Real-time water level monitoring for Ping River Basin
|
- Real-time water level monitoring for Ping River Basin
|
||||||
|
16
setup.py
16
setup.py
@@ -11,8 +11,18 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|||||||
long_description = fh.read()
|
long_description = fh.read()
|
||||||
|
|
||||||
# Read requirements
|
# Read requirements
|
||||||
with open("requirements.txt", "r", encoding="utf-8") as fh:
|
try:
|
||||||
requirements = [line.strip() for line in fh if line.strip() and not line.startswith("#")]
|
with open("requirements.txt", "r", encoding="utf-8") as fh:
|
||||||
|
requirements = [line.strip() for line in fh if line.strip() and not line.startswith("#")]
|
||||||
|
except FileNotFoundError:
|
||||||
|
# Fallback to minimal requirements if file not found
|
||||||
|
requirements = [
|
||||||
|
"requests>=2.31.0",
|
||||||
|
"schedule>=1.2.0",
|
||||||
|
"pandas>=2.1.0",
|
||||||
|
"fastapi>=0.104.0",
|
||||||
|
"uvicorn>=0.24.0",
|
||||||
|
]
|
||||||
|
|
||||||
# Extract core requirements (exclude dev dependencies)
|
# Extract core requirements (exclude dev dependencies)
|
||||||
core_requirements = []
|
core_requirements = []
|
||||||
@@ -22,7 +32,7 @@ for req in requirements:
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="northern-thailand-ping-river-monitor",
|
name="northern-thailand-ping-river-monitor",
|
||||||
version="3.1.1",
|
version="3.1.3",
|
||||||
author="Ping River Monitor Team",
|
author="Ping River Monitor Team",
|
||||||
author_email="contact@example.com",
|
author_email="contact@example.com",
|
||||||
description="Real-time water level monitoring system for the Ping River Basin in Northern Thailand",
|
description="Real-time water level monitoring system for the Ping River Basin in Northern Thailand",
|
||||||
|
@@ -6,7 +6,7 @@ A comprehensive real-time water level monitoring system for the Ping River Basin
|
|||||||
in Northern Thailand, covering Royal Irrigation Department (RID) stations.
|
in Northern Thailand, covering Royal Irrigation Department (RID) stations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = "3.1.1"
|
__version__ = "3.1.3"
|
||||||
__author__ = "Ping River Monitor Team"
|
__author__ = "Ping River Monitor Team"
|
||||||
__description__ = "Northern Thailand Ping River Monitoring System"
|
__description__ = "Northern Thailand Ping River Monitoring System"
|
||||||
|
|
||||||
|
@@ -297,7 +297,7 @@ Examples:
|
|||||||
)
|
)
|
||||||
|
|
||||||
logger.info("🏔️ Northern Thailand Ping River Monitor starting...")
|
logger.info("🏔️ Northern Thailand Ping River Monitor starting...")
|
||||||
logger.info(f"Version: 3.1.1")
|
logger.info(f"Version: 3.1.3")
|
||||||
logger.info(f"Log level: {args.log_level}")
|
logger.info(f"Log level: {args.log_level}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@@ -143,7 +143,7 @@ async def lifespan(app: FastAPI):
|
|||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="Northern Thailand Ping River Monitor API",
|
title="Northern Thailand Ping River Monitor API",
|
||||||
description="Real-time water level monitoring system for Northern Thailand's Ping River Basin stations",
|
description="Real-time water level monitoring system for Northern Thailand's Ping River Basin stations",
|
||||||
version="3.1.1",
|
version="3.1.3",
|
||||||
lifespan=lifespan
|
lifespan=lifespan
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -165,7 +165,7 @@ def test_logging():
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Run all tests"""
|
"""Run all tests"""
|
||||||
print("🧪 Running integration tests for Northern Thailand Ping River Monitor v3.1.1")
|
print("🧪 Running integration tests for Northern Thailand Ping River Monitor v3.1.3")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
|
Reference in New Issue
Block a user