feat(deploy): update deployment workflow and remove obsolete Docker publish workflow
This commit is contained in:
@@ -1,72 +1,103 @@
|
|||||||
name: Deploy GMW to VPS
|
name: Deploy to VPS
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_run:
|
push:
|
||||||
workflows: ["Build and Push Docker Image"]
|
|
||||||
types:
|
|
||||||
- completed
|
|
||||||
branches:
|
branches:
|
||||||
- main
|
|
||||||
- master
|
- master
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: gmw-deploy
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/bete
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
submodules: recursive
|
||||||
submodules: false
|
|
||||||
|
|
||||||
- name: Deploy GMW on VPS
|
- name: Normalize image name
|
||||||
|
run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to GHCR
|
||||||
|
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@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.IMAGE_NAME }}:latest
|
||||||
|
${{ env.IMAGE_NAME }}:${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Deploy to VPS
|
||||||
|
uses: appleboy/ssh-action@v1.2.0
|
||||||
env:
|
env:
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
IMAGE_NAME: ${{ env.IMAGE_NAME }}
|
||||||
VPS_HOST: ${{ secrets.VPS_HOST }}
|
GHCR_USERNAME: ${{ github.actor }}
|
||||||
VPS_USER: ${{ secrets.VPS_USER }}
|
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
VPS_TARGET_DIR: ${{ secrets.VPS_TARGET_DIR }}
|
ENV_FILE: ${{ secrets.ENV_FILE }}
|
||||||
ENV_FILE_PRODUCTION: ${{ secrets.ENV_FILE_PRODUCTION }}
|
with:
|
||||||
run: |
|
host: ${{ secrets.VPS_HOST }}
|
||||||
set -euo pipefail
|
username: ${{ secrets.VPS_USERNAME }}
|
||||||
|
key: ${{ secrets.VPS_SSH_KEY }}
|
||||||
|
envs: IMAGE_NAME,GHCR_USERNAME,GHCR_TOKEN,ENV_FILE
|
||||||
|
script: |
|
||||||
|
set -eu
|
||||||
|
|
||||||
echo "Deploy event: ${{ github.event_name }}"
|
APP_DIR=/opt/imphenbot
|
||||||
echo "Deploy ref: ${{ github.ref }}"
|
mkdir -p "$APP_DIR"
|
||||||
echo "Deploy sha: ${{ github.sha }}"
|
cd "$APP_DIR"
|
||||||
|
|
||||||
if [ -z "${SSH_PRIVATE_KEY:-}" ] || [ -z "${VPS_HOST:-}" ] || [ -z "${VPS_USER:-}" ] || [ -z "${VPS_TARGET_DIR:-}" ]; then
|
printf '%s\nIMAGE_NAME=%s:latest\n' "$ENV_FILE" "$IMAGE_NAME" > .env
|
||||||
echo "❌ Deployment secrets are not fully configured. Please set SSH_PRIVATE_KEY, VPS_HOST, VPS_USER, and VPS_TARGET_DIR."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p ~/.ssh
|
cat > docker-compose.yml <<'EOF'
|
||||||
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
|
services:
|
||||||
chmod 600 ~/.ssh/id_rsa
|
app:
|
||||||
ssh-keyscan -H -t ed25519,rsa "$VPS_HOST" >> ~/.ssh/known_hosts
|
image: ${IMAGE_NAME}
|
||||||
|
container_name: imphenbot-app
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
volumes:
|
||||||
|
- ./recordings:/app/recordings
|
||||||
|
- ./.muxer-queue.db:/app/.muxer-queue.db
|
||||||
|
- ./.muxer-queue.db-shm:/app/.muxer-queue.db-shm
|
||||||
|
- ./.muxer-queue.db-wal:/app/.muxer-queue.db-wal
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.imphenbot.rule=Host(`imphnen.asepharyana.tech`)"
|
||||||
|
- "traefik.http.routers.imphenbot.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.imphenbot.tls=true"
|
||||||
|
- "traefik.http.services.imphenbot.loadbalancer.server.port=3000"
|
||||||
|
networks:
|
||||||
|
- app-shared-net
|
||||||
|
|
||||||
SSH_OPTS=(-o ControlMaster=auto -o ControlPath=/tmp/ssh-%r@%h:%p -o ControlPersist=600 -o StrictHostKeyChecking=yes)
|
networks:
|
||||||
|
app-shared-net:
|
||||||
|
name: app-shared-net
|
||||||
|
external: true
|
||||||
|
EOF
|
||||||
|
|
||||||
ssh "${SSH_OPTS[@]}" "$VPS_USER@$VPS_HOST" "mkdir -p $VPS_TARGET_DIR"
|
touch .muxer-queue.db .muxer-queue.db-shm .muxer-queue.db-wal
|
||||||
echo "$ENV_FILE_PRODUCTION" > .env.prod
|
mkdir -p recordings
|
||||||
scp "${SSH_OPTS[@]}" .env.prod "$VPS_USER@$VPS_HOST:$VPS_TARGET_DIR/.env"
|
|
||||||
|
|
||||||
echo "📦 Syncing GMW app files to VPS..."
|
echo "$GHCR_TOKEN" | docker login ghcr.io -u "$GHCR_USERNAME" --password-stdin
|
||||||
rsync -az --delete \
|
|
||||||
--exclude '.git' \
|
|
||||||
--exclude '.github' \
|
|
||||||
--exclude 'node_modules' \
|
|
||||||
--exclude 'dist' \
|
|
||||||
--exclude '.claude' \
|
|
||||||
--exclude 'logs' \
|
|
||||||
-e "ssh ${SSH_OPTS[*]}" \
|
|
||||||
./ "$VPS_USER@$VPS_HOST:$VPS_TARGET_DIR/"
|
|
||||||
|
|
||||||
ssh "${SSH_OPTS[@]}" "$VPS_USER@$VPS_HOST" "cd $VPS_TARGET_DIR && (docker compose up -d --build || docker-compose up -d --build)"
|
docker compose pull
|
||||||
|
docker compose up -d --force-recreate --remove-orphans
|
||||||
|
docker image prune -f
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
name: Build and Push Docker Image
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- master
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
GHCR_IMAGE: ghcr.io/mytheclipse/gmw
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-push:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- uses: docker/setup-buildx-action@v4
|
|
||||||
|
|
||||||
- name: Log in to GHCR
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.GHCR_IMAGE }}
|
|
||||||
tags: |
|
|
||||||
type=raw,value=latest
|
|
||||||
type=ref,event=tag
|
|
||||||
type=sha,prefix=sha-
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v7
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=registry,ref=${{ env.GHCR_IMAGE }}:buildcache
|
|
||||||
cache-to: type=registry,ref=${{ env.GHCR_IMAGE }}:buildcache,mode=max
|
|
||||||
provenance: false
|
|
||||||
sbom: false
|
|
||||||
+3
-3
@@ -70,7 +70,7 @@ const configSchema = z
|
|||||||
AI_LLM_BASE_URL: z
|
AI_LLM_BASE_URL: z
|
||||||
.string()
|
.string()
|
||||||
.url()
|
.url()
|
||||||
.default("https://9router.asepharyana.tech/v1"),
|
.default("https://9router.asepharyana.my.id/v1"),
|
||||||
/** Model used for text-only moderation (messages, badword analysis). */
|
/** Model used for text-only moderation (messages, badword analysis). */
|
||||||
AI_LLM_MODEL: z.string().default("text"),
|
AI_LLM_MODEL: z.string().default("text"),
|
||||||
/** Model used for image/video moderation (vision-capable model). */
|
/** Model used for image/video moderation (vision-capable model). */
|
||||||
@@ -147,9 +147,9 @@ const configSchema = z
|
|||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.transform((v) => v === "true")
|
.transform((v) => v === "true")
|
||||||
.default(true),
|
.default(false),
|
||||||
AUTO_DELETE_MIN_CONFIDENCE: z.coerce.number().min(0).max(1).default(0.5),
|
AUTO_DELETE_MIN_CONFIDENCE: z.coerce.number().min(0).max(1).default(0.5),
|
||||||
AUTO_DELETE_ALLOWED_SEVERITIES: z.string().default("critical,high,medium"),
|
AUTO_DELETE_ALLOWED_SEVERITIES: z.string().default("critical,high,medium,low"),
|
||||||
AUTO_DELETE_ALLOWED_CATEGORIES: z.string().default(""),
|
AUTO_DELETE_ALLOWED_CATEGORIES: z.string().default(""),
|
||||||
AUTO_DELETE_EXCLUDED_CHANNEL_IDS: z.string().default(""),
|
AUTO_DELETE_EXCLUDED_CHANNEL_IDS: z.string().default(""),
|
||||||
AUTO_DELETE_EXCLUDED_USER_IDS: z.string().default(""),
|
AUTO_DELETE_EXCLUDED_USER_IDS: z.string().default(""),
|
||||||
|
|||||||
Reference in New Issue
Block a user