From e4c074c81f47bea51fc257596c5949db6b9e6905 Mon Sep 17 00:00:00 2001 From: MythEclipse Date: Mon, 18 May 2026 08:40:56 +0700 Subject: [PATCH] docs: add deployment design and implementation plan --- .../plans/2026-05-18-deploy-plan.md | 294 ++++++++++++++++++ .../specs/2026-05-18-deploy-design.md | 89 ++++++ 2 files changed, 383 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-18-deploy-plan.md create mode 100644 docs/superpowers/specs/2026-05-18-deploy-design.md diff --git a/docs/superpowers/plans/2026-05-18-deploy-plan.md b/docs/superpowers/plans/2026-05-18-deploy-plan.md new file mode 100644 index 0000000..d8438a3 --- /dev/null +++ b/docs/superpowers/plans/2026-05-18-deploy-plan.md @@ -0,0 +1,294 @@ +# Docker, Traefik, and GitHub Actions Deployment Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Containerize TeleUploader using Bun, configure Traefik labels for routing `upload.asepharyana.tech`, and set up full GitHub Actions CI/CD to VPS `45.127.35.244`. + +**Architecture:** Use a multi-stage `Dockerfile` with Bun to bundle and run the application. Serve via `docker-compose.yml` linking to external Traefik network `app-shared-net`. Deploy via GitHub Action workflow SSH using GitHub secrets for secure configuration. + +**Tech Stack:** Bun 1.1, Docker, Docker Compose, Traefik, GitHub Actions. + +--- + +### Task 1: Dockerfile Setup + +**Files:** +- Create: `Dockerfile` +- Create: `.dockerignore` + +- [ ] **Step 1: Create `.dockerignore` to prevent copying unnecessary files** +Write to `/.dockerignore`: +```ignore +node_modules +.git +.github +docs +logs +dist +.env +``` + +- [ ] **Step 2: Create multi-stage `Dockerfile` using Bun** +Write to `/Dockerfile`: +```dockerfile +# Stage 1: Build the application +FROM oven/bun:1.1-alpine AS builder + +WORKDIR /usr/src/app + +# Copy dependency configuration files +COPY package.json bun.lock ./ + +# Install dependencies +RUN bun install --frozen-lockfile + +# Copy source and configurations +COPY tsconfig.json biome.json ./ +COPY src ./src + +# Lint, format and build +RUN bun run lint +RUN bun run build + +# Stage 2: Final minimal production environment +FROM oven/bun:1.1-slim AS runner + +WORKDIR /usr/src/app + +# Set production environment variables +ENV NODE_ENV=production +ENV PORT=3000 + +# Copy necessary files from builder and repo +COPY --from=builder /usr/src/app/dist/index.js ./dist/index.js +COPY --from=builder /usr/src/app/package.json ./package.json +COPY schema.sql ./schema.sql + +# Expose server port +EXPOSE 3000 + +# Start server +CMD ["bun", "dist/index.js"] +``` + +- [ ] **Step 3: Test Docker build locally** +Run: `docker build -t teleuploader:test .` +Expected: Successfully builds without errors. + +- [ ] **Step 4: Commit Dockerfile changes** +Run: +```bash +git add Dockerfile .dockerignore +git commit -m "chore: add Dockerfile and dockerignore for production build" +``` + +--- + +### Task 2: Docker Compose Setup + +**Files:** +- Create: `docker-compose.yml` + +- [ ] **Step 1: Write `docker-compose.yml` with external Traefik network configuration** +Write to `/docker-compose.yml`: +```yaml +version: '3.8' + +services: + app: + image: ghcr.io/mytheclipse/teleuploader:latest + container_name: teleuploader-app + restart: always + environment: + - BOT_TOKEN=${BOT_TOKEN} + - STORAGE_CHANNEL_ID=${STORAGE_CHANNEL_ID} + - BASE_URL=${BASE_URL} + - DATABASE_URL=${DATABASE_URL} + - PORT=3000 + - NODE_ENV=production + - LOG_LEVEL=info + networks: + - app-shared-net + labels: + - "traefik.enable=true" + - "traefik.http.routers.teleuploader.rule=Host(`upload.asepharyana.tech`)" + - "traefik.http.routers.teleuploader.entrypoints=websecure" + - "traefik.http.routers.teleuploader.tls=true" + - "traefik.http.routers.teleuploader.tls.certresolver=letsencrypt" + - "traefik.http.services.teleuploader.loadbalancer.server.port=3000" + +networks: + app-shared-net: + name: app-shared-net + external: true +``` + +- [ ] **Step 2: Commit docker-compose configuration** +Run: +```bash +git add docker-compose.yml +git commit -m "chore: add docker-compose.yml with Traefik configurations" +``` + +--- + +### Task 3: GitHub Actions Deployment Pipeline + +**Files:** +- Create: `.github/workflows/deploy.yml` + +- [ ] **Step 1: Create GitHub Action deploy workflow** +Write to `/.github/workflows/deploy.yml`: +```yaml +name: Deploy TeleUploader + +on: + push: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.1 + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Run Tests + env: + BOT_TOKEN: "mock_token" + STORAGE_CHANNEL_ID: "123456" + BASE_URL: "http://localhost:3000" + DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/postgres" + PORT: "3000" + run: bun run test + + build-and-push: + needs: test + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and Push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ghcr.io/mytheclipse/teleuploader:latest + + deploy: + needs: build-and-push + runs-on: ubuntu-latest + steps: + - name: Deploy to VPS via SSH + uses: appleboy/ssh-action@v1.0.3 + with: + host: 45.127.35.244 + username: root + key: ${{ secrets.SSH_PRIVATE_KEY }} + script: | + mkdir -p /opt/teleuploader + cd /opt/teleuploader + + # Log in to GHCR on VPS + echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + + # Write dynamic docker-compose.yml + cat << 'EOF' > docker-compose.yml + version: '3.8' + + services: + app: + image: ghcr.io/mytheclipse/teleuploader:latest + container_name: teleuploader-app + restart: always + environment: + - BOT_TOKEN=${BOT_TOKEN} + - STORAGE_CHANNEL_ID=${STORAGE_CHANNEL_ID} + - BASE_URL=${BASE_URL} + - DATABASE_URL=${DATABASE_URL} + - PORT=3000 + - NODE_ENV=production + - LOG_LEVEL=info + networks: + - app-shared-net + labels: + - "traefik.enable=true" + - "traefik.http.routers.teleuploader.rule=Host(`upload.asepharyana.tech`)" + - "traefik.http.routers.teleuploader.entrypoints=websecure" + - "traefik.http.routers.teleuploader.tls=true" + - "traefik.http.routers.teleuploader.tls.certresolver=letsencrypt" + - "traefik.http.services.teleuploader.loadbalancer.server.port=3000" + + networks: + app-shared-net: + name: app-shared-net + external: true + EOF + + # Write .env file from secrets + cat << EOF > .env + BOT_TOKEN=${{ secrets.BOT_TOKEN }} + STORAGE_CHANNEL_ID=${{ secrets.STORAGE_CHANNEL_ID }} + BASE_URL=${{ secrets.BASE_URL }} + DATABASE_URL=${{ secrets.DATABASE_URL }} + PORT=3000 + EOF + + # Pull latest docker image + docker compose pull + + # Run DB migrations + docker compose run --rm app bun run db:migrate + + # Start service + docker compose up -d +``` + +- [ ] **Step 2: Commit workflow** +Run: +```bash +git add .github/workflows/deploy.yml +git commit -m "ci: add GitHub Actions pipeline for tests, Docker build, and deployment" +``` + +--- + +### Task 4: GitHub Secrets Configuration + +**Files:** +- Run Commands with `gh` CLI locally + +- [ ] **Step 1: Check GitHub CLI authentication** +Run: `gh auth status` +Expected: Authenticated successfully as `MythEclipse` (or similar). If not authenticated, prompt user to login using `! gh auth login`. + +- [ ] **Step 2: Configure SSH_PRIVATE_KEY secret** +Run: `gh secret set SSH_PRIVATE_KEY < ~/.ssh/id_rsa` (adjust path if custom key is used). +Expected: Secret successfully set. + +- [ ] **Step 3: Configure environment secrets** +Run: +```bash +gh secret set BOT_TOKEN --body "8605908810:AAFpUzlIBktfd_7wpEj7zMJob2CFxvG-ZGY" +gh secret set STORAGE_CHANNEL_ID --body "-1003996572954" +gh secret set BASE_URL --body "https://upload.asepharyana.tech" +gh secret set DATABASE_URL --body "postgresql://neondb_owner:npg_2ziHMPwZCet9@ep-long-glitter-ao3sjoyu-pooler.c-2.ap-southeast-1.aws.neon.tech/dcbot?sslmode=verify-full&channel_binding=require&connect_timeout=10" +``` +Expected: All secrets successfully set in repository. diff --git a/docs/superpowers/specs/2026-05-18-deploy-design.md b/docs/superpowers/specs/2026-05-18-deploy-design.md new file mode 100644 index 0000000..2eff26b --- /dev/null +++ b/docs/superpowers/specs/2026-05-18-deploy-design.md @@ -0,0 +1,89 @@ +--- +name: docker-traefik-ghactions-deploy +description: Dockerize TeleUploader, route through Traefik, and set up CI/CD with GitHub Actions to deploy to VPS +metadata: + type: project +--- + +# Design: TeleUploader Deployment & CI/CD Setup + +We are setting up production deployment for TeleUploader on VPS `45.127.35.244` behind Traefik utilizing GitHub Actions. + +## 1. System Architecture + +TeleUploader is a Bun-based service. +- **Docker Containerization**: Custom Docker image based on `oven/bun:1.1` to build and run the Bun application. +- **Reverse Proxy**: Traefik running on VPS acts as reverse proxy and TLS terminator. +- **Shared Network**: The application joins `app-shared-net` (external network pre-configured with Traefik). +- **Database**: External PostgreSQL database (Neon). Migration runs automatically before the service boots. + +## 2. Docker Specification + +### `Dockerfile` +- Multi-stage build. +- **Stage 1 (Build)**: Install dependencies, copy source files, run Biome lint/format checks, compile TS build to `dist/index.js` using `bun build`. +- **Stage 2 (Run)**: Use minimal `oven/bun:1.1-slim` runtime. Copy `dist/index.js`, `schema.sql`, and `package.json`. Expose port `3000`. + +### `docker-compose.yml` +```yaml +version: '3.8' + +services: + app: + image: ghcr.io/mytheclipse/teleuploader:latest + container_name: teleuploader-app + restart: always + environment: + - BOT_TOKEN=${BOT_TOKEN} + - STORAGE_CHANNEL_ID=${STORAGE_CHANNEL_ID} + - BASE_URL=${BASE_URL} + - DATABASE_URL=${DATABASE_URL} + - PORT=3000 + - NODE_ENV=production + - LOG_LEVEL=info + networks: + - app-shared-net + labels: + - "traefik.enable=true" + - "traefik.http.routers.teleuploader.rule=Host(`upload.asepharyana.tech`)" + - "traefik.http.routers.teleuploader.entrypoints=websecure" + - "traefik.http.routers.teleuploader.tls=true" + - "traefik.http.routers.teleuploader.tls.certresolver=letsencrypt" + - "traefik.http.services.teleuploader.loadbalancer.server.port=3000" + +networks: + app-shared-net: + name: app-shared-net + external: true +``` + +## 3. CI/CD GitHub Actions Specification + +- File: `.github/workflows/deploy.yml` +- Runs on: `ubuntu-latest` +- Triggers on: Push to `main` branch. + +### Pipeline Steps: +1. **Repository Checkout**: Retrieve code. +2. **Setup Bun**: Prepare test environment. +3. **Run Tests**: Execute `bun test` to guarantee correctness before build. +4. **Log in to GitHub Container Registry (GHCR)**: Authenticate using `GITHUB_TOKEN`. +5. **Build and Push**: Build Docker image and tag as `ghcr.io/mytheclipse/teleuploader:latest`, push to GHCR. +6. **VPS Deployment via SSH**: + - Establish SSH connection to `45.127.35.244` using private key. + - Sync/create directory `/opt/teleuploader`. + - Write dynamic `docker-compose.yml` and `.env` containing production secrets. + - Pull latest image: `docker compose pull`. + - Run database migrations: `docker compose run --rm app bun run db:migrate`. + - Restart service: `docker compose up -d`. + +## 4. Secret Configuration Plan +Using Github CLI (`gh secret set`): +- `SSH_PRIVATE_KEY` (using `~/.ssh/id_rsa` or designated key) +- `BOT_TOKEN` +- `STORAGE_CHANNEL_ID` +- `BASE_URL` +- `DATABASE_URL` + +--- +**Next Step**: User reviews written spec. Let me know if you want changes.