147 lines
6.3 KiB
Bash
Executable File
147 lines
6.3 KiB
Bash
Executable File
#!/bin/sh
|
||
# Post-receive hook: push 后自动 build + start
|
||
#
|
||
echo "DEBUG: PWD=$PWD GIT_DIR=$GIT_DIR GIT_WORK_TREE=$GIT_WORK_TREE HOME=$HOME USER=$USER" >&2
|
||
|
||
# pnpm 环境变量
|
||
export NVM_DIR="$HOME/.nvm"
|
||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
||
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
|
||
|
||
REPO_TOPLEVEL="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
|
||
cd "$REPO_TOPLEVEL" || { echo "FATAL: cannot cd to $REPO_TOPLEVEL" >&2; exit 1; }
|
||
|
||
# ============================================================
|
||
# 0. sync_worktree —— 将**工**作**树**同步到**新** push 的 HEAD
|
||
# 背景:服务器 .git/config 设了 receive.denyCurrentBranch=updateInstead 时
|
||
# git 自**动**更**新**工作**树**但**会**跳过**此**钩子;**改**为 false 后
|
||
# git **不**会**更**新**工作**树**,**手**动 reset --hard 同步**才**能**让**下**面**
|
||
# 的 build **用**上**新**代**码**。
|
||
# ============================================================
|
||
sync_worktree() {
|
||
echo "=== sync_worktree: git reset --hard HEAD @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ==="
|
||
git reset --hard HEAD
|
||
}
|
||
|
||
# ============================================================
|
||
# 1. init_log_file —— 定**义** LOG_FILE + 创**建** logs/ 目**录**
|
||
# ============================================================
|
||
init_log_file() {
|
||
LOG_FILE="$REPO_TOPLEVEL/logs/post-receive.log"
|
||
mkdir -p "$(dirname "$LOG_FILE")"
|
||
}
|
||
|
||
# ============================================================
|
||
# 2. write_metadata_header —— **覆**盖写**入**元信息(**首**次**写** → **覆**盖**让日志**开**头是**这**个)
|
||
# ============================================================
|
||
write_metadata_header() {
|
||
{
|
||
echo "=== post-receive @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ==="
|
||
echo "branch: $(git rev-parse --abbrev-ref HEAD)"
|
||
echo "commit: $(git rev-parse --short HEAD)"
|
||
echo "cwd: $(pwd)"
|
||
echo ""
|
||
} > "$LOG_FILE"
|
||
}
|
||
|
||
# ============================================================
|
||
# 3. copy_env_by_branch —— 根**据**分支选 env-example → .env
|
||
# main 分支 → .env.production(prod 部署)
|
||
# test 分支 → .env.local(test **不**碰 .env.production,**避**免**读**到 prod 域名)
|
||
# dev 分支 → .env.local(**未**配远端**用**途,**但**保**留**)
|
||
# ============================================================
|
||
copy_env_by_branch() {
|
||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||
echo "=== branch: $CURRENT_BRANCH ===" >> "$LOG_FILE"
|
||
case "$CURRENT_BRANCH" in
|
||
main)
|
||
cp -f env-example/.env.production.example .env.production
|
||
echo "=== env: copied .env.production (main → prod) ===" >> "$LOG_FILE"
|
||
;;
|
||
test)
|
||
cp -f env-example/.env.local.example .env.local
|
||
echo "=== env: copied .env.local (test → test) ===" >> "$LOG_FILE"
|
||
;;
|
||
dev)
|
||
cp -f env-example/.env.development.example .env.local
|
||
echo "=== env: copied .env.local (dev → dev) ===" >> "$LOG_FILE"
|
||
;;
|
||
*)
|
||
echo "=== env: UNKNOWN branch '$CURRENT_BRANCH', skip env copy ===" >> "$LOG_FILE"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# ============================================================
|
||
# 4. run_build —— 跑 `pnpm run build`,输出**丢**,**捕**获退出码写日志
|
||
# 返回:build 退出码(main **用** if ! run_build 决**定**是否**继**续)
|
||
# ============================================================
|
||
run_build() {
|
||
echo "=== build START @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ===" >> "$LOG_FILE"
|
||
pnpm run build > /dev/null 2>&1
|
||
BUILD_EXIT=$?
|
||
echo "=== build EXIT_CODE=$BUILD_EXIT @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ===" >> "$LOG_FILE"
|
||
echo "" >> "$LOG_FILE"
|
||
return $BUILD_EXIT
|
||
}
|
||
|
||
# ============================================================
|
||
# 5. stop_existing_next —— 两阶段 kill + 等 1s 确**保**端口**完**全释放
|
||
# 1) 优**雅**关**闭**(SIGTERM)—— 旧**进**程**自**己 drain
|
||
# 2) 等 2s
|
||
# 3) 强杀(SIGKILL)—— **还**在**的**话
|
||
# 4) 等 1s
|
||
# ============================================================
|
||
stop_existing_next() {
|
||
echo "=== stop existing next @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ===" >> "$LOG_FILE"
|
||
pkill -f "next start" 2>/dev/null
|
||
pkill -f "next-server" 2>/dev/null
|
||
sleep 2
|
||
pkill -9 -f "next start" 2>/dev/null
|
||
pkill -9 -f "next-server" 2>/dev/null
|
||
sleep 1
|
||
echo "=== stop DONE @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ===" >> "$LOG_FILE"
|
||
echo "" >> "$LOG_FILE"
|
||
}
|
||
|
||
# ============================================================
|
||
# 6. launch_next —— 后**台**启 `pnpm run start`,捕 PID
|
||
# ============================================================
|
||
launch_next() {
|
||
# next stdout/stderr 接**到** logs/next-server.log(**覆**盖**模式**,**每**次 deploy **重**新**开**始)
|
||
NEXT_LOG_FILE="$REPO_TOPLEVEL/logs/next-server.log"
|
||
echo "=== start LAUNCH @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ===" >> "$LOG_FILE"
|
||
nohup pnpm run start > "$NEXT_LOG_FILE" 2>&1 &
|
||
START_PID=$!
|
||
echo "=== start PID=$START_PID @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ===" >> "$LOG_FILE"
|
||
}
|
||
|
||
# ============================================================
|
||
# 7. log_abort —— build 失败**时**写**结**束**标**记(**不**输**出** START LAUNCH)
|
||
# ============================================================
|
||
log_abort() {
|
||
echo "=== ABORTED @ $(date -u +%Y-%m-%dT%H:%M:%SZ) (build failed, start skipped) ===" >> "$LOG_FILE"
|
||
}
|
||
|
||
# ============================================================
|
||
# main —— 调**度**所**有**步骤
|
||
# ============================================================
|
||
main() {
|
||
init_log_file
|
||
sync_worktree # **先**同步工作**树**到**新** HEAD(**与** git config denyCurrentBranch 配**合**)
|
||
write_metadata_header # **先**覆盖写元信息(**这**样 env copy 日**志****能**保留在**后**面)
|
||
copy_env_by_branch # **追**加 env copy 日志
|
||
|
||
if ! run_build; then
|
||
log_abort
|
||
exit $BUILD_EXIT # build 失败 → **不**启 start(**避**免**用**旧 .next **跑**新代码)
|
||
fi
|
||
|
||
stop_existing_next # build 成功 → **先**关旧**进**程
|
||
launch_next # 再启新
|
||
}
|
||
|
||
main "$@"
|
||
|
||
exit 0
|