227 lines
6.8 KiB
Bash
Executable File
227 lines
6.8 KiB
Bash
Executable File
#!/bin/sh
|
|
# Post-receive hook: push 后自动部署容器
|
|
# 默认在服务器本机构建镜像;配置 .deploy.env 后也可从镜像仓库拉取 CI 构建好的镜像。
|
|
|
|
REPO_TOPLEVEL=""
|
|
LOG_FILE=""
|
|
CURRENT_BRANCH=""
|
|
DEPLOY_ENV=""
|
|
ENV_FILE=""
|
|
START_PORT=""
|
|
IMAGE_TAG=""
|
|
IMAGE_SOURCE=""
|
|
REMOTE_IMAGE_TAG=""
|
|
CONTAINER_NAME=""
|
|
COMPOSE_PROJECT_NAME=""
|
|
COMPOSE_EXIT=0
|
|
|
|
resolve_repo_context() {
|
|
GIT_DIR_ABS="$(git rev-parse --absolute-git-dir 2>/dev/null)"
|
|
if [ -n "$GIT_DIR_ABS" ] && [ -d "$GIT_DIR_ABS" ]; then
|
|
REPO_TOPLEVEL="$(cd "$GIT_DIR_ABS/.." && pwd)"
|
|
export GIT_DIR="$GIT_DIR_ABS"
|
|
else
|
|
REPO_TOPLEVEL="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
|
|
fi
|
|
unset GIT_WORK_TREE
|
|
cd "$REPO_TOPLEVEL" || { echo "FATAL: cannot cd to $REPO_TOPLEVEL" >&2; exit 1; }
|
|
echo "=== REPO_TOPLEVEL=$REPO_TOPLEVEL (GIT_DIR=$GIT_DIR) ===" >&2
|
|
}
|
|
|
|
init_log_file() {
|
|
LOG_FILE="$REPO_TOPLEVEL/logs/post-receive.log"
|
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
}
|
|
|
|
write_metadata_header() {
|
|
{
|
|
echo "=== post-receive @ $(date +%Y-%m-%dT%H:%M:%S%z) ==="
|
|
echo "branch: $(git rev-parse --abbrev-ref HEAD)"
|
|
echo "commit: $(git rev-parse --short HEAD)"
|
|
echo "cwd: $(pwd)"
|
|
echo ""
|
|
} > "$LOG_FILE"
|
|
}
|
|
|
|
sync_worktree() {
|
|
echo "=== sync_worktree: git reset --hard HEAD @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
|
|
git reset --hard HEAD >> "$LOG_FILE" 2>&1
|
|
}
|
|
|
|
load_deploy_config() {
|
|
if [ ! -f ".deploy.env" ]; then
|
|
echo "=== deploy config: .deploy.env not found, use defaults ===" >> "$LOG_FILE"
|
|
return 0
|
|
fi
|
|
|
|
echo "=== deploy config: loading .deploy.env ===" >> "$LOG_FILE"
|
|
set -a
|
|
# shellcheck disable=SC1091
|
|
. ./.deploy.env
|
|
set +a
|
|
}
|
|
|
|
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
|
|
ENV_FILE=".env.production"
|
|
DEPLOY_ENV="prod"
|
|
START_PORT=9185
|
|
echo "=== env: copied .env.production (main → prod) ===" >> "$LOG_FILE"
|
|
;;
|
|
test)
|
|
cp -f env-example/.env.local.example .env.local
|
|
ENV_FILE=".env.local"
|
|
DEPLOY_ENV="test"
|
|
START_PORT=9135
|
|
echo "=== env: copied .env.local (test → test) ===" >> "$LOG_FILE"
|
|
;;
|
|
dev)
|
|
cp -f env-example/.env.development.example .env.local
|
|
ENV_FILE=".env.local"
|
|
DEPLOY_ENV="dev"
|
|
START_PORT=9135
|
|
echo "=== env: copied .env.local (dev → dev) ===" >> "$LOG_FILE"
|
|
;;
|
|
*)
|
|
ENV_FILE=".env.local"
|
|
DEPLOY_ENV="$CURRENT_BRANCH"
|
|
START_PORT=9135
|
|
echo "=== env: UNKNOWN branch '$CURRENT_BRANCH', use defaults ===" >> "$LOG_FILE"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
prepare_compose_env() {
|
|
SHORT_SHA="$(git rev-parse --short HEAD)"
|
|
IMAGE_SOURCE="${COZSWEET_DEPLOY_IMAGE_SOURCE:-build}"
|
|
REMOTE_IMAGE_TAG="${COZSWEET_REMOTE_IMAGE_TAG:-${DEPLOY_ENV}-${SHORT_SHA}}"
|
|
|
|
case "$IMAGE_SOURCE" in
|
|
build)
|
|
IMAGE_TAG="cozsweet-web:${DEPLOY_ENV}-${SHORT_SHA}"
|
|
;;
|
|
pull)
|
|
if [ -z "${COZSWEET_REGISTRY_IMAGE:-}" ]; then
|
|
echo "=== missing COZSWEET_REGISTRY_IMAGE for pull deploy ===" >> "$LOG_FILE"
|
|
exit 64
|
|
fi
|
|
IMAGE_TAG="${COZSWEET_REGISTRY_IMAGE}:${REMOTE_IMAGE_TAG}"
|
|
;;
|
|
*)
|
|
echo "=== unsupported COZSWEET_DEPLOY_IMAGE_SOURCE: $IMAGE_SOURCE ===" >> "$LOG_FILE"
|
|
exit 64
|
|
;;
|
|
esac
|
|
|
|
CONTAINER_NAME="cozsweet-web-${DEPLOY_ENV}"
|
|
COMPOSE_PROJECT_NAME="cozsweet-web-${DEPLOY_ENV}"
|
|
|
|
export COZSWEET_IMAGE="$IMAGE_TAG"
|
|
export COZSWEET_CONTAINER_NAME="$CONTAINER_NAME"
|
|
export COZSWEET_ENV_FILE="$ENV_FILE"
|
|
export COZSWEET_HOST_PORT="$START_PORT"
|
|
export COMPOSE_PROJECT_NAME
|
|
export DOCKER_BUILDKIT=1
|
|
export COMPOSE_DOCKER_CLI_BUILD=1
|
|
|
|
{
|
|
echo "=== deploy env: $DEPLOY_ENV ==="
|
|
echo "=== image source: $IMAGE_SOURCE ==="
|
|
echo "=== env file: $COZSWEET_ENV_FILE ==="
|
|
echo "=== image: $COZSWEET_IMAGE ==="
|
|
echo "=== remote image tag: $REMOTE_IMAGE_TAG ==="
|
|
echo "=== container: $COZSWEET_CONTAINER_NAME ==="
|
|
echo "=== host port: $COZSWEET_HOST_PORT ==="
|
|
echo "=== compose project: $COMPOSE_PROJECT_NAME ==="
|
|
echo ""
|
|
} >> "$LOG_FILE"
|
|
}
|
|
|
|
docker_compose() {
|
|
if docker compose version >/dev/null 2>&1; then
|
|
docker compose "$@"
|
|
return $?
|
|
fi
|
|
|
|
if command -v docker-compose >/dev/null 2>&1; then
|
|
docker-compose "$@"
|
|
return $?
|
|
fi
|
|
|
|
echo "=== docker compose command not found ===" >> "$LOG_FILE"
|
|
return 127
|
|
}
|
|
|
|
run_container_build() {
|
|
echo "=== docker build START @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
|
|
docker_compose build web >> "$LOG_FILE" 2>&1
|
|
COMPOSE_EXIT=$?
|
|
echo "=== docker build EXIT_CODE=$COMPOSE_EXIT @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
|
|
echo "" >> "$LOG_FILE"
|
|
return $COMPOSE_EXIT
|
|
}
|
|
|
|
pull_container_image() {
|
|
echo "=== docker pull START @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
|
|
docker_compose pull web >> "$LOG_FILE" 2>&1
|
|
COMPOSE_EXIT=$?
|
|
echo "=== docker pull EXIT_CODE=$COMPOSE_EXIT @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
|
|
echo "" >> "$LOG_FILE"
|
|
return $COMPOSE_EXIT
|
|
}
|
|
|
|
launch_container() {
|
|
echo "=== docker compose up START @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
|
|
if [ "$IMAGE_SOURCE" = "pull" ]; then
|
|
docker_compose up -d --remove-orphans --no-build web >> "$LOG_FILE" 2>&1
|
|
else
|
|
docker_compose up -d --remove-orphans web >> "$LOG_FILE" 2>&1
|
|
fi
|
|
COMPOSE_EXIT=$?
|
|
echo "=== docker compose up EXIT_CODE=$COMPOSE_EXIT @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
|
|
echo "" >> "$LOG_FILE"
|
|
return $COMPOSE_EXIT
|
|
}
|
|
|
|
log_abort() {
|
|
echo "=== ABORTED @ $(date +%Y-%m-%dT%H:%M:%S%z) ($1) ===" >> "$LOG_FILE"
|
|
}
|
|
|
|
main() {
|
|
resolve_repo_context
|
|
init_log_file
|
|
write_metadata_header
|
|
|
|
sync_worktree
|
|
load_deploy_config
|
|
copy_env_by_branch
|
|
prepare_compose_env
|
|
|
|
if [ "$IMAGE_SOURCE" = "pull" ]; then
|
|
if ! pull_container_image; then
|
|
log_abort "docker pull failed, old container kept running"
|
|
exit $COMPOSE_EXIT
|
|
fi
|
|
else
|
|
if ! run_container_build; then
|
|
log_abort "docker build failed, old container kept running"
|
|
exit $COMPOSE_EXIT
|
|
fi
|
|
fi
|
|
|
|
if ! launch_container; then
|
|
log_abort "docker compose up failed"
|
|
exit $COMPOSE_EXIT
|
|
fi
|
|
|
|
echo "=== DEPLOY DONE @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
|
|
}
|
|
|
|
main "$@"
|
|
|
|
exit 0
|