From 49911c53ac6976d899bac8dff788a4cb41d9314a Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 17 Feb 2026 16:29:38 -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/docker-build.yml | 286 +++++++----------------------- CLAUDE.md | 2 + 2 files changed, 62 insertions(+), 226 deletions(-) diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml index 0e5043b..d70b544 100644 --- a/.gitea/workflows/docker-build.yml +++ b/.gitea/workflows/docker-build.yml @@ -1,13 +1,10 @@ -# Gitea Actions: Major Domo Database - Docker Build, Push, and Notify +# Gitea Actions: Docker Build, Push, and Notify # -# This workflow provides a complete CI/CD pipeline for the Major Domo Database API: -# - Validates semantic versioning on PRs +# CI/CD pipeline for Major Domo 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-04 -# Adapted from: paper-dynasty-database template name: Build Docker Image @@ -24,192 +21,91 @@ 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: 2.4.1 → 2.4.2 (bug fixes) - # - Minor: 2.4.1 → 2.5.0 (new features) - # - Major: 2.4.1 → 3.0.0 (breaking changes) - # - # Invalid bumps: - # - 2.4.1 → 2.6.0 (skipped minor version) - # - 2.4.1 → 2.3.0 (went backwards) - # - 2.4.1 → 2.5.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 (manticorum67) - # - 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., "2.4.1") - # - sha_short: First 7 chars of commit SHA - # - version_sha: Combined version+commit (e.g., "v2.4.1-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/major-domo-database:latest - # - manticorum67/major-domo-database:v2.4.1 - # - manticorum67/major-domo-database:v2.4.1-a1b2c3d - # - # Push behavior: - # - PRs: Build only (test), don't push - # - Main: Build and push to Docker Hub - # - - name: Build Docker image + echo "CalVer version: ${VERSION}" + + # Dev build: push with dev + dev-SHA tags (PR/feature branches) + - name: Build Docker image (dev) + if: github.ref != 'refs/heads/main' uses: docker/build-push-action@v5 with: context: . - push: ${{ github.ref == 'refs/heads/main' }} + push: true + tags: | + manticorum67/major-domo-database:dev + manticorum67/major-domo-database:dev-${{ steps.meta.outputs.sha_short }} + cache-from: type=registry,ref=manticorum67/major-domo-database:buildcache + cache-to: type=registry,ref=manticorum67/major-domo-database:buildcache,mode=max + + # Production build: push with latest + CalVer tags (main only) + - name: Build Docker image (production) + if: github.ref == 'refs/heads/main' + uses: docker/build-push-action@v5 + with: + context: . + push: true tags: | manticorum67/major-domo-database:latest - manticorum67/major-domo-database:v${{ steps.meta.outputs.version }} + manticorum67/major-domo-database:${{ steps.meta.outputs.version }} manticorum67/major-domo-database:${{ steps.meta.outputs.version_sha }} cache-from: type=registry,ref=manticorum67/major-domo-database:buildcache cache-to: type=registry,ref=manticorum67/major-domo-database:buildcache,mode=max - # ============================================== - # 7. BUILD SUMMARY - # ============================================== - # Creates a formatted summary visible in Actions UI - # Shows: image tags, build details, push status - # + # 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 + - 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/major-domo-database:latest\`" >> $GITHUB_STEP_SUMMARY - echo "- \`manticorum67/major-domo-database:v${{ steps.meta.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY + echo "- \`manticorum67/major-domo-database:${{ steps.meta.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY echo "- \`manticorum67/major-domo-database:${{ steps.meta.outputs.version_sha }}\`" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Build Details:**" >> $GITHUB_STEP_SUMMARY @@ -218,39 +114,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/major-domo-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 as secret: DISCORD_WEBHOOK_URL in Gitea repo settings - # - name: Discord Notification - Success if: success() && github.ref == 'refs/heads/main' run: | curl -H "Content-Type: application/json" \ -d '{ "embeds": [{ - "title": "✅ Major Domo Database Build Successful", - "description": "Docker image built and pushed to Docker Hub!", + "title": "Major Domo 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 +141,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 +151,6 @@ jobs: "value": "${{ github.actor }}", "inline": true }, - { - "name": "Docker Hub", - "value": "[manticorum67/major-domo-database](https://hub.docker.com/r/manticorum67/major-domo-database)", - "inline": false - }, { "name": "View Run", "value": "[Click here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})", @@ -289,13 +162,6 @@ jobs: }' \ ${{ secrets.DISCORD_WEBHOOK_URL }} - # ============================================== - # 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 +169,7 @@ jobs: curl -H "Content-Type: application/json" \ -d '{ "embeds": [{ - "title": "❌ Major Domo Database Build Failed", + "title": "Major Domo Database - Build Failed", "description": "Docker build encountered an error.", "color": 15158332, "fields": [ @@ -332,35 +198,3 @@ jobs: }] }' \ ${{ secrets.DISCORD_WEBHOOK_URL }} - -# ============================================== -# SETUP REQUIRED -# ============================================== -# Before this workflow will work: -# -# ✅ Add secrets to Gitea repo (Settings → Secrets → Actions): -# - DOCKERHUB_USERNAME: manticorum67 -# - DOCKERHUB_TOKEN: Docker Hub access token -# - DISCORD_WEBHOOK_URL: Discord webhook for build notifications -# -# ✅ Ensure VERSION file exists in repo root (currently: 2.4.1) -# -# ============================================== -# TROUBLESHOOTING -# ============================================== -# Common issues and solutions: -# -# 1. VERSION validation failing unexpectedly -# - Ensure VERSION file contains only version number (no 'v' prefix) -# - 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 -# -# 3. Discord notifications not appearing -# - Test webhook URL manually with curl -# - Check webhook still exists in Discord channel settings -# - Look for HTTP error codes in Actions logs -# -# ============================================== diff --git a/CLAUDE.md b/CLAUDE.md index 72eaae6..3a5a653 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,6 +28,8 @@ python migrations.py # Run migrations (SQL files in migrat **Required**: `API_TOKEN`, `POSTGRES_HOST`, `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD` **Optional**: `POSTGRES_PORT` (5432), `LOG_LEVEL` (WARNING), `PRIVATE_IN_SCHEMA` +- **CI/CD**: Gitea Actions on PR to `main` — builds Docker image, auto-generates CalVer version (`YYYY.MM.BUILD`) on merge + ## Important - All active code is in `/app` — root-level `main.py`, `db_engine.py` are legacy, not in use