From c7f55d79e3484b34210c36b99497afee1f78a0ef Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Thu, 5 Feb 2026 14:16:22 -0600 Subject: [PATCH] Fix injury commands player.team None errors and add Gitea CI/CD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix /injury roll and /injury clear commands crashing when player.team is None - Add team fetching logic after search_players() for all injury commands - The search_players() API endpoint returns team as ID only (not nested object) - Added null checks and explicit team fetching using team_service.get_team() - Add Gitea Actions workflow for automated Docker builds - Implements semantic version validation on PRs - Builds and pushes to Docker Hub on main branch merges - Sends Discord notifications on success/failure - Generates 3 image tags: latest, v{VERSION}, v{VERSION}-{COMMIT} Fixes AttributeError: 'NoneType' object has no attribute 'roster_type' Fixes AttributeError: 'NoneType' object has no attribute 'thumbnail' 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .gitea/workflows/docker-build.yml | 375 ++++++++++++++++++++++++++++++ VERSION | 2 +- commands/injuries/management.py | 15 ++ 3 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 .gitea/workflows/docker-build.yml diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml new file mode 100644 index 0000000..4938bea --- /dev/null +++ b/.gitea/workflows/docker-build.yml @@ -0,0 +1,375 @@ +# Gitea Actions: Docker Build, Push, and Notify +# +# This workflow provides a complete CI/CD pipeline for Major Domo Discord Bot: +# - 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: Major Domo Discord Bot v2.0 + +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., "2.29.4") + # - sha_short: First 7 chars of commit SHA + # - version_sha: Combined version+commit (e.g., "v2.29.4-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/major-domo-discordapp:latest + # - manticorum67/major-domo-discordapp:v2.29.4 + # - manticorum67/major-domo-discordapp:v2.29.4-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/major-domo-discordapp:latest + manticorum67/major-domo-discordapp:v${{ steps.meta.outputs.version }} + manticorum67/major-domo-discordapp:${{ 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/major-domo-discordapp:latest\`" >> $GITHUB_STEP_SUMMARY + echo "- \`manticorum67/major-domo-discordapp:v${{ steps.meta.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY + echo "- \`manticorum67/major-domo-discordapp:${{ 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/major-domo-discordapp: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": "✅ Major Domo Build Successful", + "description": "Discord bot 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/major-domo-discordapp](https://hub.docker.com/r/manticorum67/major-domo-discordapp)", + "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": "❌ Major Domo Build Failed", + "description": "Discord bot 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 2.29.4) +# +# ✅ 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 +# +# ============================================== diff --git a/VERSION b/VERSION index 2b99657..274b0c2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.29.3 +2.29.4 diff --git a/commands/injuries/management.py b/commands/injuries/management.py index 6ca26f0..be845cc 100644 --- a/commands/injuries/management.py +++ b/commands/injuries/management.py @@ -86,6 +86,11 @@ class InjuryGroup(app_commands.Group): player = players[0] + # Fetch full team data if team is not populated + if player.team_id and not player.team: + from services.team_service import team_service + player.team = await team_service.get_team(player.team_id) + # Check if player already has an active injury existing_injury = await injury_service.get_active_injury(player.id, current.season) if existing_injury: @@ -402,6 +407,11 @@ class InjuryGroup(app_commands.Group): player = players[0] + # Fetch full team data if team is not populated + if player.team_id and not player.team: + from services.team_service import team_service + player.team = await team_service.get_team(player.team_id) + # Check if player is on user's team # Note: This assumes you have a function to get team by owner # For now, we'll skip this check - you can add it if needed @@ -583,6 +593,11 @@ class InjuryGroup(app_commands.Group): player = players[0] + # Fetch full team data if team is not populated + if player.team_id and not player.team: + from services.team_service import team_service + player.team = await team_service.get_team(player.team_id) + # Get active injury injury = await injury_service.get_active_injury(player.id, current.season)