refactor(ci): 移除所有第三方 uses action,改用纯 run 命令(规避 GitHub 下载超时)
This commit is contained in:
+244
-154
@@ -34,19 +34,26 @@ jobs:
|
|||||||
container:
|
container:
|
||||||
image: node:22-bookworm-slim
|
image: node:22-bookworm-slim
|
||||||
steps:
|
steps:
|
||||||
- name: 检出代码
|
- name: 安装基础运行依赖(git/ca-certificates)
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
|
|
||||||
- name: 运行时环境自检
|
|
||||||
shell: bash
|
|
||||||
run: |
|
run: |
|
||||||
set -euxo pipefail
|
set -euxo pipefail
|
||||||
|
apt-get update -qq
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -yqq --no-install-recommends \
|
||||||
|
git ca-certificates curl
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
git --version
|
||||||
node -v
|
node -v
|
||||||
npm -v
|
npm -v
|
||||||
which node npm
|
|
||||||
cat /etc/os-release 2>/dev/null | head -4 || true
|
- name: 检出代码
|
||||||
|
run: |
|
||||||
|
set -euxo pipefail
|
||||||
|
REPO_URL="https://${GITEA_HOST}/${GITHUB_REPOSITORY}.git"
|
||||||
|
BRANCH="${GITHUB_REF_NAME:-main}"
|
||||||
|
echo "Clone from: ${REPO_URL} (${BRANCH})"
|
||||||
|
rm -rf * .[^.]* 2>/dev/null || true
|
||||||
|
git clone --depth 1 -b "${BRANCH}" "${REPO_URL}" .
|
||||||
|
git rev-parse --short HEAD
|
||||||
|
|
||||||
- name: 安装依赖(npm ci)
|
- name: 安装依赖(npm ci)
|
||||||
run: |
|
run: |
|
||||||
@@ -84,13 +91,19 @@ jobs:
|
|||||||
image_latest: ${{ steps.meta.outputs.latest }}
|
image_latest: ${{ steps.meta.outputs.latest }}
|
||||||
steps:
|
steps:
|
||||||
- name: 检出代码
|
- name: 检出代码
|
||||||
uses: actions/checkout@v4
|
run: |
|
||||||
with:
|
set -euxo pipefail
|
||||||
fetch-depth: 1
|
REPO_URL="https://${GITEA_HOST}/${GITHUB_REPOSITORY}.git"
|
||||||
|
BRANCH="${GITHUB_REF_NAME:-main}"
|
||||||
|
echo "Clone from: ${REPO_URL} (${BRANCH})"
|
||||||
|
rm -rf * .[^.]* 2>/dev/null || true
|
||||||
|
git clone --depth 1 -b "${BRANCH}" "${REPO_URL}" .
|
||||||
|
git rev-parse --short HEAD
|
||||||
|
docker version
|
||||||
|
docker buildx version || echo "buildx 未安装,将使用 docker build --push"
|
||||||
|
|
||||||
- name: 计算镜像标签
|
- name: 计算镜像标签
|
||||||
id: meta
|
id: meta
|
||||||
shell: bash
|
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
SHA="${GITHUB_SHA:-$(git rev-parse HEAD)}"
|
SHA="${GITHUB_SHA:-$(git rev-parse HEAD)}"
|
||||||
@@ -107,54 +120,83 @@ jobs:
|
|||||||
echo "Latest image : ${IMG_LATEST}"
|
echo "Latest image : ${IMG_LATEST}"
|
||||||
echo "======================================"
|
echo "======================================"
|
||||||
|
|
||||||
- name: 启用 QEMU
|
- name: 初始化 Docker Buildx builder
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
with:
|
|
||||||
platforms: linux/amd64
|
|
||||||
|
|
||||||
- name: 启用 Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
with:
|
|
||||||
install: true
|
|
||||||
|
|
||||||
- name: 登录 Gitea 容器仓库
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.GITEA_HOST }}
|
|
||||||
username: ${{ secrets.REGISTRY_USER || github.actor }}
|
|
||||||
password: ${{ secrets.REGISTRY_TOKEN || github.token }}
|
|
||||||
|
|
||||||
- name: 构建并推送镜像
|
|
||||||
id: build_push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
env:
|
|
||||||
CACHE_REF: ${{ env.GITEA_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:buildcache
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
platforms: linux/amd64
|
|
||||||
push: true
|
|
||||||
provenance: false
|
|
||||||
tags: |
|
|
||||||
${{ steps.meta.outputs.full }}
|
|
||||||
${{ steps.meta.outputs.latest }}
|
|
||||||
build-args: |
|
|
||||||
BUILDKIT_INLINE_CACHE=1
|
|
||||||
NODE_ENV=production
|
|
||||||
cache-from: ${{ inputs.skip_build_cache == true && '""' || format('type=registry,ref={0}', env.CACHE_REF) }}
|
|
||||||
cache-to: ${{ inputs.skip_build_cache == true && '""' || format('type=registry,ref={0},mode=max', env.CACHE_REF) }}
|
|
||||||
labels: |
|
|
||||||
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
|
||||||
org.opencontainers.image.revision=${{ github.sha }}
|
|
||||||
org.opencontainers.image.created=${{ github.event.head_commit.timestamp }}
|
|
||||||
|
|
||||||
- name: 镜像体积与层信息
|
|
||||||
run: |
|
run: |
|
||||||
set -euxo pipefail
|
set -euxo pipefail
|
||||||
docker pull "${{ steps.meta.outputs.full }}" || true
|
BUILDER_NAME="multi"
|
||||||
docker image inspect "${{ steps.meta.outputs.full }}" --format='{{.Size}}' 2>/dev/null \
|
# 如果已存在同名 builder,直接用;否则创建(默认 driver docker-container,支持 --cache-to registry)
|
||||||
|
if ! docker buildx inspect "${BUILDER_NAME}" >/dev/null 2>&1; then
|
||||||
|
docker buildx create --name "${BUILDER_NAME}" --driver docker-container --use \
|
||||||
|
--buildkitd-flags '--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host'
|
||||||
|
else
|
||||||
|
docker buildx use "${BUILDER_NAME}"
|
||||||
|
fi
|
||||||
|
docker buildx inspect --bootstrap
|
||||||
|
|
||||||
|
- name: 登录 Gitea 容器仓库
|
||||||
|
env:
|
||||||
|
REGISTRY_USER: ${{ secrets.REGISTRY_USER || github.actor }}
|
||||||
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN || github.token }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [ -z "${REGISTRY_TOKEN}" ]; then
|
||||||
|
echo "ERROR: REGISTRY_TOKEN 为空。请在 Repository Secrets 中新建 REGISTRY_TOKEN(权限:read:package + write:package)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
printf '%s' "${REGISTRY_TOKEN}" | docker login "${GITEA_HOST}" \
|
||||||
|
-u "${REGISTRY_USER}" --password-stdin
|
||||||
|
echo "登录成功:${REGISTRY_USER} @ ${GITEA_HOST}"
|
||||||
|
|
||||||
|
- name: 构建并推送镜像
|
||||||
|
env:
|
||||||
|
CACHE_REF: ${{ env.GITEA_HOST }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:buildcache
|
||||||
|
SKIP_CACHE: ${{ inputs.skip_build_cache }}
|
||||||
|
IMG_FULL: ${{ steps.meta.outputs.full }}
|
||||||
|
IMG_LATEST: ${{ steps.meta.outputs.latest }}
|
||||||
|
OCS_SOURCE: ${{ github.server_url }}/${{ github.repository }}
|
||||||
|
OCS_REVISION: ${{ github.sha }}
|
||||||
|
OCS_CREATED: ${{ github.event.head_commit.timestamp }}
|
||||||
|
run: |
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
BUILD_ARGS=(
|
||||||
|
--build-arg BUILDKIT_INLINE_CACHE=1
|
||||||
|
--build-arg NODE_ENV=production
|
||||||
|
)
|
||||||
|
CACHE_ARGS=()
|
||||||
|
if [ "${SKIP_CACHE}" != "true" ]; then
|
||||||
|
CACHE_ARGS=(
|
||||||
|
--cache-from "type=registry,ref=${CACHE_REF}"
|
||||||
|
--cache-to "type=registry,ref=${CACHE_REF},mode=max"
|
||||||
|
)
|
||||||
|
else
|
||||||
|
echo "已启用 skip_build_cache,不使用分层缓存,强制重新构建"
|
||||||
|
BUILD_ARGS+=( --no-cache )
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker buildx build \
|
||||||
|
--context . \
|
||||||
|
--file ./Dockerfile \
|
||||||
|
--platform linux/amd64 \
|
||||||
|
--push \
|
||||||
|
--provenance=false \
|
||||||
|
"${BUILD_ARGS[@]}" \
|
||||||
|
"${CACHE_ARGS[@]}" \
|
||||||
|
--tag "${IMG_FULL}" \
|
||||||
|
--tag "${IMG_LATEST}" \
|
||||||
|
--label "org.opencontainers.image.source=${OCS_SOURCE}" \
|
||||||
|
--label "org.opencontainers.image.revision=${OCS_REVISION}" \
|
||||||
|
--label "org.opencontainers.image.created=${OCS_CREATED}"
|
||||||
|
|
||||||
|
- name: 镜像体积与层信息
|
||||||
|
env:
|
||||||
|
IMG_FULL: ${{ steps.meta.outputs.full }}
|
||||||
|
run: |
|
||||||
|
set -euxo pipefail
|
||||||
|
docker pull "${IMG_FULL}" || true
|
||||||
|
docker image inspect "${IMG_FULL}" --format='{{.Size}}' 2>/dev/null \
|
||||||
| awk '{printf "Image size: %.2f MB\n", $1/1024/1024}' || true
|
| awk '{printf "Image size: %.2f MB\n", $1/1024/1024}' || true
|
||||||
docker history --no-trunc "${{ steps.meta.outputs.full }}" 2>/dev/null | head -20 || true
|
docker history --no-trunc "${IMG_FULL}" 2>/dev/null | head -20 || true
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
name: 部署到生产服务器
|
name: 部署到生产服务器
|
||||||
@@ -167,121 +209,169 @@ jobs:
|
|||||||
url: ${{ secrets.NUXT_PUBLIC_SITE_URL }}
|
url: ${{ secrets.NUXT_PUBLIC_SITE_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: 校验部署所需密钥
|
- name: 校验部署所需密钥
|
||||||
shell: bash
|
env:
|
||||||
|
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||||
|
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
||||||
|
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
MISSING=()
|
MISSING=()
|
||||||
[ -n "${{ secrets.DEPLOY_HOST }}" ] || MISSING+=("DEPLOY_HOST")
|
[ -n "${DEPLOY_HOST}" ] || MISSING+=("DEPLOY_HOST")
|
||||||
[ -n "${{ secrets.DEPLOY_USER }}" ] || MISSING+=("DEPLOY_USER")
|
[ -n "${DEPLOY_USER}" ] || MISSING+=("DEPLOY_USER")
|
||||||
[ -n "${{ secrets.DEPLOY_SSH_KEY }}" ] || MISSING+=("DEPLOY_SSH_KEY")
|
[ -n "${DEPLOY_SSH_KEY}" ] || MISSING+=("DEPLOY_SSH_KEY")
|
||||||
if [ ${#MISSING[@]} -gt 0 ]; then
|
if [ ${#MISSING[@]} -gt 0 ]; then
|
||||||
echo "缺少以下 Repository Secrets:${MISSING[*]}"
|
echo "缺少以下 Repository Secrets:${MISSING[*]}"
|
||||||
echo "请在 Gitea 仓库 -> 设置 -> 工作流 -> 密钥 页面添加"
|
echo "请在 Gitea 仓库 -> 设置 -> 工作流 -> 密钥 页面添加(注意是 Repository Secrets,不是 Deploy Keys)"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "部署密钥配置完整"
|
echo "部署密钥配置完整(DEPLOY_HOST=${DEPLOY_HOST},DEPLOY_USER=${DEPLOY_USER})"
|
||||||
|
|
||||||
- name: SSH 滚动部署
|
- name: SSH 滚动部署
|
||||||
uses: appleboy/ssh-action@v1.2.0
|
env:
|
||||||
with:
|
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||||
host: ${{ secrets.DEPLOY_HOST }}
|
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT || 22 }}
|
||||||
port: ${{ secrets.DEPLOY_PORT || 22 }}
|
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
||||||
username: ${{ secrets.DEPLOY_USER }}
|
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
REGISTRY_PULL_USER: ${{ secrets.REGISTRY_PULL_USER }}
|
||||||
command_timeout: 30m
|
REGISTRY_PULL_TOKEN: ${{ secrets.REGISTRY_PULL_TOKEN }}
|
||||||
script_stop: true
|
NUXT_PUBLIC_SITE_URL: ${{ secrets.NUXT_PUBLIC_SITE_URL }}
|
||||||
script: |
|
IMAGE_NEW: ${{ needs.build-image.outputs.image_full }}
|
||||||
set -euxo pipefail
|
IMAGE_LATEST: ${{ needs.build-image.outputs.image_latest }}
|
||||||
|
GITHUB_SERVER_URL: ${{ github.server_url }}
|
||||||
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||||
|
GITHUB_REF_NAME: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
IMAGE_NEW="${{ needs.build-image.outputs.image_full }}"
|
# 写私钥到临时文件(避免 stdin 转义问题)
|
||||||
IMAGE_LATEST="${{ needs.build-image.outputs.image_latest }}"
|
SSH_KEY_FILE="$(mktemp -t deploy_ssh_key.XXXXXX)"
|
||||||
DEPLOY_DIR="${DEPLOY_PATH}"
|
printf '%s\n' "${DEPLOY_SSH_KEY}" > "${SSH_KEY_FILE}"
|
||||||
COMPOSE="docker compose"
|
chmod 600 "${SSH_KEY_FILE}"
|
||||||
|
|
||||||
echo "=============================================="
|
SSH_OPTS=(
|
||||||
echo "Deploy image : ${IMAGE_NEW}"
|
-i "${SSH_KEY_FILE}"
|
||||||
echo "Deploy dir : ${DEPLOY_DIR}"
|
-o StrictHostKeyChecking=no
|
||||||
echo "Executor : $(whoami)"
|
-o UserKnownHostsFile=/dev/null
|
||||||
echo "=============================================="
|
-o ConnectTimeout=10
|
||||||
|
-o ServerAliveInterval=30
|
||||||
|
-o ServerAliveCountMax=8
|
||||||
|
-p "${DEPLOY_PORT}"
|
||||||
|
)
|
||||||
|
|
||||||
mkdir -p "${DEPLOY_DIR}"
|
echo "=============================================="
|
||||||
cd "${DEPLOY_DIR}"
|
echo "Deploy target: ${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PORT}"
|
||||||
|
echo "Deploy image : ${IMAGE_NEW}"
|
||||||
|
echo "Deploy dir : ${DEPLOY_PATH}"
|
||||||
|
echo "=============================================="
|
||||||
|
|
||||||
if [ -n "${{ secrets.REGISTRY_PULL_TOKEN }}" ] && [ -n "${{ secrets.REGISTRY_PULL_USER }}" ]; then
|
# 本地预检查:ssh 先连上跑 whoami 确认连通,避免 20 分钟后才发现连不上
|
||||||
echo "使用专用 pull 凭证登录 Gitea Container Registry"
|
ssh "${SSH_OPTS[@]}" "${DEPLOY_USER}@${DEPLOY_HOST}" "whoami; docker --version; docker compose version"
|
||||||
echo "${{ secrets.REGISTRY_PULL_TOKEN }}" \
|
|
||||||
| docker login "${GITEA_HOST}" \
|
|
||||||
-u "${{ secrets.REGISTRY_PULL_USER }}" \
|
|
||||||
--password-stdin 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Pulling new image ${IMAGE_NEW} ..."
|
# 把变量作为 heredoc 脚本的输入传过去,远端展开我们需要的值
|
||||||
docker pull "${IMAGE_NEW}"
|
ssh "${SSH_OPTS[@]}" "${DEPLOY_USER}@${DEPLOY_HOST}" \
|
||||||
docker tag "${IMAGE_NEW}" "${IMAGE_LATEST}"
|
IMAGE_NEW="${IMAGE_NEW}" \
|
||||||
echo "New image pulled and tagged as latest"
|
IMAGE_LATEST="${IMAGE_LATEST}" \
|
||||||
|
DEPLOY_PATH="${DEPLOY_PATH}" \
|
||||||
|
GITEA_HOST="${GITEA_HOST}" \
|
||||||
|
REGISTRY_PULL_USER="${REGISTRY_PULL_USER}" \
|
||||||
|
REGISTRY_PULL_TOKEN="${REGISTRY_PULL_TOKEN}" \
|
||||||
|
IMAGE_NAME="${IMAGE_NAME}" \
|
||||||
|
NUXT_PUBLIC_SITE_URL="${NUXT_PUBLIC_SITE_URL}" \
|
||||||
|
GITHUB_SERVER_URL="${GITHUB_SERVER_URL}" \
|
||||||
|
GITHUB_REPOSITORY="${GITHUB_REPOSITORY}" \
|
||||||
|
GITHUB_REF_NAME="${GITHUB_REF_NAME}" \
|
||||||
|
bash <<'REMOTE_SCRIPT'
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
cat > docker-compose.override.yml <<YAML
|
DEPLOY_DIR="${DEPLOY_PATH}"
|
||||||
services:
|
COMPOSE="docker compose"
|
||||||
voicehub:
|
|
||||||
image: ${IMAGE_NEW}
|
|
||||||
YAML
|
|
||||||
echo "docker-compose.override.yml written, image=${IMAGE_NEW}"
|
|
||||||
|
|
||||||
if [ ! -f docker-compose.yml ]; then
|
echo "=============================================="
|
||||||
echo "docker-compose.yml 不存在,自动从仓库模板下载"
|
echo "Executor : $(whoami)"
|
||||||
TEMPLATE_URL="${GITHUB_SERVER_URL:-https://${GITEA_HOST}}/${GITHUB_REPOSITORY:-${GITHUB_REPOSITORY_OWNER}/${IMAGE_NAME}}/raw/branch/${GITHUB_REF_NAME:-main}/docker-compose.yml"
|
echo "Deploy image : ${IMAGE_NEW}"
|
||||||
echo "URL=${TEMPLATE_URL}"
|
echo "Deploy dir : ${DEPLOY_DIR}"
|
||||||
curl -fsSL --max-time 30 "${TEMPLATE_URL}" -o docker-compose.yml \
|
echo "=============================================="
|
||||||
|| (
|
|
||||||
echo "自动下载模板失败,请手动上传 docker-compose.yml 和 .env"
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f .env ]; then
|
mkdir -p "${DEPLOY_DIR}"
|
||||||
echo "================================================================="
|
cd "${DEPLOY_DIR}"
|
||||||
echo "WARNING:${DEPLOY_DIR}/.env 不存在"
|
|
||||||
echo "首次部署请在服务器上创建 .env(至少填写 POSTGRES_PASSWORD / JWT_SECRET / NUXT_PUBLIC_SITE_URL)"
|
|
||||||
echo "参考仓库根目录的 .env.example"
|
|
||||||
echo "================================================================="
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Executing docker compose up -d (voicehub only)..."
|
if [ -n "${REGISTRY_PULL_TOKEN}" ] && [ -n "${REGISTRY_PULL_USER}" ]; then
|
||||||
${COMPOSE} up -d --remove-orphans
|
echo "使用专用 pull 凭证登录 Gitea Container Registry"
|
||||||
|
printf '%s' "${REGISTRY_PULL_TOKEN}" \
|
||||||
MAX_WAIT=24
|
| docker login "${GITEA_HOST}" \
|
||||||
WAITED=0
|
-u "${REGISTRY_PULL_USER}" \
|
||||||
echo "Waiting for container healthcheck (max 120s)..."
|
--password-stdin 2>/dev/null || true
|
||||||
while [ ${WAITED} -lt ${MAX_WAIT} ]; do
|
|
||||||
STATUS="$(${COMPOSE} ps --format json 2>/dev/null || true)"
|
|
||||||
if echo "${STATUS}" | grep -q '"Health":"healthy"'; then
|
|
||||||
echo "voicehub healthcheck passed"
|
|
||||||
break
|
|
||||||
fi
|
fi
|
||||||
if echo "${STATUS}" | grep -q '"State":"running"'; then
|
|
||||||
:
|
echo "Pulling new image ${IMAGE_NEW} ..."
|
||||||
else
|
docker pull "${IMAGE_NEW}"
|
||||||
echo "voicehub not running, current status:"
|
docker tag "${IMAGE_NEW}" "${IMAGE_LATEST}"
|
||||||
${COMPOSE} ps || true
|
echo "New image pulled and tagged as latest"
|
||||||
|
|
||||||
|
cat > docker-compose.override.yml <<YAML
|
||||||
|
services:
|
||||||
|
voicehub:
|
||||||
|
image: ${IMAGE_NEW}
|
||||||
|
YAML
|
||||||
|
echo "docker-compose.override.yml written, image=${IMAGE_NEW}"
|
||||||
|
|
||||||
|
if [ ! -f docker-compose.yml ]; then
|
||||||
|
echo "docker-compose.yml 不存在,自动从仓库模板下载"
|
||||||
|
TEMPLATE_URL="${GITHUB_SERVER_URL:-https://${GITEA_HOST}}/${GITHUB_REPOSITORY}/raw/branch/${GITHUB_REF_NAME:-main}/docker-compose.yml"
|
||||||
|
echo "URL=${TEMPLATE_URL}"
|
||||||
|
curl -fsSL --max-time 30 "${TEMPLATE_URL}" -o docker-compose.yml \
|
||||||
|
|| echo "自动下载模板失败,请手动上传 docker-compose.yml 和 .env(参考仓库根目录 .env.example)"
|
||||||
fi
|
fi
|
||||||
sleep 5
|
|
||||||
WAITED=$((WAITED + 1))
|
|
||||||
done
|
|
||||||
if [ ${WAITED} -ge ${MAX_WAIT} ]; then
|
|
||||||
echo "Healthcheck timeout. Last 120 lines of voicehub logs:"
|
|
||||||
${COMPOSE} logs --tail=120 voicehub || true
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Pruning images older than 72 hours..."
|
if [ ! -f .env ]; then
|
||||||
docker image prune -af --filter "until=72h" || true
|
echo "================================================================="
|
||||||
docker builder prune -af || true
|
echo "WARNING:${DEPLOY_DIR}/.env 不存在"
|
||||||
|
echo "首次部署请在服务器上创建 .env(至少填写 POSTGRES_PASSWORD / JWT_SECRET / NUXT_PUBLIC_SITE_URL)"
|
||||||
|
echo "参考仓库根目录的 .env.example"
|
||||||
|
echo "================================================================="
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo "Executing docker compose up -d ..."
|
||||||
echo "================================================================="
|
${COMPOSE} up -d --remove-orphans
|
||||||
echo "Deploy succeeded. Visit: ${{ secrets.NUXT_PUBLIC_SITE_URL || '请查看你的站点 URL' }}"
|
|
||||||
echo "Rollback instructions (on server):"
|
MAX_WAIT=24
|
||||||
echo " cd ${DEPLOY_DIR}"
|
WAITED=0
|
||||||
echo " docker images | grep ${IMAGE_NAME} | head -5"
|
echo "Waiting for container healthcheck (max 120s)..."
|
||||||
echo " echo 'services:\n voicehub:\n image: ${GITEA_HOST}/${GITHUB_REPOSITORY_OWNER}/${IMAGE_NAME}:<OLD_TAG>' > docker-compose.override.yml"
|
while [ ${WAITED} -lt ${MAX_WAIT} ]; do
|
||||||
echo " docker compose up -d"
|
STATUS="$(${COMPOSE} ps --format json 2>/dev/null || true)"
|
||||||
echo "================================================================="
|
if echo "${STATUS}" | grep -q '"Health":"healthy"'; then
|
||||||
|
echo "voicehub healthcheck passed"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if echo "${STATUS}" | grep -q '"State":"running"'; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
echo "voicehub not running, current status:"
|
||||||
|
${COMPOSE} ps || true
|
||||||
|
fi
|
||||||
|
sleep 5
|
||||||
|
WAITED=$((WAITED + 1))
|
||||||
|
done
|
||||||
|
if [ ${WAITED} -ge ${MAX_WAIT} ]; then
|
||||||
|
echo "Healthcheck timeout. Last 120 lines of voicehub logs:"
|
||||||
|
${COMPOSE} logs --tail=120 voicehub || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Pruning images older than 72 hours..."
|
||||||
|
docker image prune -af --filter "until=72h" || true
|
||||||
|
docker builder prune -af || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "================================================================="
|
||||||
|
echo "Deploy succeeded. Visit: ${NUXT_PUBLIC_SITE_URL:-请自行查看你的站点 URL}"
|
||||||
|
echo "Rollback instructions (on server):"
|
||||||
|
echo " cd ${DEPLOY_DIR}"
|
||||||
|
echo " docker images | grep ${IMAGE_NAME} | head -5"
|
||||||
|
echo " printf 'services:\\n voicehub:\\n image: <OLD_TAG_FROM_ABOVE>\\n' > docker-compose.override.yml"
|
||||||
|
echo " docker compose up -d"
|
||||||
|
echo "================================================================="
|
||||||
|
REMOTE_SCRIPT
|
||||||
|
|
||||||
|
# 清理私钥临时文件
|
||||||
|
rm -f "${SSH_KEY_FILE}"
|
||||||
|
|||||||
Reference in New Issue
Block a user