Files
zkxw/scripts/package-docker-deploy.sh
2026-09-17 23:49:16 +08:00

216 lines
7.9 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 打包 Docker 交付物:预构建应用镜像 + compose + 数据库 SQL(不含前后端源码)
# 目标运行环境:甲方 Linux x86_64 云主机(固定 linux/amd64,与发版机 macOS/ARM 无关)
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
# 交付镜像架构(甲方云主机默认 amd64;勿改为 arm64 除非已确认对方为 ARM 云)
export DOCKER_PLATFORM=linux/amd64
export DOCKER_BUILDKIT=1
VERSION="${1:-$(date +%Y%m%d)}"
OUT_DIR="${ROOT}/dist"
PKG_NAME="zkxw-docker-deploy-${VERSION}"
STAGING="${OUT_DIR}/${PKG_NAME}"
ARCHIVE="${OUT_DIR}/${PKG_NAME}.tar.gz"
IMAGES_TAR="${STAGING}/images/zkxw-app-images.tar"
BACKEND_IMAGE="zkxw-backend:${VERSION}"
WEB_IMAGE="zkxw-web:${VERSION}"
echo "==> [1/4] 构建应用镜像(平台 ${DOCKER_PLATFORM},供 Linux 云主机)"
"${ROOT}/scripts/build-docker-images.sh" "${VERSION}"
echo "==> [2/4] 导出镜像到 tar"
rm -rf "${STAGING}"
mkdir -p "${STAGING}/images" "${STAGING}/deploy/docker" "${STAGING}/docs/sql"
docker save -o "${IMAGES_TAR}" "${BACKEND_IMAGE}" "${WEB_IMAGE}"
echo "==> [3/4] 组装部署文件(无 backend/ web-admin 源码)"
cp "${ROOT}/deploy/docker/docker-compose.yml" "${STAGING}/deploy/docker/"
sed -e "s|^ZKXW_BACKEND_IMAGE=.*|ZKXW_BACKEND_IMAGE=${BACKEND_IMAGE}|" \
-e "s|^ZKXW_WEB_IMAGE=.*|ZKXW_WEB_IMAGE=${WEB_IMAGE}|" \
"${ROOT}/deploy/docker/.env.example" > "${STAGING}/deploy/docker/.env.example"
# 从本地 deploy/docker/.env 注入运行时密钥与微信配置到交付包 .env.example
# (不注入 MYSQL/SECRET_KEY/MINIO 密码,避免覆盖甲方现网库口令)
LOCAL_ENV="${ROOT}/deploy/docker/.env"
STAGING_ENV="${STAGING}/deploy/docker/.env.example"
if [[ -f "${LOCAL_ENV}" ]]; then
if python3 - "${LOCAL_ENV}" "${STAGING_ENV}" <<'PY'
import sys
from pathlib import Path
local_env, staging_env = map(Path, sys.argv[1:3])
# 打包时注入:登录 RSA + 微信/小程序(业务通知依赖);不注入库口令
INJECT_KEYS = (
"LOGIN_RSA_PRIVATE_KEY_PEM",
"WXPAY_MINI_APPID",
"WXPAY_SECRET",
"WX_MP_NOTIFY_ENABLED",
"WX_MP_APPID",
"WX_MP_SECRET",
"WX_MP_TEMPLATE_APPROVAL",
"WX_MP_TEMPLATE_CC",
"WX_MP_TEMPLATE_APPLICANT_PROGRESS",
"WX_MP_TPL_FIELD_TITLE",
"WX_MP_TPL_FIELD_APPLICANT",
"WX_MP_TPL_FIELD_NODE",
"WX_MP_TPL_FIELD_TIME",
"WX_MP_TPL_FIELD_PROCESS",
"WX_MP_TPL_CC_FIELD_TITLE",
"WX_MP_TPL_CC_FIELD_APPLICANT",
"WX_MP_TPL_CC_FIELD_TIME",
"WX_MP_TPL_CC_FIELD_PROCESS",
"WX_MP_TPL_PROGRESS_FIELD_BUSINESS",
"WX_MP_TPL_PROGRESS_FIELD_PROCESS",
"WX_MP_TPL_PROGRESS_FIELD_APPROVER",
"WX_MP_TPL_PROGRESS_FIELD_RESULT",
"WX_MP_TPL_PROGRESS_FIELD_TIME",
)
def parse_env(text: str) -> dict[str, str]:
out: dict[str, str] = {}
for line in text.splitlines():
s = line.strip()
if not s or s.startswith("#") or "=" not in line:
continue
# 保留行首 key=(允许值内含 =)
if line.lstrip().startswith("#"):
continue
key, _, val = line.partition("=")
key = key.strip()
if key:
out[key] = line # 整行,保留原样(含引号/换行转义)
return out
local_map = parse_env(local_env.read_text())
injected: list[str] = []
missing: list[str] = []
for k in INJECT_KEYS:
if k in local_map and local_map[k].partition("=")[2].strip():
injected.append(k)
else:
missing.append(k)
lines_out: list[str] = []
seen: set[str] = set()
for line in staging_env.read_text().splitlines():
raw = line.lstrip()
if raw.startswith("#"):
# 更新注释:标明微信/RSA 可能已由打包注入
if "登录 RSA" in line:
lines_out.append(
"# 登录 RSA(打包时若本地 deploy/docker/.env 有值则已注入)"
)
continue
if "微信服务号" in line or "微信支付 / 小程序" in line:
lines_out.append(line)
if "微信服务号" in line:
lines_out.append(
"# 下列 WX_MP_* / 模板字段:打包时从本地 deploy/docker/.env 注入(有则覆盖)"
)
continue
lines_out.append(line)
continue
key = line.partition("=")[0].strip() if "=" in line else ""
# 去掉注释形态的同名键,稍后写注入行
if key.startswith("#"):
maybe = key.lstrip("#").strip()
if maybe in INJECT_KEYS:
continue
lines_out.append(line)
continue
if key in INJECT_KEYS:
if key in local_map and local_map[key].partition("=")[2].strip():
lines_out.append(local_map[key])
seen.add(key)
else:
lines_out.append(line)
seen.add(key)
continue
lines_out.append(line)
# 本地有、example 里没有的键:追加到文件末尾
extra = [local_map[k] for k in INJECT_KEYS if k not in seen and k in local_map and local_map[k].partition("=")[2].strip()]
if extra:
lines_out.append("")
lines_out.append("# ---- 打包自 deploy/docker/.env 追加 ----")
lines_out.extend(extra)
staging_env.write_text("\n".join(lines_out) + "\n")
# 同时生成可直接 cp 的 .env(与 .env.example 相同;甲方改库密码即可)
staging_env.with_name(".env").write_text(staging_env.read_text())
print("injected:" + ",".join(injected) if injected else "injected:(none)")
if missing:
print("missing_or_empty:" + ",".join(missing))
sys.exit(0 if injected else 2)
PY
then
echo " 已从 deploy/docker/.env 注入微信/小程序/RSA 等到交付包 .env.example 与 .env"
elif [[ $? -eq 2 ]]; then
echo " 警告: deploy/docker/.env 中微信/RSA 等注入项为空,交付包仍用 .env.example 占位" >&2
cp "${STAGING}/deploy/docker/.env.example" "${STAGING}/deploy/docker/.env"
else
echo " 错误: 注入交付包环境变量失败" >&2
exit 1
fi
else
echo " 警告: 无 deploy/docker/.env,跳过注入;仅提供 .env.example"
cp "${STAGING}/deploy/docker/.env.example" "${STAGING}/deploy/docker/.env"
fi
cp "${ROOT}/deploy/docker/README.md" "${STAGING}/deploy/docker/"
cp -R "${ROOT}/deploy/docker/mysql" "${STAGING}/deploy/docker/"
cp "${ROOT}/docs/sql/zkxw.sql" "${STAGING}/docs/sql/zkxw.sql"
cp "${ROOT}/deploy/docker/README.md" "${STAGING}/部署说明.md"
cp "${ROOT}/deploy/docker/部署指导手册.html" "${STAGING}/部署指导手册.html"
cp "${ROOT}/deploy/docker/部署指导手册.html" "${STAGING}/deploy/docker/部署指导手册.html"
cat > "${STAGING}/VERSION.txt" <<EOF
package=${PKG_NAME}
version=${VERSION}
platform=${DOCKER_PLATFORM}
backend_image=${BACKEND_IMAGE}
web_image=${WEB_IMAGE}
web_includes=web-admin+mobile-oa-h5(/oa-h5/)
env_note=微信/小程序/RSA 已从发版机 deploy/docker/.env 注入(若有);库口令仍须按甲方现网填写
built_at=$(date -Iseconds)
git_commit=$(git -C "${ROOT}" rev-parse --short HEAD 2>/dev/null || echo unknown)
deploy_dir=deploy/docker
EOF
echo "==> [4/4] 生成压缩包"
mkdir -p "${OUT_DIR}"
tar -czf "${ARCHIVE}" -C "${OUT_DIR}" "${PKG_NAME}"
echo ""
echo "完成: ${ARCHIVE}"
echo "应用镜像: $(du -h "${IMAGES_TAR}" | cut -f1)"
echo "总包大小: $(du -h "${ARCHIVE}" | cut -f1)"
echo ""
echo "包内不含前后端源码,包含:"
echo " - images/zkxw-app-images.tar(zkxw-backend + zkxw-web,web 含 /oa-h5/)"
echo " - deploy/docker/ (compose + .env.example + .env + mysql init)"
echo " - docs/sql/zkxw.sql"
echo " - 部署指导手册.html(客户安装手册,浏览器打开)"
echo ""
echo "甲方服务器执行:"
echo " tar -xzf ${PKG_NAME}.tar.gz"
echo " cd ${PKG_NAME}"
echo " docker load -i images/zkxw-app-images.tar"
echo " cd deploy/docker"
echo " # 新装: cp .env.example .env 后改 MYSQL/SECRET/MINIO 密码"
echo " # 升级: 保留现网库密码;可用包内已注入的微信配置,或合并 .env.example 中 WX_*/WXPAY_* 段"
echo " docker compose up -d"