From 9d37e3a190e9eaee9623c9ec1bf8d10ad926b0a2 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Sat, 14 Feb 2026 08:30:55 -0600 Subject: [PATCH 1/4] Optimize CLAUDE.md from 119 to 31 lines Remove generated architecture docs, vague data flow sections, and boilerplate. Keep commands, key patterns, and development notes. Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..56735b4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,30 @@ +# Paper Dynasty Discord Bot + +Baseball card game Discord bot. discord.py with cog-based commands, SQLModel for database ops. + +## Commands + +```bash +python -m pytest # Run tests +python paperdynasty.py # Start bot +pip install -r requirements.txt # Install dependencies +``` + +## Architecture + +- **Cogs** (`cogs/`): Command modules — gameplay, economy, players, admins +- **Game engine** (`in_game/`): Turn-based simulation, AI manager, dice mechanics, WPA calculations +- **UI** (`utilities/`): Buttons, dropdowns, embeds +- **Database**: Production uses FastAPI database directly; dev may use separate PostgreSQL via SQLModel + +## Key Patterns + +- Cards generated from MLB statistics with complex rating calculations +- Card ratings split by handedness (`vs_hand: 'R'` or `'L'`) +- Multiple cardsets (seasons) with different priorities for gameplay +- Use factory data in testing as often as possible + +## Development Notes + +- Connect to proper docker socket when running tests +- Plans go in `./.claude/plans/` with descriptive filenames From b3220f0d25e2fd288fda3608544ac4b6caadc342 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Sat, 14 Feb 2026 08:32:34 -0600 Subject: [PATCH 2/4] Remove CLAUDE.md from .gitignore CLAUDE.md should be tracked in version control. Co-Authored-By: Claude Opus 4.6 --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index c78cbe5..da22593 100644 --- a/.gitignore +++ b/.gitignore @@ -132,7 +132,6 @@ dmypy.json .idea/ storage* *compose.yml -CLAUDE** **.db **/htmlcov .vscode/** From 73353c2086d4e485b55df9eda40f6a28a33a0ab0 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Sun, 15 Feb 2026 19:05:16 -0600 Subject: [PATCH 3/4] Add deployment details to CLAUDE.md (container name, logs, CI/CD) Align production environment section with Major Domo's format: container name, remote log command, co-hosted services, tea PR workflow. Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 56735b4..d4f1c3e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,6 +24,26 @@ pip install -r requirements.txt # Install dependencies - Multiple cardsets (seasons) with different priorities for gameplay - Use factory data in testing as often as possible +## Deployment + +### Production Environment +- **Host**: `ssh sba-bots` (10.10.0.88, alias `pd-bots`) +- **Path**: `/home/cal/container-data/paper-dynasty` +- **Bot container**: `paper-dynasty_discord-app_1` +- **Logs**: `ssh sba-bots "docker logs --since 1h paper-dynasty_discord-app_1"` +- **Other services on same host**: `paper-dynasty_adminer_1`, `paper-dynasty_db_1`, `sba-website_sba-web_1`, `sba-ghost_sba-ghost_1` +- **Image**: `manticorum67/paper-dynasty-discordapp` (Docker Hub) +- **Version file**: `VERSION` (current: 1.9.2) — bump before merge to `main` +- **Health**: Port 8080 — `/health`, `/ready`, `/metrics`, `/diagnostics` +- **Env vars**: `BOT_TOKEN`, `GUILD_ID`, `API_TOKEN`, `DATABASE` (Dev/Prod), `LOG_LEVEL`, `TZ=America/Chicago` + +### CI/CD +Builds and deploys are handled by Gitea Actions. Create a PR to `main` using `tea`: +```bash +tea pulls create --repo cal/paper-dynasty --head --base main --title "title" --description "description" +``` +Gitea validates the version, builds the Docker image, and deploys on merge. + ## Development Notes - Connect to proper docker socket when running tests From d2a4b27ff3232a2f8e54b506e43fef10d35fe710 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 17 Feb 2026 16:29:39 -0600 Subject: [PATCH 4/4] 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 | 174 +++++++++++++----------------- CLAUDE.md | 35 ++++-- 2 files changed, 96 insertions(+), 113 deletions(-) diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml index 4ec57be..895c291 100644 --- a/.gitea/workflows/docker-build.yml +++ b/.gitea/workflows/docker-build.yml @@ -1,3 +1,11 @@ +# Gitea Actions: Docker Build, Push, and Notify +# +# CI/CD pipeline for Paper Dynasty Discord Bot: +# - Builds Docker images on every push/PR +# - 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 + name: Build Docker Image on: @@ -15,122 +23,89 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - - - 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 (like 'v' prefix or pre-release tags) - 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 + with: + fetch-depth: 0 # Full history for tag counting - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - 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 }} - - - 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 - - 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/paper-dynasty-discordapp:dev + manticorum67/paper-dynasty-discordapp:dev-${{ steps.meta.outputs.sha_short }} + cache-from: type=registry,ref=manticorum67/paper-dynasty-discordapp:buildcache + cache-to: type=registry,ref=manticorum67/paper-dynasty-discordapp: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/paper-dynasty-discordapp:latest - manticorum67/paper-dynasty-discordapp:v${{ steps.meta.outputs.version }} + manticorum67/paper-dynasty-discordapp:${{ steps.meta.outputs.version }} manticorum67/paper-dynasty-discordapp:${{ steps.meta.outputs.version_sha }} cache-from: type=registry,ref=manticorum67/paper-dynasty-discordapp:buildcache cache-to: type=registry,ref=manticorum67/paper-dynasty-discordapp: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 + - 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-discordapp:latest\`" >> $GITHUB_STEP_SUMMARY - echo "- \`manticorum67/paper-dynasty-discordapp:v${{ steps.meta.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY + echo "- \`manticorum67/paper-dynasty-discordapp:${{ steps.meta.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY echo "- \`manticorum67/paper-dynasty-discordapp:${{ steps.meta.outputs.version_sha }}\`" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Build Details:**" >> $GITHUB_STEP_SUMMARY @@ -139,7 +114,7 @@ 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-discordapp:latest\`" >> $GITHUB_STEP_SUMMARY else @@ -152,36 +127,31 @@ jobs: curl -H "Content-Type: application/json" \ -d '{ "embeds": [{ - "title": "✅ Paper Dynasty Build Successful", - "description": "Docker image built and ready to deploy!", + "title": "Paper Dynasty Bot - 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 }, - { + { "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": "View Run", "value": "[Click here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})", "inline": false @@ -195,10 +165,11 @@ jobs: - 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 Build Failed", + "title": "Paper Dynasty Bot - Build Failed", "description": "Docker build encountered an error.", "color": 15158332, "fields": [ @@ -207,12 +178,12 @@ jobs: "value": "`${{ github.ref_name }}`", "inline": true }, - { + { "name": "Commit", "value": "`${{ github.sha }}`", "inline": true }, - { + { "name": "Author", "value": "${{ github.actor }}", "inline": true @@ -223,8 +194,7 @@ jobs: "inline": false } ], - "timestamp": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'" + "timestamp": "'"$TIMESTAMP"'" }] }' \ https://discord.com/api/webhooks/1468485116631453840/JALtLV8dO2IWso3tNcntHW8wDgQeMZuLgFKVUeoEY9ig_OPjjm4PmjvzwYebH5l7_Nxv - diff --git a/CLAUDE.md b/CLAUDE.md index d4f1c3e..d5cd775 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,25 +24,38 @@ pip install -r requirements.txt # Install dependencies - Multiple cardsets (seasons) with different priorities for gameplay - Use factory data in testing as often as possible -## Deployment +## Deployment & Troubleshooting -### Production Environment +### Production - **Host**: `ssh sba-bots` (10.10.0.88, alias `pd-bots`) - **Path**: `/home/cal/container-data/paper-dynasty` -- **Bot container**: `paper-dynasty_discord-app_1` -- **Logs**: `ssh sba-bots "docker logs --since 1h paper-dynasty_discord-app_1"` -- **Other services on same host**: `paper-dynasty_adminer_1`, `paper-dynasty_db_1`, `sba-website_sba-web_1`, `sba-ghost_sba-ghost_1` -- **Image**: `manticorum67/paper-dynasty-discordapp` (Docker Hub) -- **Version file**: `VERSION` (current: 1.9.2) — bump before merge to `main` -- **Health**: Port 8080 — `/health`, `/ready`, `/metrics`, `/diagnostics` -- **Env vars**: `BOT_TOKEN`, `GUILD_ID`, `API_TOKEN`, `DATABASE` (Dev/Prod), `LOG_LEVEL`, `TZ=America/Chicago` +- **Container**: `paper-dynasty_discord-app_1` +- **Image**: `manticorum67/paper-dynasty-discordapp` +- **Health**: `GET http://localhost:8080/health` (HTTP server in `health_server.py`) +- **Versioning**: CalVer (`YYYY.MM.BUILD`) — auto-generated on merge to `main` + +### Logs +- **Container logs**: `ssh sba-bots "docker logs --since 1h paper-dynasty_discord-app_1"` +- **Log file (in container)**: `/usr/src/app/logs/discord.log` (rotating, 32MB, 5 backups) +- **Dev log mount**: `../dev-logs` → `/usr/src/app/logs` + +### Key Env Vars +`BOT_TOKEN`, `GUILD_ID`, `API_TOKEN`, `DATABASE` (Dev/Prod), `LOG_LEVEL`, `SCOREBOARD_CHANNEL`, `TZ=America/Chicago` + +### Common Issues +- Bot not responding → check `docker logs`, verify `BOT_TOKEN` and `GUILD_ID` +- API errors → verify `DATABASE` is set to `Prod` or `Dev`, check `API_TOKEN` matches the database API +- Game engine errors → check `/usr/src/app/logs/discord.log` for detailed tracebacks +- Health endpoint not responding → `health_server.py` runs on port 8080 inside the container ### CI/CD -Builds and deploys are handled by Gitea Actions. Create a PR to `main` using `tea`: +Gitea Actions on PR to `main` — builds Docker image, auto-generates CalVer version on merge. ```bash tea pulls create --repo cal/paper-dynasty --head --base main --title "title" --description "description" ``` -Gitea validates the version, builds the Docker image, and deploys on merge. + +### Other Containers on Same Host +`paper-dynasty_adminer_1`, `paper-dynasty_db_1`, `sba-website_sba-web_1`, `sba-ghost_sba-ghost_1` ## Development Notes