Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
799c3ed
chore: add prisma and SQLite deps
hmd-ali Aug 17, 2026
d70b32b
feat: add prisma schema
hmd-ali Aug 17, 2026
d07be56
ci: enhance CI/CD workflows with rollback support and Prisma integration
hmd-ali Aug 17, 2026
f4248db
chore: add lru-cache package
hmd-ali Aug 17, 2026
ccd4e7c
feat: add TAG_ACCESS_ROLE_ID env
hmd-ali Aug 17, 2026
631cd30
feat: add prisma to tsconfig
hmd-ali Aug 17, 2026
51d9003
feat: add utils
hmd-ali Aug 17, 2026
25628ae
feat: export prisma singleton
hmd-ali Aug 17, 2026
31e6196
feat: add ErrorMessages class
hmd-ali Aug 17, 2026
0b2e912
feat: add bot options
hmd-ali Aug 17, 2026
6c4b6bd
feat: add tags feature
hmd-ali Aug 17, 2026
20033be
fix: add missing test env
hmd-ali Aug 17, 2026
978bf69
fix: expiresAt column definition
hmd-ali Aug 17, 2026
1398aa0
feat: add `createdAt` and `lastUsedAt` column indices
hmd-ali Aug 18, 2026
a3fc77e
feat: implement select menu interaction handling
hmd-ali Aug 18, 2026
de72d3c
feat: add methods for retrieving unused and prunable tags, and implem…
hmd-ali Aug 18, 2026
ac70fda
feat: make `/tags list` ephemeral
hmd-ali Aug 18, 2026
b9d0296
feat: add prune command for managing unused
hmd-ali Aug 18, 2026
e9c7dd7
test: add prune-tags tests
hmd-ali Aug 19, 2026
3b1c609
feat: move prisma generated to src/ folder
hmd-ali Aug 19, 2026
9584e16
ci: switch to building on the vps instead of in ci
hmd-ali Aug 19, 2026
5ae4a86
feat: add pnpm `allowBuilds` list
hmd-ali Aug 19, 2026
80b095a
chore: remove unused build entry
hmd-ali Aug 19, 2026
8b9bb73
Merge branch 'main' of github.com:r-webdev/webdev-bot into feat/tags
hmd-ali Aug 19, 2026
baaf593
chore: revert deployment directory path in workflow
hmd-ali Aug 21, 2026
8572ec3
fix: update Docker command for deploying Discord commands
hmd-ali Aug 21, 2026
a7ac042
docs: update Docker commands for development and production modes
hmd-ali Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.example
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,6 +16,7 @@ ARCHIVE_CATEGORY_ID=archive_category_id_here
MODERATORS_ROLE_IDS=role_id_1,role_id_2,role_id_3
REPEL_ROLE_ID=your_repel_role_id_here
ONBOARDING_ROLE_ID=onboarding_role_id_here
TAG_ACCESS_ROLE_ID=tag_access_role_id_here

# Optional Role IDs
# ROLE_A_ID=optional_role_a_id
Expand All@@ -29,4 +30,7 @@ GUIDES_TRACKER_PATH=guides-tracker.json
ADVENT_OF_CODE_TRACKER_PATH=advent-of-code-tracker.json


DATABASE_URL=file:./data/webdev.db



1 change: 1 addition & 0 deletions .env.production
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,6 +19,7 @@ ARCHIVE_CATEGORY_ID=837507969859977258
REPEL_ROLE_ID=1002411741776461844
MODERATORS_ROLE_IDS=849481536654803004
REGULAR_ROLE_ID=1383170370634514544
TAG_ACCESS_ROLE_ID=1502128862224711805

# Other
GUIDES_TRACKER_PATH=/app/data/guides-tracker.json
Expand Down
5 changes: 4 additions & 1 deletion .env.test
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,7 +25,10 @@ ARCHIVE_CATEGORY_ID=your-archived-category-id
REPEL_ROLE_ID=your-repel-role-id
MODERATORS_ROLE_IDS=your-moderator-role-id
REGULAR_ROLE_ID=your-regular-role-id
TAG_ACCESS_ROLE_ID=your-tag-access-role-id

# Other
GUIDES_TRACKER_PATH=guides-tracker.json
ADVENT_OF_CODE_TRACKER_PATH=test-advent-tracker.json
ADVENT_OF_CODE_TRACKER_PATH=test-advent-tracker.json

DATABASE_URL=test.db
2 changes: 1 addition & 1 deletion .github/workflows/deploy-commands.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,6 +32,6 @@ jobs:
# Run deploy script inside the already running Docker container
# .env file should already exist from main deployment
echo "Deploying Discord commands..."
docker compose --profile prod exec bot-prod node dist/scripts/deploy-commands.js
docker compose exec bot node dist/scripts/deploy-commands.js

echo "Discord commands deployment completed!"
132 changes: 100 additions & 32 deletions .github/workflows/deploy.yml
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,126 @@
name: Build, Test and Deploy Discord Bot to VPS

name: Deploy

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you rename it?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main purpose of the of this workflow is deploying the bot

on:
push:
branches: ['main']
workflow_dispatch: # Allow manual deployment
branches: [main]
workflow_dispatch:
inputs:
ref:
description: 'Git ref (commit SHA or tag) to deploy. Leave blank for latest main.'
required: false
default: ''

concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: false

env:
MAX_BACKUPS: 4

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain what this is for?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We make db backup when deploying a new version, this represents the total active backups, older ones are deleted.
It's as an env to avoid magic numbers

DIR: /home/${{ secrets.VPS_USER }}/webdev-bot-deploy

jobs:
deploy:
lint:
name: Lint & Format Check
runs-on: ubuntu-latest

steps:
- name: Checkout code
- name: Checkout
uses: actions/checkout@v4

- name: Read Node version
run: |
NODE_VERSION=$(cat .nvmrc | sed 's/v//')
echo "NODE_VERSION=$NODE_VERSION" >> $GITHUB_ENV
- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Prisma generate (sanity check schema)
run: pnpm run prisma:generate
env:
DATABASE_URL: file:/tmp/ci.db

- name: Deploy to VPS
uses: appleboy/ssh-action@v1.0.3
- name: Lint
run: pnpm lint

- name: Format
run: pnpm fmt:check

- name: Typecheck
run: pnpm typecheck

deploy:
name: Deploy to VPS
runs-on: ubuntu-latest
needs: [lint]
permissions:
contents: read
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
envs: REPO_URL,REF
script: |
cd /home/${{ secrets.VPS_USER }}/webdev-bot-deploy
set -eu
mkdir -p ${{ env.DIR }}
cd ${{ env.DIR }}

# Stash any local changes
git stash push -m "Auto-deploy $(date)" 2>/dev/null || true
if [ -d .git ]; then
git stash push -m "Auto-deploy $(date)" 2>/dev/null || true
git fetch origin
git checkout "$REF"
git reset --hard "origin/$REF" 2>/dev/null || git reset --hard "$REF"
else
git clone "$REPO_URL" .
git checkout "$REF"
fi

# Pull latest changes
git checkout main
git pull origin main
mkdir -p ${{ env.DIR }}/backups

# Read NODE_VERSION from .nvmrc
export NODE_VERSION=$(cat .nvmrc | sed 's/v//')
export NODE_VERSION=$(cat .nvmrc | tr -d 'v')
echo "Using Node version: $NODE_VERSION"

# Create .env file with secrets
# Public config comes from .env.production (committed to repo)
# NODE_ENV=production is set in docker-compose.yml
cat > .env << EOF
cat > .env <<'EOF'
DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }}
CLIENT_ID=${{ secrets.CLIENT_ID }}
EOF
chmod 600 .env

# Backup the SQLite DB before touching anything
if docker volume inspect webdev-bot_guides-data >/dev/null 2>&1; then
docker run --rm \
-v webdev-bot_guides-data:/data \
-v ${{ env.DIR }}/backups:/backup \
alpine sh -c "if [ -f /data/db.sqlite ]; then cp /data/db.sqlite /backup/db-\$(date +%Y%m%d%H%M%S).sqlite; fi"
ls -1t ${{ env.DIR }}/backups/*.sqlite 2>/dev/null \
| tail -n +$((env.MAX_BACKUPS+1)) \
| xargs -r rm --
fi

Comment on lines +94 to +104

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you explain all the commands to me?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • if docker volume inspect webdev-bot_guides-data >/dev/null 2>&1; then
    checks if the volume (where the db is at) exists, otherwise don't run (in case of first tim deployment, nothing to back up)

  • docker run --rm
    run in a temp container that's auto removed after it's done

  • -v ...
    mounts the existing database and backup folders

  • ...rest
    only copies if db exists, with a timestamp, deletes older backups based on MAX_BACKUPS

docker compose config >/dev/null
docker compose build bot

if ! docker compose run --rm migrate; then
echo "Migration failed — leaving existing bot container untouched."
exit 1
fi

# Stop any existing containers
docker compose down || true
docker compose up -d --remove-orphans bot

# Build and start production container with profile
# NODE_ENV=production is explicitly set in docker-compose.yml bot-prod service
docker compose --profile prod up -d --build
sleep 8
if ! docker compose ps bot --status running | grep -q bot; then
echo "Bot container is not running after deploy!"
docker compose logs --tail=50 bot
exit 1
fi

# Check status
echo "Deployment completed. Container status:"
docker compose ps
docker image prune -f
env:
REPO_URL: https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
REF: ${{ github.event.inputs.ref != '' && github.event.inputs.ref || 'main' }}
Comment on lines +125 to +126

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you explain this, too?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

variables for the Deploy via SSH step, (imagine this is TS file and these are const REPO_URL = ...). makes it cleaner than hardcoding/duplicating them in the scripts.

5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,6 +31,11 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Prisma generate (sanity check schema)
run: pnpm run prisma:generate
env:
DATABASE_URL: file:/tmp/ci.db

- name: Lint
run: pnpm lint

Expand Down
10 changes: 9 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,4 +30,12 @@ docker-compose.yml
# terraform
**/.terraform/
*.tfstate
*.tfstate.*
*.tfstate.*

# prisma
src/generated

# Database files
*.db
*.sqlite
*.sqlite3
24 changes: 12 additions & 12 deletions DOCKER.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,7 +36,7 @@ pnpm run docker:dev
Or using docker-compose directly:

```bash
docker compose --profile dev up
docker compose -f docker-compose.dev.yml up
```

In development mode:
Expand All@@ -48,9 +48,9 @@ In development mode:
To stop the development container:

```bash
pnpm run docker:stop
pnpm run docker:dev:stop
# or
docker compose --profile dev down
docker compose -f docker-compose.dev.yml down
```

### Production Mode
Expand All@@ -64,7 +64,7 @@ pnpm run docker:prod
Or using docker-compose directly:

```bash
docker compose --profile prod up -d
docker compose up -d
```

In production mode:
Expand All@@ -76,7 +76,7 @@ In production mode:
To stop the production container:

```bash
docker compose --profile prod down
docker compose down
```

## Manual Docker Commands
Expand DownExpand Up@@ -128,12 +128,12 @@ docker run -it \
1. Check if environment variables are set correctly:

```bash
docker compose --profile dev run --rm bot-dev env | grep DISCORD
docker compose -f docker-compose.dev.yml run --rm bot-dev env | grep DISCORD
```

2. View container logs:
```bash
docker compose --profile dev logs bot-dev
docker compose -f docker-compose.dev.yml logs bot-dev
# or
docker logs webdev-bot-dev
```
Expand All@@ -159,7 +159,7 @@ If code changes aren't being detected in development mode:

2. Restart the container:
```bash
docker compose --profile dev restart
docker compose -f docker-compose.dev.yml restart
```

### Image Size Concerns
Expand DownExpand Up@@ -189,8 +189,8 @@ docker compose build --no-cache
## Best Practices

1. **Never commit `.env`** - Keep your secrets secure
2. **Use production profile for deployment** - Smaller, more secure images
3. **Keep development profile for local testing** - Faster iteration with hot reload
2. **Use production compose for deployment** - Smaller, more secure images
3. **Keep development compose for local testing** - Faster iteration with hot reload
4. **Node version is managed in `.nvmrc`** - Update `.nvmrc` to change Node version for Docker
5. **Regularly update the base image** - Rebuild images after updating `.nvmrc`
6. **Monitor container resources** - Use `docker stats` to check resource usage
Expand DownExpand Up@@ -218,11 +218,11 @@ docker compose down -v
View container logs in real-time:

```bash
docker compose --profile dev logs -f bot-dev
docker compose -f docker-compose.dev.yml logs -f bot-dev
```

Execute commands inside the container:

```bash
docker compose --profile dev exec bot-dev sh
docker compose -f docker-compose.dev.yml exec bot-dev sh
```
Loading
Loading