宝塔 WebHook 自动化部署脚本

用www用户成个ssh key

sudo -u www ssh-keygen -t rsa -C "your_email@example.com"

自动部署shell脚本

#!/bin/bash
set -euo pipefail

exec > >(tee -a /tmp/deploy.log) 2>&1

#项目要放在宝塔的文件夹路径
APP_DIR="" 
#GitHub ssh链接
REPO_SSH=""
# 仓库分支名称
BRANCH="master"
# 要运行的用户
RUN_USER="www"
#sshkey pub文件路径 
SSH_KEY=""
# 钉钉机器人通知配置
DINGTALK_WEBHOOK=""

# ========= 输出美化 =========
if [[ -t 1 ]] && command -v tput >/dev/null 2>&1; then
  C_RESET="$(tput sgr0)"
  C_DIM="$(tput dim)"
  C_BOLD="$(tput bold)"
  C_RED="$(tput setaf 1)"
  C_GREEN="$(tput setaf 2)"
  C_YELLOW="$(tput setaf 3)"
  C_BLUE="$(tput setaf 4)"
  C_CYAN="$(tput setaf 6)"
else
  C_RESET=""; C_DIM=""; C_BOLD=""
  C_RED=""; C_GREEN=""; C_YELLOW=""; C_BLUE=""; C_CYAN=""
fi

hr() { echo -e "${C_DIM}────────────────────────────────────────────────────────${C_RESET}"; }
ts() { date "+%Y-%m-%d %H:%M:%S"; }

log_title() {
  hr
  echo -e "${C_BOLD}${C_CYAN}🚀 Deploy${C_RESET}  $(ts)"
  hr
}

log_kv() { printf "  ${C_DIM}%-10s${C_RESET} %s\n" "$1" "$2"; }

log_step() {
  STEP_NAME="$1"
  STEP_START="$(date +%s)"
  hr
  echo -e "${C_BOLD}${C_BLUE}▶ ${STEP_NAME}${C_RESET}"
}

log_ok()   { echo -e "  ${C_GREEN}✔${C_RESET} $*"; }
log_warn() { echo -e "  ${C_YELLOW}⚠${C_RESET} $*"; }
log_err()  { echo -e "  ${C_RED}✘${C_RESET} $*"; }

log_done_step() {
  local end
  end="$(date +%s)"
  local cost=$(( end - STEP_START ))
  echo -e "  ${C_GREEN}✓ 完成${C_RESET} (${cost}s)"
}

DEPLOY_STATUS="success"
DEPLOY_START_TIME=$(date +%s)

trap 'log_err "发生错误:第 ${LINENO} 行执行失败"; echo -e "  ${C_DIM}命令:${BASH_COMMAND}${C_RESET}"; hr; DEPLOY_STATUS="failed"; exit 1' ERR

# ========= 业务函数 =========
run_as_user() {
  sudo -n -u "${RUN_USER}" -H bash -c "$*"
}

git_as_user() {
  sudo -n -u "${RUN_USER}" -H bash -c \
    "GIT_SSH_COMMAND='ssh -i ${SSH_KEY} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new' git $*"
}

send_dingtalk_notification() {
  local status="$1"
  local message="$2"
  local duration="$3"

  if [[ -z "${DINGTALK_WEBHOOK}" ]] || [[ "${DINGTALK_WEBHOOK}" == *"YOUR_ACCESS_TOKEN"* ]]; then
    log_warn "未配置钉钉 Webhook,跳过通知"
    return 0
  fi

  local title="✅ 部署成功"
  if [[ "${status}" == "failed" ]]; then
    title="❌ 部署失败"
  fi

  local markdown_text="#### ${title}
> **项目**: python_ani_search
> **分支**: ${BRANCH}
> **状态**: ${status}
> **耗时**: ${duration}s
> **时间**: $(date '+%Y-%m-%d %H:%M:%S')

${message}"

  local response
  response=$(curl -s -w "\n%{http_code}" -X POST "${DINGTALK_WEBHOOK}" \
    -H 'Content-Type: application/json' \
    -d "{
      \"msgtype\": \"markdown\",
      \"markdown\": {
        \"title\": \"${title}\",
        \"text\": \"${markdown_text}\"
      },
      \"at\": {
        \"isAtAll\": true
      }
    }" 2>/dev/null)

  local http_code
  http_code=$(echo "$response" | tail -n1)

  local body
  body=$(echo "$response" | sed '$d')

  if [[ "${http_code}" == "200" ]]; then
    local errcode
    errcode=$(echo "$body" | python3 -c "import sys,json;print(json.load(sys.stdin).get('errcode',-1))" 2>/dev/null || echo "-1")

    if [[ "${errcode}" == "0" ]]; then
      log_ok "钉钉通知已发送"
    else
      log_warn "钉钉通知发送失败:errcode=${errcode}"
    fi
  else
    log_warn "钉钉通知发送失败:http_code=${http_code}"
  fi
}

# ========= 主流程 =========
log_title
echo -e "${C_BOLD}Start${C_RESET}"
log_kv "项目目录:" "${APP_DIR}"
log_kv "仓库地址:" "${REPO_SSH}"
log_kv "分支:" "${BRANCH}"
log_kv "执行用户:" "${RUN_USER}"
log_kv "SSH Key:" "${SSH_KEY}"

log_step "Step0: 检查 SSH Key"
if [ ! -f "${SSH_KEY}" ]; then
  log_err "SSH Key 不存在 -> ${SSH_KEY}"
  exit 1
fi

chown "${RUN_USER}:${RUN_USER}" "${SSH_KEY}"
chmod 600 "${SSH_KEY}"
log_ok "SSH Key 存在并已修正权限"
log_done_step

log_step "Step1: 检查项目目录"
if [ ! -d "${APP_DIR}" ]; then
  log_warn "项目目录不存在,开始创建 -> ${APP_DIR}"
  mkdir -p "${APP_DIR}"
  chown -R "${RUN_USER}:${RUN_USER}" "${APP_DIR}"
fi
log_ok "目录存在"
log_done_step

log_step "Step2: 拉取/更新代码"
cd "${APP_DIR}"

if [ ! -d "${APP_DIR}/.git" ]; then
  log_warn "未初始化 git,开始初始化..."
  git_as_user "init"
  git_as_user "remote add origin ${REPO_SSH}"
  git_as_user "fetch origin ${BRANCH}"
  git_as_user "checkout -b ${BRANCH} origin/${BRANCH}"
  log_ok "初始化并检出 ${BRANCH}"
else
  log_ok "已存在 git 仓库,开始更新..."
  git_as_user "remote set-url origin ${REPO_SSH}"
  git_as_user "checkout ${BRANCH}"
  git_as_user "pull origin ${BRANCH}"
  log_ok "已拉取最新代码"
fi

log_done_step

log_step "Step3: 清理不需要的文件/目录"

if [ -f "${APP_DIR}/test" ]; then
  rm -f "${APP_DIR}/test"
  log_ok "移除 test"
else
  log_kv "跳过:" "test 不存在"
fi

if [ -f "${APP_DIR}/README.md" ]; then
  rm -f "${APP_DIR}/README.md"
  log_ok "移除 README.md"
else
  log_kv "跳过:" "README.md 不存在"
fi

if [ -f "${APP_DIR}/.gitignore" ]; then
  rm -f "${APP_DIR}/.gitignore"
  log_ok "移除 .gitignore"
else
  log_kv "跳过:" ".gitignore 不存在"
fi

if [ -d "${APP_DIR}/.github" ]; then
  rm -rf "${APP_DIR}/.github"
  log_ok "移除 .github/"
else
  log_kv "跳过:" ".github/ 不存在"
fi

log_done_step

log_step "Step4: 修正权限"
chown -R "${RUN_USER}:${RUN_USER}" "${APP_DIR}"
log_ok "chown -R ${RUN_USER}:${RUN_USER} ${APP_DIR}"
log_done_step

DEPLOY_END_TIME=$(date +%s)
DEPLOY_DURATION=$((DEPLOY_END_TIME - DEPLOY_START_TIME))

hr
echo -e "${C_BOLD}${C_GREEN}✅ 部署完成${C_RESET}"
echo -e "  ${C_DIM}总耗时:${DEPLOY_DURATION}s${C_RESET}"
echo "End"
hr

send_dingtalk_notification "${DEPLOY_STATUS}" "部署任务已完成" "${DEPLOY_DURATION}"
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容