Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
DISCORD_TOKEN="" # Your bot token
APPLICATION_ID="" # Your bot's application ID
CLIENT_ID="" # Your bot's application ID
122 changes: 122 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
name: Deploy

on:
workflow_dispatch: # Manual trigger only
# Uncomment below when VPS is ready:
# push:
# branches: [ "main" ]
# paths-ignore:
# - 'docs/**'
# - '.gitignore'
# - 'LICENSE'

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

jobs:
build-test-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

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

- name: Install dependencies
run: |
if [ -f package-lock.json ]; then
npm ci --no-audit --no-fund
else
npm install --no-audit --no-fund
fi

- name: Lint
run: npm run lint

- name: Build
run: npm run build:ci
env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
CLIENT_ID: ${{ secrets.CLIENT_ID }}

- name: Run tests
run: npm test

- name: Package artifact
run: |
tar -czf release.tar.gz dist package.json package-lock.json .nvmrc || tar -czf release.tar.gz dist package.json .nvmrc

- name: Create .env file from secrets
env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
CLIENT_ID: ${{ secrets.CLIENT_ID }}
run: |
set -euo pipefail
printf "DISCORD_TOKEN=%s\n" "$DISCORD_TOKEN" > .env
printf "CLIENT_ID=%s\n" "$CLIENT_ID" >> .env
printf "NODE_ENV=production\n" >> .env

- name: Copy artifact to VPS
env:
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_USER: ${{ secrets.SSH_USER }}
SSH_PORT: ${{ secrets.SSH_PORT }}
SSH_KEY: ${{ secrets.SSH_KEY }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 -p ${SSH_PORT:-22} $SSH_USER@$SSH_HOST "mkdir -p ~/apps/webdev-bot/releases"
scp -i ~/.ssh/id_ed25519 -P ${SSH_PORT:-22} -o StrictHostKeyChecking=no release.tar.gz $SSH_USER@$SSH_HOST:~/apps/webdev-bot/releases/release.tar.gz

- name: Upload .env to VPS
env:
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_USER: ${{ secrets.SSH_USER }}
SSH_PORT: ${{ secrets.SSH_PORT }}
SSH_KEY: ${{ secrets.SSH_KEY }}
APP_DIR: ${{ secrets.APP_DIR }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 -p ${SSH_PORT:-22} $SSH_USER@$SSH_HOST "mkdir -p ${APP_DIR:-\"~/apps/webdev-bot\"}/shared && chmod 700 ${APP_DIR:-\"~/apps/webdev-bot\"}/shared"
scp -i ~/.ssh/id_ed25519 -P ${SSH_PORT:-22} -o StrictHostKeyChecking=no .env $SSH_USER@$SSH_HOST:${APP_DIR:-"~/apps/webdev-bot"}/shared/.env
ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 -p ${SSH_PORT:-22} $SSH_USER@$SSH_HOST "chmod 600 ${APP_DIR:-\"~/apps/webdev-bot\"}/shared/.env"

- name: Deploy on VPS
env:
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_USER: ${{ secrets.SSH_USER }}
SSH_PORT: ${{ secrets.SSH_PORT }}
SSH_KEY: ${{ secrets.SSH_KEY }}
APP_DIR: ${{ secrets.APP_DIR }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_ed25519 -p ${SSH_PORT:-22} $SSH_USER@$SSH_HOST << 'EOF'
set -euo pipefail
APP_DIR=${APP_DIR:-"~/apps/webdev-bot"}
mkdir -p "$APP_DIR/current" "$APP_DIR/releases" "$APP_DIR/shared"
cd "$APP_DIR"
rm -rf current/*
tar -xzf releases/release.tar.gz -C current
cd current
# Load env from shared/.env for the PM2 process
set -a
if [ -f "$APP_DIR/shared/.env" ]; then . "$APP_DIR/shared/.env"; fi
set +a
if [ -f package-lock.json ]; then
npm ci --omit=dev --no-audit --no-fund || true
else
npm install --omit=dev --no-audit --no-fund || true
fi
pm2 describe webdev-bot >/dev/null 2>&1 && pm2 restart webdev-bot || pm2 start "node dist/index.js" --name webdev-bot
pm2 save || true
EOF

46 changes: 46 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Test

on:
pull_request:
branches: [ "**" ]
paths-ignore:
- 'docs/**'
- '.gitignore'
- 'LICENSE'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

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

- name: Install dependencies
run: |
if [ -f package-lock.json ]; then
npm ci --no-audit --no-fund
else
npm install --no-audit --no-fund
fi

- name: Lint
run: npm run lint

- name: Build
run: npm run build:ci
env:
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
CLIENT_ID: ${{ secrets.CLIENT_ID }}

- name: Run tests
run: npm test

1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v22.20.0
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"description": "Web Dev & Web Design discord bot",
"type": "module",
"scripts": {
"build": "pnpm run build:ts && pnpm run build:copy",
"build:ci": "tsup && node scripts/copy-assets.js",
"build:dev": "pnpm run build:ts && pnpm run build:copy",
"build:ts": "tsup",
"build:copy": "node scripts/copy-assets.js",
"start": "node dist/index.js",
Expand All @@ -14,7 +15,8 @@
"lint:fix": "biome lint --fix .",
"format": "biome format --write .",
"check": "biome check .",
"check:fix": "biome check --write ."
"check:fix": "biome check --write .",
"test": "node --test"
},
"keywords": [],
"author": "",
Expand Down
7 changes: 7 additions & 0 deletions test/sanity.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import assert from 'node:assert/strict';
import test from 'node:test';

test('sanity: 1 + 1 equals 2', () => {
assert.equal(1 + 1, 2);
});