#!/usr/bin/env bash # 自动发版:git pull + remote-release(由 webhook 或手动触发) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ENV_FILE="$SCRIPT_DIR/auto-release.env" APP_ROOT="${APP_ROOT:-/opt/dukang}" GIT_REMOTE="${GIT_REMOTE:-origin}" GIT_BRANCH="${GIT_BRANCH:-dev}" LOCK_FILE="${DEPLOY_LOCK_FILE:-/var/run/dukang-deploy.lock}" PENDING_FILE="${DEPLOY_PENDING_FILE:-/var/run/dukang-deploy.pending}" LOG_FILE="${DEPLOY_LOG_FILE:-/var/log/dukang/deploy.log}" WEBHOOK_FETCH_RETRIES="${WEBHOOK_FETCH_RETRIES:-5}" WEBHOOK_FETCH_DELAY_SEC="${WEBHOOK_FETCH_DELAY_SEC:-3}" if [[ -f "$ENV_FILE" ]]; then # shellcheck disable=SC1090 source <(sed 's/\r$//' "$ENV_FILE") fi mkdir -p "$(dirname "$LOG_FILE")" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" } exec 9>"$LOCK_FILE" if ! flock -n 9; then if [[ -n "${DEPLOY_TRIGGER:-}" ]]; then date '+%s' >"$PENDING_FILE" log "QUEUED: another deploy is in progress (webhook will retry after current run)" else log "SKIP: another deploy is in progress" fi exit 0 fi log "START auto-release (branch=$GIT_BRANCH)" cd "$APP_ROOT" if [[ ! -d .git ]]; then log "ERROR: $APP_ROOT is not a git repository" exit 1 fi # 统一远程仓库为 SSH 地址 GIT_REPO_URL="${GIT_REPO_URL:-git@git.yqidian.com:jacy/dukang.git}" CURRENT_URL="$(git remote get-url "$GIT_REMOTE" 2>/dev/null || true)" if [[ "$CURRENT_URL" != "$GIT_REPO_URL" ]]; then log "SET remote $GIT_REMOTE -> $GIT_REPO_URL (was: ${CURRENT_URL:-none})" git remote set-url "$GIT_REMOTE" "$GIT_REPO_URL" 2>/dev/null \ || git remote add "$GIT_REMOTE" "$GIT_REPO_URL" fi git fetch "$GIT_REMOTE" git checkout "$GIT_BRANCH" LOCAL_SHA="$(git rev-parse HEAD)" REMOTE_SHA="$(git rev-parse "$GIT_REMOTE/$GIT_BRANCH")" # CodeUp 常在 push 完成前连发 webhook,首次 fetch 可能仍拿到旧 SHA if [[ "$LOCAL_SHA" == "$REMOTE_SHA" ]] && [[ -n "${DEPLOY_TRIGGER:-}" ]]; then for ((i = 1; i <= WEBHOOK_FETCH_RETRIES; i++)); do log "WEBHOOK retry fetch $i/$WEBHOOK_FETCH_RETRIES (waiting ${WEBHOOK_FETCH_DELAY_SEC}s for remote)" sleep "$WEBHOOK_FETCH_DELAY_SEC" git fetch "$GIT_REMOTE" REMOTE_SHA="$(git rev-parse "$GIT_REMOTE/$GIT_BRANCH")" if [[ "$LOCAL_SHA" != "$REMOTE_SHA" ]]; then break fi done fi if [[ "$LOCAL_SHA" == "$REMOTE_SHA" ]]; then log "SKIP: already up to date ($LOCAL_SHA)" exit 0 fi log "PULL $LOCAL_SHA -> $REMOTE_SHA" # 保留服务器生产密钥,避免 reset 覆盖 ENV_BACKUP="$(mktemp)" API_ENV_PROD_BACKUP="$(mktemp)" cp "$ENV_FILE" "$ENV_BACKUP" 2>/dev/null || true cp "$APP_ROOT/server/dukang-api/.env.production" "$API_ENV_PROD_BACKUP" 2>/dev/null || true git checkout "$GIT_BRANCH" git reset --hard "$GIT_REMOTE/$GIT_BRANCH" cp "$ENV_BACKUP" "$ENV_FILE" 2>/dev/null || true cp "$API_ENV_PROD_BACKUP" "$APP_ROOT/server/dukang-api/.env.production" 2>/dev/null || true rm -f "$ENV_BACKUP" "$API_ENV_PROD_BACKUP" sed -i 's/\r$//' "$ENV_FILE" 2>/dev/null || true # 修复 Windows 换行 find "$APP_ROOT/deploy" -maxdepth 1 -name '*.sh' -exec sed -i 's/\r$//' {} + 2>/dev/null || true log "RUN remote-release.sh" RELEASE_ARGS=() if [[ "${PRISMA_ACCEPT_DATA_LOSS:-false}" == "true" ]]; then RELEASE_ARGS+=(--accept-data-loss) fi bash "$APP_ROOT/deploy/remote-release.sh" "${RELEASE_ARGS[@]}" log "DONE auto-release ($REMOTE_SHA)" if [[ -f "$PENDING_FILE" ]]; then rm -f "$PENDING_FILE" log "RETRY: running queued webhook deploy" DEPLOY_TRIGGER="${DEPLOY_TRIGGER:-queued}" bash "$0" fi