name: Release - Northern Thailand Ping River Monitor on: push: tags: - 'v*.*.*' workflow_dispatch: inputs: version: description: 'Release version (e.g., v3.1.3)' required: true type: string env: PYTHON_VERSION: '3.11' REGISTRY: git.b4l.co.th IMAGE_NAME: b4l/northern-thailand-ping-river-monitor # GitHub token for better rate limits and authentication GH_TOKEN: ${{ secrets.GH_TOKEN }} jobs: # Create release create-release: name: Create Release runs-on: ubuntu-latest outputs: version: ${{ steps.version.outputs.version }} steps: - name: Checkout code uses: actions/checkout@v4 with: token: ${{ secrets.GITEA_TOKEN }} fetch-depth: 0 - name: Get version id: version run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT else echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT fi - name: Generate changelog id: changelog run: | # Generate changelog from git commits echo "## Changes" > 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 "## Docker Images" >> 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 env: GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }} with: tag_name: ${{ steps.version.outputs.version }} release_name: Northern Thailand Ping River Monitor ${{ steps.version.outputs.version }} body_path: CHANGELOG.md draft: false prerelease: false # Build and test for release test-release: name: Test Release Build runs-on: ubuntu-latest needs: create-release strategy: matrix: python-version: ['3.9', '3.10', '3.11', '3.12'] steps: - name: Checkout code uses: actions/checkout@v4 with: token: ${{ secrets.GITEA_TOKEN }} - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | 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-dev.txt - name: Run full test suite run: | python tests/test_integration.py python tests/test_station_management.py python run.py --test - name: Build Python package run: | pip install --root-user-action=ignore build python -m build - name: Upload Python package uses: actions/upload-artifact@v3 with: name: python-package-${{ matrix.python-version }} path: dist/ # Build release Docker images build-release: name: Build Release Images runs-on: ubuntu-latest needs: [create-release, test-release] steps: - name: Checkout code uses: actions/checkout@v4 with: token: ${{ secrets.GITEA_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - 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: Build and push release images uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64,linux/arm64 push: true tags: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest labels: | 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.version=${{ needs.create-release.outputs.version }} org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max env: GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }} # Security scan for release security-scan: name: Security Scan runs-on: ubuntu-latest needs: build-release steps: - name: Checkout code uses: actions/checkout@v4 with: token: ${{ secrets.GITEA_TOKEN}} # Test release deployment locally deploy-release: name: Test Release Deployment runs-on: ubuntu-latest needs: [create-release, build-release, security-scan] environment: name: testing url: http://localhost:8080 steps: - name: Checkout code uses: actions/checkout@v4 with: token: ${{ secrets.GITEA_TOKEN }} - 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..." # 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" # Notify stakeholders notify: name: Notify Release runs-on: ubuntu-latest needs: [create-release, deploy-release] if: always() steps: - name: Notify success if: needs.deploy-release.result == 'success' run: | echo "๐ŸŽ‰ Release ${{ needs.create-release.outputs.version }} tested successfully!" echo "๐Ÿงช Local Test: Passed all health checks" echo "๏ฟฝ GDocker Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.create-release.outputs.version }}" echo "โœ… Ready for production deployment" # Add notification to Slack, Discord, email, etc. # 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!"}' \ # ${{ secrets.SLACK_WEBHOOK_URL }} - name: Notify failure if: needs.deploy-release.result == 'failure' run: | echo "โŒ Release ${{ needs.create-release.outputs.version }} testing failed!" echo "Please check the logs and fix issues before production deployment." # Add failure notification # curl -X POST -H 'Content-type: application/json' \ # --data '{"text":"โŒ Northern Thailand Ping River Monitor ${{ needs.create-release.outputs.version }} testing failed!"}' \ # ${{ secrets.SLACK_WEBHOOK_URL }}