From 737bde9b941caf817d4b07f0988f485117a3253a Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Thu, 5 Feb 2026 13:35:08 -0600 Subject: [PATCH] Add Gitea Actions CI/CD workflow for Docker builds - Semantic version validation on PRs - Automated Docker image builds and pushes to Docker Hub - Discord notifications for build status - Multi-tag strategy: latest, semver, commit hash Co-Authored-By: Claude Sonnet 4.5 --- .gitea/workflows/build.yml | 375 +++++++++++++++++++++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 .gitea/workflows/build.yml diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..95e9863 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,375 @@ +# Gitea Actions: Docker Build, Push, and Notify +# +# This workflow provides a complete CI/CD pipeline for Paper Dynasty Database: +# - Validates semantic versioning on PRs +# - Builds Docker images on every push/PR +# - Pushes to Docker Hub on main branch merges +# - Sends Discord notifications on success/failure +# +# Created: 2026-02-05 +# For: Paper Dynasty Database API + +name: Build Docker Image + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + steps: + # ============================================== + # 1. CHECKOUT CODE + # ============================================== + - name: Checkout code + uses: actions/checkout@v4 + + # ============================================== + # 2. SEMANTIC VERSION VALIDATION (PRs only) + # ============================================== + # Enforces proper semantic versioning: + # - Blocks PRs that don't bump VERSION file + # - Validates version changes follow semver rules + # - Prevents skipping versions or going backwards + # + # Valid bumps: + # - Patch: 1.2.3 → 1.2.4 (bug fixes) + # - Minor: 1.2.3 → 1.3.0 (new features) + # - Major: 1.2.3 → 2.0.0 (breaking changes) + # + # Invalid bumps: + # - 1.2.3 → 1.4.0 (skipped minor version) + # - 1.2.3 → 1.2.0 (went backwards) + # - 1.2.3 → 1.3.1 (didn't reset patch) + # + - name: Check VERSION was bumped (semantic versioning) + if: github.event_name == 'pull_request' + run: | + # Get VERSION from this PR branch + PR_VERSION=$(cat VERSION 2>/dev/null || echo "0.0.0") + + # Get VERSION from main branch + git fetch origin main:main + MAIN_VERSION=$(git show main:VERSION 2>/dev/null || echo "0.0.0") + + echo "📋 Semantic Version Check" + echo "Main branch version: $MAIN_VERSION" + echo "PR branch version: $PR_VERSION" + echo "" + + # Parse versions into components + IFS='.' read -r MAIN_MAJOR MAIN_MINOR MAIN_PATCH <<< "$MAIN_VERSION" + IFS='.' read -r PR_MAJOR PR_MINOR PR_PATCH <<< "$PR_VERSION" + + # Remove any non-numeric characters + MAIN_MAJOR=${MAIN_MAJOR//[!0-9]/} + MAIN_MINOR=${MAIN_MINOR//[!0-9]/} + MAIN_PATCH=${MAIN_PATCH//[!0-9]/} + PR_MAJOR=${PR_MAJOR//[!0-9]/} + PR_MINOR=${PR_MINOR//[!0-9]/} + PR_PATCH=${PR_PATCH//[!0-9]/} + + # Check if VERSION unchanged + if [ "$PR_VERSION" = "$MAIN_VERSION" ]; then + echo "❌ ERROR: VERSION file has not been updated!" + echo "" + echo "Please update the VERSION file in your PR." + echo "Current version: $MAIN_VERSION" + exit 1 + fi + + # Validate semantic version bump + VALID=false + BUMP_TYPE="" + + # Check for major version bump (X.0.0) + if [ "$PR_MAJOR" -eq $((MAIN_MAJOR + 1)) ] && [ "$PR_MINOR" -eq 0 ] && [ "$PR_PATCH" -eq 0 ]; then + VALID=true + BUMP_TYPE="major" + # Check for minor version bump (x.X.0) + elif [ "$PR_MAJOR" -eq "$MAIN_MAJOR" ] && [ "$PR_MINOR" -eq $((MAIN_MINOR + 1)) ] && [ "$PR_PATCH" -eq 0 ]; then + VALID=true + BUMP_TYPE="minor" + # Check for patch version bump (x.x.X) + elif [ "$PR_MAJOR" -eq "$MAIN_MAJOR" ] && [ "$PR_MINOR" -eq "$MAIN_MINOR" ] && [ "$PR_PATCH" -eq $((MAIN_PATCH + 1)) ]; then + VALID=true + BUMP_TYPE="patch" + fi + + if [ "$VALID" = true ]; then + echo "✅ Valid $BUMP_TYPE version bump: $MAIN_VERSION → $PR_VERSION" + else + echo "❌ ERROR: Invalid semantic version change!" + echo "" + echo "Current version: $MAIN_VERSION" + echo "PR version: $PR_VERSION" + echo "" + echo "Valid version bumps:" + echo " - Patch: $MAIN_MAJOR.$MAIN_MINOR.$((MAIN_PATCH + 1))" + echo " - Minor: $MAIN_MAJOR.$((MAIN_MINOR + 1)).0" + echo " - Major: $((MAIN_MAJOR + 1)).0.0" + echo "" + echo "Common issues:" + echo " ❌ Skipping versions (e.g., 2.5.0 → 2.7.0)" + echo " ❌ Going backwards (e.g., 2.5.0 → 2.4.0)" + echo " ❌ Not resetting lower components (e.g., 2.5.0 → 2.6.1)" + exit 1 + fi + + # ============================================== + # 3. DOCKER BUILDX SETUP + # ============================================== + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # ============================================== + # 4. DOCKER HUB LOGIN (main branch only) + # ============================================== + # Requires secrets in Gitea: + # - DOCKERHUB_USERNAME: Your Docker Hub username + # - DOCKERHUB_TOKEN: Docker Hub access token (not password!) + # + # To create token: + # 1. Go to hub.docker.com + # 2. Account Settings → Security → New Access Token + # 3. Copy token to Gitea repo → Settings → Secrets → Actions + # + - name: Login to Docker Hub + if: github.ref == 'refs/heads/main' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + # ============================================== + # 5. EXTRACT METADATA + # ============================================== + # Reads VERSION file and generates image tags: + # - version: From VERSION file (e.g., "1.2.3") + # - sha_short: First 7 chars of commit SHA + # - version_sha: Combined version+commit (e.g., "v1.2.3-a1b2c3d") + # - branch: Current branch name + # - timestamp: ISO 8601 format for Discord + # + - name: Extract metadata + id: meta + run: | + VERSION=$(cat VERSION 2>/dev/null || echo "0.0.0") + SHA_SHORT=$(echo ${{ github.sha }} | cut -c1-7) + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "sha_short=${SHA_SHORT}" >> $GITHUB_OUTPUT + echo "version_sha=v${VERSION}-${SHA_SHORT}" >> $GITHUB_OUTPUT + echo "branch=${{ github.ref_name }}" >> $GITHUB_OUTPUT + echo "timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT + + # ============================================== + # 6. BUILD AND PUSH DOCKER IMAGE + # ============================================== + # Creates 3 tags for each build: + # - latest: Always points to newest build + # - v{VERSION}: Semantic version from VERSION file + # - v{VERSION}-{COMMIT}: Version + commit hash for traceability + # + # Example tags: + # - manticorum67/paper-dynasty-database:latest + # - manticorum67/paper-dynasty-database:v1.5.5 + # - manticorum67/paper-dynasty-database:v1.5.5-a1b2c3d + # + # Push behavior: + # - PRs: Build only (test), don't push + # - Main: Build and push to Docker Hub + # + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: ${{ github.ref == 'refs/heads/main' }} + tags: | + manticorum67/paper-dynasty-database:latest + manticorum67/paper-dynasty-database:v${{ steps.meta.outputs.version }} + manticorum67/paper-dynasty-database:${{ steps.meta.outputs.version_sha }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # ============================================== + # 7. BUILD SUMMARY + # ============================================== + # Creates a formatted summary visible in Actions UI + # Shows: image tags, build details, push status + # + - name: Build Summary + run: | + echo "## 🐳 Docker Build Successful! ✅" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Image Tags:**" >> $GITHUB_STEP_SUMMARY + echo "- \`manticorum67/paper-dynasty-database:latest\`" >> $GITHUB_STEP_SUMMARY + echo "- \`manticorum67/paper-dynasty-database:v${{ steps.meta.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY + echo "- \`manticorum67/paper-dynasty-database:${{ steps.meta.outputs.version_sha }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Build Details:**" >> $GITHUB_STEP_SUMMARY + echo "- Branch: \`${{ steps.meta.outputs.branch }}\`" >> $GITHUB_STEP_SUMMARY + echo "- Commit: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY + echo "- Timestamp: \`${{ steps.meta.outputs.timestamp }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + if [ "${{ github.ref }}" == "refs/heads/main" ]; then + echo "🚀 **Pushed to Docker Hub!**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Pull with: \`docker pull manticorum67/paper-dynasty-database:latest\`" >> $GITHUB_STEP_SUMMARY + else + echo "_PR build - image not pushed to Docker Hub_" >> $GITHUB_STEP_SUMMARY + fi + + # ============================================== + # 8. DISCORD NOTIFICATION - SUCCESS + # ============================================== + # Sends green embed to Discord on successful builds + # + # Only fires on main branch pushes (not PRs) + # + # Setup: + # 1. Create webhook in Discord channel: + # Right-click channel → Edit → Integrations → Webhooks → New + # 2. Copy webhook URL + # 3. Add to Gitea: Settings → Secrets → Actions → DISCORD_WEBHOOK + # + - name: Discord Notification - Success + if: success() && github.ref == 'refs/heads/main' + run: | + curl -H "Content-Type: application/json" \ + -d '{ + "embeds": [{ + "title": "✅ Paper Dynasty Database Build Successful", + "description": "Docker image built and pushed to Docker Hub!", + "color": 3066993, + "fields": [ + { + "name": "Version", + "value": "`v${{ steps.meta.outputs.version }}`", + "inline": true + }, + { + "name": "Image Tag", + "value": "`${{ steps.meta.outputs.version_sha }}`", + "inline": true + }, + { + "name": "Branch", + "value": "`${{ steps.meta.outputs.branch }}`", + "inline": true + }, + { + "name": "Commit", + "value": "`${{ steps.meta.outputs.sha_short }}`", + "inline": true + }, + { + "name": "Author", + "value": "${{ github.actor }}", + "inline": true + }, + { + "name": "Docker Hub", + "value": "[manticorum67/paper-dynasty-database](https://hub.docker.com/r/manticorum67/paper-dynasty-database)", + "inline": false + }, + { + "name": "View Run", + "value": "[Click here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})", + "inline": false + } + ], + "timestamp": "${{ steps.meta.outputs.timestamp }}" + }] + }' \ + ${{ secrets.DISCORD_WEBHOOK }} + + # ============================================== + # 9. DISCORD NOTIFICATION - FAILURE + # ============================================== + # Sends red embed to Discord on build failures + # + # Only fires on main branch pushes (not PRs) + # + - name: Discord Notification - Failure + if: failure() && github.ref == 'refs/heads/main' + run: | + TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) + curl -H "Content-Type: application/json" \ + -d '{ + "embeds": [{ + "title": "❌ Paper Dynasty Database Build Failed", + "description": "Docker build encountered an error.", + "color": 15158332, + "fields": [ + { + "name": "Branch", + "value": "`${{ github.ref_name }}`", + "inline": true + }, + { + "name": "Commit", + "value": "`${{ github.sha }}`", + "inline": true + }, + { + "name": "Author", + "value": "${{ github.actor }}", + "inline": true + }, + { + "name": "View Logs", + "value": "[Click here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})", + "inline": false + } + ], + "timestamp": "'"$TIMESTAMP"'" + }] + }' \ + ${{ secrets.DISCORD_WEBHOOK }} + +# ============================================== +# SETUP CHECKLIST +# ============================================== +# Before this workflow will work: +# +# ✅ Add secrets to Gitea repo (Settings → Secrets → Actions): +# - DOCKERHUB_USERNAME: Your Docker Hub username (manticorum67) +# - DOCKERHUB_TOKEN: Docker Hub access token +# - DISCORD_WEBHOOK: Discord webhook URL for notifications +# +# ✅ Ensure VERSION file exists in repo root (currently at 1.5.5) +# +# ✅ Update branch name if not using "main" +# +# ============================================== +# TROUBLESHOOTING +# ============================================== +# Common issues and solutions: +# +# 1. VERSION validation failing unexpectedly +# - Ensure VERSION file exists in repo root +# - Check file contains only version number (no 'v' prefix or extra text) +# - Verify version follows semver: MAJOR.MINOR.PATCH +# +# 2. Docker Hub push failing +# - Verify DOCKERHUB_USERNAME and DOCKERHUB_TOKEN secrets are set +# - Check Docker Hub token has push permissions +# - Ensure repository name matches your Docker Hub repo exactly +# +# 3. Discord notifications not appearing +# - Verify DISCORD_WEBHOOK secret is set in Gitea +# - Test webhook URL manually with curl +# - Check webhook still exists in Discord channel settings +# +# 4. Build cache not working +# - GitHub Actions cache is stored per repository +# - Cache is shared across branches +# - May need to clear cache if corrupted +# +# ==============================================