All checks were successful
Build docker and publish / prepare (20.15.1) (push) Successful in 10s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Successful in 4m16s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Successful in 7m46s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Successful in 4m24s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Successful in 4m9s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.rpc-core image_name:ppanel-rpc-core name:rpc-core]) (push) Successful in 8m14s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Successful in 3m52s
Build docker and publish / deploy (push) Successful in 37s
Build docker and publish / notify (push) Successful in 4s
351 lines
14 KiB
YAML
351 lines
14 KiB
YAML
name: Build docker and publish
|
||
run-name: zero-ppanel Docker构建和部署
|
||
|
||
on:
|
||
push:
|
||
branches:
|
||
- main
|
||
- dev
|
||
pull_request:
|
||
branches:
|
||
- main
|
||
- dev
|
||
|
||
env:
|
||
REPO: ${{ vars.REPO || 'registry.kxsw.us/vpn-server' }}
|
||
SSH_HOST: ${{ github.ref_name == 'main' && vars.SSH_HOST || vars.DEV_SSH_HOST }}
|
||
SSH_PORT: ${{ vars.SSH_PORT }}
|
||
SSH_USER: ${{ vars.SSH_USER }}
|
||
SSH_PASSWORD: ${{ github.ref_name == 'main' && vars.SSH_PASSWORD || vars.DEV_SSH_PASSWORD }}
|
||
TG_BOT_TOKEN: 8114337882:AAHkEx03HSu7RxN4IHBJJEnsK9aPPzNLIk0
|
||
TG_CHAT_ID: "-4940243803"
|
||
VERSION: ${{ github.sha }}
|
||
BUILDTIME: ${{ github.event.head_commit.timestamp }}
|
||
|
||
jobs:
|
||
# ============================================================
|
||
# Job 1: 设置环境变量,供后续 jobs 共享
|
||
# ============================================================
|
||
prepare:
|
||
runs-on: zero-ppanel-server
|
||
container:
|
||
image: node:20
|
||
strategy:
|
||
matrix:
|
||
# 只有node支持版本号别名
|
||
node: ['20.15.1']
|
||
outputs:
|
||
docker_tag: ${{ steps.vars.outputs.docker_tag }}
|
||
container_suffix: ${{ steps.vars.outputs.container_suffix }}
|
||
deploy_path: ${{ steps.vars.outputs.deploy_path }}
|
||
has_changes: ${{ steps.changes.outputs.has_changes }}
|
||
changed_services: ${{ steps.changes.outputs.changed_services }}
|
||
deploy_services: ${{ steps.changes.outputs.deploy_services }}
|
||
steps:
|
||
- name: ⚙️ 计算部署变量
|
||
id: vars
|
||
run: |
|
||
case "${{ github.ref_name }}" in
|
||
main)
|
||
echo "docker_tag=latest" >> $GITHUB_OUTPUT
|
||
echo "container_suffix=" >> $GITHUB_OUTPUT
|
||
echo "deploy_path=/root/bindbox" >> $GITHUB_OUTPUT
|
||
;;
|
||
dev)
|
||
echo "docker_tag=dev" >> $GITHUB_OUTPUT
|
||
echo "container_suffix=-dev" >> $GITHUB_OUTPUT
|
||
echo "deploy_path=/root/bindbox-dev" >> $GITHUB_OUTPUT
|
||
;;
|
||
*)
|
||
echo "docker_tag=${{ github.ref_name }}" >> $GITHUB_OUTPUT
|
||
echo "container_suffix=-${{ github.ref_name }}" >> $GITHUB_OUTPUT
|
||
echo "deploy_path=/root/vpn_server_other" >> $GITHUB_OUTPUT
|
||
;;
|
||
esac
|
||
|
||
- name: 📥 下载代码
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
|
||
- name: 🔍 检测变更服务
|
||
id: changes
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
|
||
BEFORE="${{ github.event.before }}"
|
||
SHA="${{ github.sha }}"
|
||
RANGE=""
|
||
|
||
if [ -n "${BEFORE}" ] && [ "${BEFORE}" != "0000000000000000000000000000000000000000" ]; then
|
||
RANGE="${BEFORE}..${SHA}"
|
||
elif git rev-parse HEAD~1 >/dev/null 2>&1; then
|
||
RANGE="HEAD~1..HEAD"
|
||
else
|
||
RANGE="HEAD"
|
||
fi
|
||
|
||
if [ "${RANGE}" = "HEAD" ]; then
|
||
git show --pretty=format: --name-only HEAD | sed '/^$/d' > changed_files.txt
|
||
else
|
||
git diff --name-only "${RANGE}" > changed_files.txt
|
||
fi
|
||
|
||
echo "Changed files:"
|
||
cat changed_files.txt || true
|
||
|
||
has_file() {
|
||
grep -Eq "$1" changed_files.txt
|
||
}
|
||
|
||
add_service() {
|
||
local service="$1"
|
||
if [[ " ${services[*]} " != *" ${service} "* ]]; then
|
||
services+=("${service}")
|
||
fi
|
||
}
|
||
|
||
all=0
|
||
if has_file '^(go\.mod|go\.sum|pkg/|sql/|deploy/docker-compose\.cloud\.yml|deploy/docker-compose-env\.yml|deploy/\.env\.example|\.gitea/workflows/deploy\.yml)'; then
|
||
all=1
|
||
fi
|
||
|
||
services=()
|
||
if [ "${all}" -eq 1 ]; then
|
||
services=(rpc-core api admin node queue scheduler)
|
||
else
|
||
if has_file '^apps/rpc/' || has_file '^deploy/Dockerfile.rpc-core$' || has_file '^deploy/etc/core/'; then
|
||
add_service "rpc-core"
|
||
fi
|
||
if has_file '^apps/api/' || has_file '^deploy/Dockerfile.api$' || has_file '^deploy/etc/api/'; then
|
||
add_service "api"
|
||
fi
|
||
if has_file '^apps/admin/' || has_file '^deploy/Dockerfile.admin$' || has_file '^deploy/etc/admin/'; then
|
||
add_service "admin"
|
||
fi
|
||
if has_file '^apps/node/' || has_file '^deploy/Dockerfile.node$' || has_file '^deploy/etc/node/'; then
|
||
add_service "node"
|
||
fi
|
||
if has_file '^apps/queue/' || has_file '^deploy/Dockerfile.queue$' || has_file '^deploy/etc/queue/'; then
|
||
add_service "queue"
|
||
fi
|
||
if has_file '^apps/scheduler/' || has_file '^deploy/Dockerfile.scheduler$' || has_file '^deploy/etc/scheduler/'; then
|
||
add_service "scheduler"
|
||
fi
|
||
fi
|
||
|
||
if [ "${#services[@]}" -eq 0 ]; then
|
||
echo "No service changes detected, skip build/deploy."
|
||
echo "has_changes=false" >> "$GITHUB_OUTPUT"
|
||
echo "changed_services=" >> "$GITHUB_OUTPUT"
|
||
echo "deploy_services=" >> "$GITHUB_OUTPUT"
|
||
exit 0
|
||
fi
|
||
|
||
deploy_services=()
|
||
for service in "${services[@]}"; do
|
||
case "${service}" in
|
||
rpc-core) deploy_services+=("ppanel-rpc-core") ;;
|
||
api) deploy_services+=("ppanel-api") ;;
|
||
admin) deploy_services+=("ppanel-admin") ;;
|
||
node) deploy_services+=("ppanel-node") ;;
|
||
queue) deploy_services+=("ppanel-queue") ;;
|
||
scheduler) deploy_services+=("ppanel-scheduler") ;;
|
||
esac
|
||
done
|
||
|
||
changed_services="$(IFS=,; echo "${services[*]}")"
|
||
deploy_services_str="${deploy_services[*]}"
|
||
|
||
echo "has_changes=true" >> "$GITHUB_OUTPUT"
|
||
echo "changed_services=${changed_services}" >> "$GITHUB_OUTPUT"
|
||
echo "deploy_services=${deploy_services_str}" >> "$GITHUB_OUTPUT"
|
||
|
||
echo "Will build services: ${changed_services}"
|
||
echo "Will deploy services: ${deploy_services_str}"
|
||
|
||
# ============================================================
|
||
# Job 2: 并行矩阵构建 6 个服务镜像 - 💥 重点修改这里 💥
|
||
# ============================================================
|
||
build:
|
||
runs-on: zero-ppanel-server
|
||
container: # <-- 整个 build job 在 Node.js 容器中运行
|
||
image: node:20.15.1
|
||
needs: prepare
|
||
if: needs.prepare.outputs.has_changes == 'true' && contains(needs.prepare.outputs.changed_services, matrix.service.name)
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
service:
|
||
- name: rpc-core
|
||
dockerfile: deploy/Dockerfile.rpc-core
|
||
image_name: ppanel-rpc-core
|
||
- name: api
|
||
dockerfile: deploy/Dockerfile.api
|
||
image_name: ppanel-api
|
||
- name: admin
|
||
dockerfile: deploy/Dockerfile.admin
|
||
image_name: ppanel-admin
|
||
- name: node
|
||
dockerfile: deploy/Dockerfile.node
|
||
image_name: ppanel-node
|
||
- name: queue
|
||
dockerfile: deploy/Dockerfile.queue
|
||
image_name: ppanel-queue
|
||
- name: scheduler
|
||
dockerfile: deploy/Dockerfile.scheduler
|
||
image_name: ppanel-scheduler
|
||
|
||
steps:
|
||
- name: 📥 下载代码
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Set up Go environment # 在 build job 中也设置 Go 环境
|
||
uses: actions/setup-go@v2
|
||
with:
|
||
go-version: '1.24.0' # 确保使用 go.mod 中指定的精确版本
|
||
# cache: true # 可以启用缓存加速依赖下载
|
||
|
||
- name: 🔧 确保 Docker CLI 可用并初始化 Go Modules
|
||
run: |
|
||
set -e
|
||
export DEBIAN_FRONTEND=noninteractive
|
||
apt-get update -y
|
||
apt-get install -y ca-certificates curl gnupg
|
||
|
||
# !!! 在 node 容器中安装 docker-ce-cli !!!
|
||
curl -fsSL https://download.docker.com/linux/debian/gpg \
|
||
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] \
|
||
https://download.docker.com/linux/debian \
|
||
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
|
||
> /etc/apt/sources.list.d/docker.list
|
||
apt-get update -y
|
||
apt-get install -y docker-ce-cli # <<-- 确保 docker CLI 被安装
|
||
|
||
echo "Docker CLI 版本: $(docker --version)"
|
||
echo "Test Docker connectivity: $(docker info --format '{{.ServerVersion}}') (Server)"
|
||
|
||
# 移除手动 Go 安装,已由 actions/setup-go 处理
|
||
# go version # 已经通过 actions/setup-go 设置
|
||
go env -w GOPROXY=https://goproxy.cn,direct # 设置 Go Proxy
|
||
go mod download # 确保所有模块已下载
|
||
|
||
- name: 📦 构建并推送 Docker 镜像
|
||
run: |
|
||
set -e
|
||
IMAGE_NAME="${{ matrix.service.image_name }}"
|
||
DOCKERFILE="${{ matrix.service.dockerfile }}"
|
||
DEPLOY_TAG="${{ needs.prepare.outputs.docker_tag }}"
|
||
REPO="${{ env.REPO }}"
|
||
|
||
FULL_IMAGE_NAME="${REPO}/${IMAGE_NAME}:${DEPLOY_TAG}"
|
||
|
||
echo "🚀 开始构建镜像: ${FULL_IMAGE_NAME} 从 ${DOCKERFILE}"
|
||
docker build --no-cache -t "${FULL_IMAGE_NAME}" -f "${DOCKERFILE}" .
|
||
|
||
echo "⬆️ 推送镜像: ${FULL_IMAGE_NAME}"
|
||
docker push "${FULL_IMAGE_NAME}"
|
||
|
||
|
||
# ============================================================
|
||
# Job 3: 部署到服务器
|
||
# ============================================================
|
||
deploy:
|
||
runs-on: zero-ppanel-server
|
||
container: # <-- 新增:deploy job 也在 Node.js 容器中运行
|
||
image: node:20.15.1
|
||
needs: [prepare, build]
|
||
# PR 不触发部署,只有直接推送才部署
|
||
if: github.event_name == 'push' && needs.prepare.outputs.has_changes == 'true'
|
||
|
||
steps:
|
||
- name: 📥 下载代码 (获取 docker-compose.cloud.yml)
|
||
uses: actions/checkout@v4
|
||
|
||
- name: 📂 传输 docker-compose.cloud.yml
|
||
uses: appleboy/scp-action@v0.1.7
|
||
with:
|
||
host: ${{ env.SSH_HOST }}
|
||
username: ${{ env.SSH_USER }}
|
||
password: ${{ env.SSH_PASSWORD }}
|
||
port: ${{ env.SSH_PORT }}
|
||
source: "deploy/docker-compose.cloud.yml,deploy/etc,deploy/.env.example"
|
||
target: "${{ needs.prepare.outputs.deploy_path }}/"
|
||
strip_components: 1
|
||
|
||
- name: 🚀 部署服务
|
||
uses: appleboy/ssh-action@v1.0.3
|
||
with:
|
||
host: ${{ env.SSH_HOST }}
|
||
username: ${{ env.SSH_USER }}
|
||
password: ${{ env.SSH_PASSWORD }}
|
||
port: ${{ env.SSH_PORT }}
|
||
timeout: 300s
|
||
command_timeout: 600s
|
||
script: |
|
||
set -e
|
||
DEPLOY_PATH="${{ needs.prepare.outputs.deploy_path }}"
|
||
DOCKER_TAG="${{ needs.prepare.outputs.docker_tag }}"
|
||
REPO="${{ env.REPO }}"
|
||
DEPLOY_SERVICES="${{ needs.prepare.outputs.deploy_services }}"
|
||
|
||
echo "部署目录: ${DEPLOY_PATH}"
|
||
echo "镜像标签: ${DOCKER_TAG}"
|
||
|
||
cd ${DEPLOY_PATH}
|
||
|
||
# 保留已有业务配置,仅更新镜像仓库和标签
|
||
touch .env
|
||
grep -q '^PPANEL_TAG=' .env \
|
||
&& sed -i "s|^PPANEL_TAG=.*|PPANEL_TAG=${DOCKER_TAG}|" .env \
|
||
|| echo "PPANEL_TAG=${DOCKER_TAG}" >> .env
|
||
grep -q '^PPANEL_REPO=' .env \
|
||
&& sed -i "s|^PPANEL_REPO=.*|PPANEL_REPO=${REPO}|" .env \
|
||
|| echo "PPANEL_REPO=${REPO}" >> .env
|
||
|
||
if [ -z "${DEPLOY_SERVICES}" ]; then
|
||
echo "没有服务变更,跳过部署。"
|
||
exit 0
|
||
fi
|
||
|
||
# 拉取所有服务的最新镜像
|
||
docker-compose -f docker-compose.cloud.yml pull ${DEPLOY_SERVICES}
|
||
|
||
# 滚动更新所有 ppanel 服务
|
||
docker-compose -f docker-compose.cloud.yml up -d ${DEPLOY_SERVICES}
|
||
|
||
# 清理旧镜像
|
||
docker image prune -f || true
|
||
|
||
echo "✅ 部署完成,当前运行容器:"
|
||
docker-compose -f docker-compose.cloud.yml ps
|
||
|
||
# ============================================================
|
||
# Job 4: 通知
|
||
# ============================================================
|
||
notify:
|
||
runs-on: zero-ppanel-server
|
||
needs: [prepare, build, deploy]
|
||
if: always() && github.event_name == 'push'
|
||
steps:
|
||
- name: 📱 发送 Telegram 通知
|
||
uses: appleboy/telegram-action@master
|
||
with:
|
||
token: ${{ env.TG_BOT_TOKEN }}
|
||
to: ${{ env.TG_CHAT_ID }}
|
||
message: |
|
||
${{ needs.prepare.outputs.has_changes != 'true' && '⏭️ 无服务变更,已跳过构建与部署。' || ((needs.build.result == 'success' && needs.deploy.result == 'success') && '✅ 部署成功!' || '❌ 部署失败!') }}
|
||
|
||
📦 项目: zero-ppanel
|
||
🌿 分支: ${{ github.ref_name }}
|
||
🏷️ 标签: ${{ needs.prepare.outputs.docker_tag }}
|
||
📝 提交: `${{ github.sha }}`
|
||
👤 提交者: ${{ github.actor }}
|
||
🕐 时间: ${{ github.event.head_commit.timestamp }}
|
||
|
||
构建: ${{ needs.prepare.outputs.has_changes != 'true' && 'skipped(no changes)' || needs.build.result }} | 部署: ${{ needs.prepare.outputs.has_changes != 'true' && 'skipped(no changes)' || needs.deploy.result }}
|
||
${{ (needs.prepare.outputs.has_changes == 'true' && (needs.build.result != 'success' || needs.deploy.result != 'success')) && '⚠️ 请检查 Actions 日志获取详细信息' || '' }}
|
||
parse_mode: Markdown
|