From 5a2e64eca106c5ac08c2dedef9d990a0affcf40d Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 17 Feb 2026 16:29:41 -0600 Subject: [PATCH] ci: Switch to CalVer (YYYY.MM.BUILD) with auto-generated versions Remove manual semver validation from PR checks. Versions are now auto-generated on merge to main by counting existing monthly tags. Co-Authored-By: Claude Opus 4.6 --- .gitea/workflows/build.yml | 279 ++++++------------------------------- CLAUDE.md | 31 ++++- 2 files changed, 69 insertions(+), 241 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 95e9863..7c85b80 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -1,13 +1,10 @@ # Gitea Actions: Docker Build, Push, and Notify # -# This workflow provides a complete CI/CD pipeline for Paper Dynasty Database: -# - Validates semantic versioning on PRs +# CI/CD pipeline for Paper Dynasty Database API: # - Builds Docker images on every push/PR -# - Pushes to Docker Hub on main branch merges +# - Auto-generates CalVer version (YYYY.MM.BUILD) on main branch merges +# - Pushes to Docker Hub and creates git tag on main # - Sends Discord notifications on success/failure -# -# Created: 2026-02-05 -# For: Paper Dynasty Database API name: Build Docker Image @@ -24,122 +21,14 @@ jobs: runs-on: ubuntu-latest steps: - # ============================================== - # 1. CHECKOUT CODE - # ============================================== - name: Checkout code uses: actions/checkout@v4 + with: + fetch-depth: 0 # Full history for tag counting - # ============================================== - # 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 @@ -147,44 +36,31 @@ jobs: 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 + # Generate CalVer: YYYY.MM.BUILD + # BUILD = count of tags matching current month + 1 + - name: Generate CalVer version id: meta run: | - VERSION=$(cat VERSION 2>/dev/null || echo "0.0.0") + YEAR=$(date -u +%Y) + MONTH=$(date -u +%-m) + PREFIX="${YEAR}.${MONTH}." + + # Count existing tags for this month + git fetch --tags + BUILD=$(git tag -l "${PREFIX}*" | wc -l) + BUILD=$((BUILD + 1)) + + VERSION="${PREFIX}${BUILD}" 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 "version_sha=${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 - # + echo "CalVer version: ${VERSION}" + - name: Build Docker image uses: docker/build-push-action@v5 with: @@ -192,24 +68,30 @@ jobs: 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 }} manticorum67/paper-dynasty-database:${{ steps.meta.outputs.version_sha }} - cache-from: type=gha - cache-to: type=gha,mode=max + cache-from: type=registry,ref=manticorum67/paper-dynasty-database:buildcache + cache-to: type=registry,ref=manticorum67/paper-dynasty-database:buildcache,mode=max + + # Create git tag and update VERSION file on successful push + - name: Tag release + if: success() && github.ref == 'refs/heads/main' + run: | + git config user.name "Gitea Actions" + git config user.email "actions@git.manticorum.com" + echo "${{ steps.meta.outputs.version }}" > VERSION + git add VERSION + git commit -m "chore: bump version to ${{ steps.meta.outputs.version }} [skip ci]" || true + git tag "${{ steps.meta.outputs.version }}" + git push origin main --tags - # ============================================== - # 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 "## 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 }}\`" >> $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 @@ -218,39 +100,26 @@ jobs: 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 "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!", + "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 }}`", + "value": "`${{ steps.meta.outputs.version }}`", "inline": true }, { @@ -258,11 +127,6 @@ jobs: "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 }}`", @@ -273,11 +137,6 @@ jobs: "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 }})", @@ -289,13 +148,6 @@ jobs: }' \ ${{ 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: | @@ -303,7 +155,7 @@ jobs: curl -H "Content-Type: application/json" \ -d '{ "embeds": [{ - "title": "❌ Paper Dynasty Database Build Failed", + "title": "Paper Dynasty Database - Build Failed", "description": "Docker build encountered an error.", "color": 15158332, "fields": [ @@ -331,45 +183,4 @@ jobs: "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 -# -# ============================================== + ${{ secrets.DISCORD_WEBHOOK }} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index b2efd72..4757d65 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,15 +24,32 @@ docker build -t paper-dynasty-db . # Build image - `ranked_cardsets`: Legal cardsets for ranked play - `CARDSETS`: Configuration for game modes and card availability -## Environments +## Environments & Troubleshooting -| Env | URL | SSH | Container | Port | -|-----|-----|-----|-----------|------| -| **Prod** | pd.manticorum.com | `akamai` | `pd_api` | 815 | -| **Dev** | pddev.manticorum.com | `pd-database` | `dev_pd_database` | 816 | +| | Dev | Prod | +|---|---|---| +| **URL** | pddev.manticorum.com | pd.manticorum.com | +| **Host** | `ssh pd-database` | `ssh akamai` → `/root/container-data/paper-dynasty` | +| **API container** | `dev_pd_database` | `pd_api` | +| **PostgreSQL** | `pd_postgres` (port 5432) | `pd_postgres` | +| **Adminer** | port 8081 | — | +| **API port** | 816 | 815 | +| **Image** | `manticorum67/paper-dynasty-database` | `manticorum67/paper-dynasty-database` | -**Prod compose path**: `ssh akamai` → `/root/container-data/paper-dynasty` -**Dev PostgreSQL**: `sba_postgres` on `10.10.0.42:5432`, user `sba_admin`, db `paperdynasty_dev` +### Logs +- **Container logs**: `docker logs --since 1h pd_api` +- **Log file (in container)**: `logs/database/YYYY-MM-DD.log` (date-stamped daily, no rotation) +- **API docs**: `/api/docs` and `/api/redoc` + +### Key Env Vars +`API_TOKEN`, `LOG_LEVEL`, `DATABASE_TYPE` (sqlite/postgresql), `POSTGRES_HOST`, `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD` + +### Common Issues +- 502 Bad Gateway → API container crashed; check `docker logs pd_api` +- Card image generation failures → Playwright/Chromium issue; check for missing dependencies +- SQLite locking (dev) → WAL mode should prevent, but check for long-running writes +- DB connection errors → verify `POSTGRES_HOST` points to correct container name +- **CI/CD**: Gitea Actions on PR to `main` — builds Docker image, auto-generates CalVer version (`YYYY.MM.BUILD`) on merge ## Important