ci: simplify Gitea admin deploy for act_runner
Some checks failed
Deploy admin to VPS / Build, push, and deploy admin (push) Has been cancelled
Some checks failed
Deploy admin to VPS / Build, push, and deploy admin (push) Has been cancelled
Single-job build/push/deploy plus nginx site install so self-hosted runners do not stall on multi-job graphs.
This commit is contained in:
parent
a307400f83
commit
ba1cf3efe2
@ -1,5 +1,7 @@
|
|||||||
name: Deploy admin to VPS
|
name: Deploy admin to VPS
|
||||||
|
|
||||||
|
# Single-job flow — Gitea act_runner is more reliable without multi-job graphs
|
||||||
|
# (same lesson as telegrambot deploy). Quality still runs on pull_request.
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
@ -9,62 +11,11 @@ concurrency:
|
|||||||
group: deploy-admin-production
|
group: deploy-admin-production
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
# PR merges: build + deploy only (quality ran on pull_request).
|
|
||||||
# Direct pushes to main: quality gate is skipped here (same as prior GitHub boot policy);
|
|
||||||
# quality still runs on pull_request via frontend-quality.yml.
|
|
||||||
jobs:
|
jobs:
|
||||||
gate:
|
deploy:
|
||||||
name: Detect direct push to main
|
name: Build, push, and deploy admin
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
timeout-minutes: 60
|
||||||
run_quality: ${{ steps.detect.outputs.run_quality }}
|
|
||||||
steps:
|
|
||||||
- id: detect
|
|
||||||
env:
|
|
||||||
# Via env — never interpolate commit text into the script body
|
|
||||||
# (backticks/`$()` in messages would otherwise become shell command substitution).
|
|
||||||
EVENT_NAME: ${{ github.event_name }}
|
|
||||||
COMMIT_MSG: ${{ github.event.head_commit.message || '' }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
|
|
||||||
echo "run_quality=false" >> "$GITHUB_OUTPUT"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
msg="$COMMIT_MSG"
|
|
||||||
if printf '%s' "$msg" | grep -qiE 'merge pull request #[0-9]+'; then
|
|
||||||
echo "run_quality=false" >> "$GITHUB_OUTPUT"
|
|
||||||
elif printf '%s' "$msg" | grep -qE '\(#[0-9]+\)[[:space:]]*$'; then
|
|
||||||
echo "run_quality=false" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
# Admin boot: deploy first; quality runs on pull_request workflow.
|
|
||||||
echo "Direct push to main — skipping quality gate for deploy."
|
|
||||||
echo "run_quality=false" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
quality:
|
|
||||||
needs: gate
|
|
||||||
if: needs.gate.outputs.run_quality == 'true'
|
|
||||||
uses: ./.gitea/workflows/frontend-quality.yml
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: read
|
|
||||||
|
|
||||||
build:
|
|
||||||
name: Build and push admin image
|
|
||||||
needs: [gate, quality]
|
|
||||||
if: >-
|
|
||||||
always() &&
|
|
||||||
needs.gate.result == 'success' &&
|
|
||||||
(needs.quality.result == 'success' || needs.quality.result == 'skipped')
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 45
|
|
||||||
outputs:
|
|
||||||
image: ${{ steps.meta.outputs.image }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -74,7 +25,6 @@ jobs:
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
owner="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')"
|
owner="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')"
|
||||||
repo="$(echo '${{ github.event.repository.name }}' | tr '[:upper:]' '[:lower:]')"
|
repo="$(echo '${{ github.event.repository.name }}' | tr '[:upper:]' '[:lower:]')"
|
||||||
# Gitea container registry (same host as git.ghabilee.ir)
|
|
||||||
echo "image=git.ghabilee.ir/${owner}/${repo}" >> "$GITHUB_OUTPUT"
|
echo "image=git.ghabilee.ir/${owner}/${repo}" >> "$GITHUB_OUTPUT"
|
||||||
echo "registry=git.ghabilee.ir" >> "$GITHUB_OUTPUT"
|
echo "registry=git.ghabilee.ir" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
@ -94,7 +44,24 @@ jobs:
|
|||||||
scp -i "$HOME/.ssh/vps_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new \
|
scp -i "$HOME/.ssh/vps_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new \
|
||||||
"${VPS_USER}@${VPS_HOST}:${env_path}" .env.production
|
"${VPS_USER}@${VPS_HOST}:${env_path}" .env.production
|
||||||
test -s .env.production
|
test -s .env.production
|
||||||
rm -f "$HOME/.ssh/vps_key"
|
|
||||||
|
- name: Install nginx site definition on VPS
|
||||||
|
env:
|
||||||
|
SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
|
||||||
|
VPS_HOST: ${{ secrets.VPS_HOST }}
|
||||||
|
VPS_USER: ${{ secrets.VPS_USER }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
SSH=(ssh -i "$HOME/.ssh/vps_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new)
|
||||||
|
scp -i "$HOME/.ssh/vps_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new \
|
||||||
|
deploy/nginx/backoffice.conf \
|
||||||
|
"${VPS_USER}@${VPS_HOST}:/etc/nginx/sites-available/backoffice"
|
||||||
|
"${SSH[@]}" "${VPS_USER}@${VPS_HOST}" '
|
||||||
|
set -eu
|
||||||
|
ln -sfn /etc/nginx/sites-available/backoffice /etc/nginx/sites-enabled/backoffice
|
||||||
|
nginx -t
|
||||||
|
systemctl reload nginx
|
||||||
|
'
|
||||||
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
@ -112,42 +79,16 @@ jobs:
|
|||||||
IMAGE_TAG: ${{ github.sha }}
|
IMAGE_TAG: ${{ github.sha }}
|
||||||
run: ./scripts/ci-build-image.sh
|
run: ./scripts/ci-build-image.sh
|
||||||
|
|
||||||
- name: Remove production build environment
|
- name: Deploy container on VPS
|
||||||
if: always()
|
|
||||||
run: rm -f .env.production
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
name: Stage or deploy admin
|
|
||||||
needs: build
|
|
||||||
if: ${{ !cancelled() && needs.build.result == 'success' }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 25
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: read
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Copy Compose definition and deploy
|
|
||||||
env:
|
env:
|
||||||
REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
REGISTRY_USER: ${{ github.actor }}
|
REGISTRY_USER: ${{ github.actor }}
|
||||||
REGISTRY_HOST: git.ghabilee.ir
|
REGISTRY_HOST: git.ghabilee.ir
|
||||||
ADMIN_IMAGE: ${{ needs.build.outputs.image }}:${{ github.sha }}
|
ADMIN_IMAGE: ${{ steps.meta.outputs.image }}:${{ github.sha }}
|
||||||
VPS_HOST: ${{ secrets.VPS_HOST }}
|
VPS_HOST: ${{ secrets.VPS_HOST }}
|
||||||
VPS_USER: ${{ secrets.VPS_USER }}
|
VPS_USER: ${{ secrets.VPS_USER }}
|
||||||
SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
|
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
install -m 700 -d "$HOME/.ssh"
|
|
||||||
printf '%s\n' "$SSH_KEY" > "$HOME/.ssh/vps_key"
|
|
||||||
chmod 600 "$HOME/.ssh/vps_key"
|
|
||||||
ssh -i "$HOME/.ssh/vps_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new \
|
|
||||||
"${VPS_USER}@${VPS_HOST}" '
|
|
||||||
set -eu
|
|
||||||
install -d -m 0750 /opt/ghabilee-admin
|
|
||||||
test -s /opt/ghabilee-admin/.env
|
|
||||||
'
|
|
||||||
scp -i "$HOME/.ssh/vps_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new \
|
scp -i "$HOME/.ssh/vps_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new \
|
||||||
deploy/docker-compose.production.yml \
|
deploy/docker-compose.production.yml \
|
||||||
"${VPS_USER}@${VPS_HOST}:/opt/ghabilee-admin/docker-compose.yml"
|
"${VPS_USER}@${VPS_HOST}:/opt/ghabilee-admin/docker-compose.yml"
|
||||||
@ -163,7 +104,6 @@ jobs:
|
|||||||
health="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' ghabilee-admin 2>/dev/null || echo missing)"
|
health="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' ghabilee-admin 2>/dev/null || echo missing)"
|
||||||
echo "Admin health ${attempt}/36: ${health}"
|
echo "Admin health ${attempt}/36: ${health}"
|
||||||
if [ "$health" = healthy ]; then
|
if [ "$health" = healthy ]; then
|
||||||
# فقط بعد از healthy: ایمیجهای unused (تگهای قبلی) را پاک کن؛ volumeها دست نخورند
|
|
||||||
if [ -x /opt/ghabilee/scripts/docker-prune.sh ]; then
|
if [ -x /opt/ghabilee/scripts/docker-prune.sh ]; then
|
||||||
/opt/ghabilee/scripts/docker-prune.sh full
|
/opt/ghabilee/scripts/docker-prune.sh full
|
||||||
else
|
else
|
||||||
@ -176,50 +116,23 @@ jobs:
|
|||||||
done
|
done
|
||||||
exit 1
|
exit 1
|
||||||
REMOTE
|
REMOTE
|
||||||
rm -f "$HOME/.ssh/vps_key"
|
|
||||||
|
|
||||||
notify-success:
|
- name: Notify Telegram
|
||||||
name: Notify Telegram (success)
|
if: always()
|
||||||
needs: deploy
|
|
||||||
# `quality` is intentionally skipped after PR merges. `success()` treats
|
|
||||||
# that skipped upstream job as non-success and would skip this job too.
|
|
||||||
if: ${{ always() && needs.deploy.result == 'success' }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Notify ops group of successful admin deploy
|
|
||||||
env:
|
env:
|
||||||
DEPLOY_SHA: ${{ github.sha }}
|
DEPLOY_SHA: ${{ github.sha }}
|
||||||
DEPLOY_STATUS: success
|
DEPLOY_STATUS: ${{ job.status }}
|
||||||
DEPLOY_COMMIT_SUBJECT: ${{ github.event.head_commit.message }}
|
DEPLOY_COMMIT_SUBJECT: ${{ github.event.head_commit.message }}
|
||||||
SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
|
SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
|
||||||
VPS_HOST: ${{ secrets.VPS_HOST }}
|
VPS_HOST: ${{ secrets.VPS_HOST }}
|
||||||
VPS_USER: ${{ secrets.VPS_USER }}
|
VPS_USER: ${{ secrets.VPS_USER }}
|
||||||
run: |
|
run: |
|
||||||
export DEPLOY_VERSION="$(node -p "require('./package.json').version")"
|
|
||||||
chmod +x scripts/notify-via-vps.sh scripts/notify-ops-telegram.sh scripts/notify-deploy.sh
|
|
||||||
./scripts/notify-via-vps.sh
|
|
||||||
|
|
||||||
notify-failed:
|
|
||||||
name: Notify Telegram (failed)
|
|
||||||
needs: [gate, quality, build, deploy]
|
|
||||||
if: failure()
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Notify ops group of failed admin deploy
|
|
||||||
env:
|
|
||||||
DEPLOY_SHA: ${{ github.sha }}
|
|
||||||
DEPLOY_STATUS: failed
|
|
||||||
DEPLOY_COMMIT_SUBJECT: ${{ github.event.head_commit.message }}
|
|
||||||
SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
|
|
||||||
VPS_HOST: ${{ secrets.VPS_HOST }}
|
|
||||||
VPS_USER: ${{ secrets.VPS_USER }}
|
|
||||||
run: |
|
|
||||||
# Do not gate on -x: notify-via-vps.sh may be 100644 in git; chmod first
|
|
||||||
# (the old `if [[ -x ... ]]` skipped the whole notify and still exited 0).
|
|
||||||
export DEPLOY_VERSION="$(node -p "require('./package.json').version" 2>/dev/null || echo '?')"
|
export DEPLOY_VERSION="$(node -p "require('./package.json').version" 2>/dev/null || echo '?')"
|
||||||
chmod +x scripts/notify-via-vps.sh scripts/notify-ops-telegram.sh scripts/notify-deploy.sh
|
# Map Gitea job.status to notify script expectations
|
||||||
./scripts/notify-via-vps.sh
|
case "${DEPLOY_STATUS}" in
|
||||||
|
success) export DEPLOY_STATUS=success ;;
|
||||||
|
*) export DEPLOY_STATUS=failed ;;
|
||||||
|
esac
|
||||||
|
chmod +x scripts/notify-via-vps.sh scripts/notify-ops-telegram.sh scripts/notify-deploy.sh || true
|
||||||
|
./scripts/notify-via-vps.sh || true
|
||||||
|
rm -f .env.production "$HOME/.ssh/vps_key"
|
||||||
|
|||||||
35
deploy/nginx/backoffice.conf
Normal file
35
deploy/nginx/backoffice.conf
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Admin backoffice — TLS terminated by Nginx, app on 127.0.0.1:3009
|
||||||
|
# DNS: point backoffice.ghabilee.ir at this VPS, then:
|
||||||
|
# certbot --nginx -d backoffice.ghabilee.ir
|
||||||
|
|
||||||
|
map $http_upgrade $connection_upgrade {
|
||||||
|
default upgrade;
|
||||||
|
'' close;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream ghabilee_admin {
|
||||||
|
server 127.0.0.1:3009;
|
||||||
|
keepalive 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name backoffice.ghabilee.ir;
|
||||||
|
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://ghabilee_admin;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user