#!/usr/bin/env bash set -euo pipefail # 构建“一键部署包”(代码 + vendor + 加密数据库快照) # 目标:下载压缩包 → 解压 → 运行 install.sh → 即可恢复到最新数据并可访问。 # # 依赖:git / tar / gzip # 可选:composer(仅当需要现装 vendor) # # 用法: # bash scripts/build_oneclick_bundle.sh # 输出: # /app/working/dist/saasshop_bundle_.tar.gz # /app/working/dist/saasshop_bundle_latest.tar.gz REPO_DIR=$(cd "$(dirname "$0")/.." && pwd) cd "$REPO_DIR" DIST_DIR="/app/working/dist" mkdir -p "$DIST_DIR" DATA_REPO_SSH="" if [[ -f /app/working.secret/saasshop_data_repo_ssh ]]; then DATA_REPO_SSH=$(cat /app/working.secret/saasshop_data_repo_ssh) fi if [[ "$DATA_REPO_SSH" == "" ]]; then echo "缺少数据仓地址:/app/working.secret/saasshop_data_repo_ssh" exit 31 fi STAMP=$(date +%Y%m%d_%H%M%S) BUNDLE_ROOT=$(mktemp -d) trap 'rm -rf "$BUNDLE_ROOT" || true' EXIT APP_DIR="$BUNDLE_ROOT/saasshop" mkdir -p "$APP_DIR" # 1) 打包代码(不带 .git) echo "[bundle] copying app files ..." # 使用 tar 管道复制(比 cp -a 更容易排除) tar \ --exclude=".git" \ --exclude="node_modules" \ --exclude="storage/logs" \ --exclude="storage/framework/cache" \ --exclude="storage/framework/sessions" \ --exclude="storage/framework/views" \ --exclude="storage/app/private" \ --exclude=".env" \ -cf - . | (cd "$APP_DIR" && tar -xf -) # 2) 确保 vendor 存在(无构建链/傻瓜部署,优先直接随包携带) if [[ ! -d "$APP_DIR/vendor" ]]; then echo "[bundle] vendor 不存在,尝试 composer install --no-dev ..." if command -v composer >/dev/null 2>&1; then (cd "$APP_DIR" && composer install --no-dev --prefer-dist --no-interaction) else echo "[bundle] composer 不存在且 vendor 缺失,无法构建傻瓜包" exit 32 fi fi # 3) 拉取数据仓最新快照 echo "[bundle] fetching latest encrypted snapshot from data repo ..." DATA_TMP="$BUNDLE_ROOT/data_repo" git clone "$DATA_REPO_SSH" "$DATA_TMP" >/dev/null if [[ ! -f "$DATA_TMP/snapshots/latest.sql.gz.enc" ]]; then echo "数据仓缺少 snapshots/latest.sql.gz.enc" exit 33 fi mkdir -p "$APP_DIR/bundle/snapshots" cp -f "$DATA_TMP/snapshots/latest.sql.gz.enc" "$APP_DIR/bundle/snapshots/latest.sql.gz.enc" if [[ -f "$DATA_TMP/snapshots/manifest.json" ]]; then cp -f "$DATA_TMP/snapshots/manifest.json" "$APP_DIR/bundle/snapshots/manifest.json" fi # 4) 写入 install.sh(真正一键入口) cat > "$APP_DIR/install.sh" <<'EOF' #!/usr/bin/env bash set -euo pipefail # SaaSShop 一键部署脚本(随 bundle 一起发放) # 用法: # export SAASSHOP_DB_SNAPSHOT_KEY='解密密钥' # bash install.sh # # 你也可以先准备好 .env(推荐),脚本会优先读取项目根目录 .env。 APP_DIR=$(cd "$(dirname "$0")" && pwd) cd "$APP_DIR" if [[ ! -f .env ]]; then if [[ -f .env.example ]]; then cp .env.example .env echo "[install] 已生成 .env(来自 .env.example),请按需修改 DB_* / APP_URL 等配置。" else echo "[install] 缺少 .env 与 .env.example,无法继续" exit 41 fi fi # 读取 .env(仅用于 DB 连接) set -a # shellcheck disable=SC1091 source .env set +a DB_HOST=${DB_HOST:-127.0.0.1} DB_PORT=${DB_PORT:-3306} DB_DATABASE=${DB_DATABASE:-appdb} DB_USERNAME=${DB_USERNAME:-appuser} DB_PASSWORD=${DB_PASSWORD:-} if [[ "${SAASSHOP_DB_SNAPSHOT_KEY:-}" == "" ]]; then echo "[install] 缺少 SAASSHOP_DB_SNAPSHOT_KEY:无法解密并恢复完整数据。" echo "[install] 请先:export SAASSHOP_DB_SNAPSHOT_KEY='...密钥...'" exit 42 fi # 权限准备 mkdir -p storage bootstrap/cache chmod -R ug+rwX storage bootstrap/cache || true # APP_KEY if ! grep -q '^APP_KEY=' .env || [[ "${APP_KEY:-}" == "" ]]; then echo "[install] 生成 APP_KEY ..." php artisan key:generate --force fi echo "[install] 导入最新数据库快照 ..." ENC_FILE="bundle/snapshots/latest.sql.gz.enc" if [[ ! -f "$ENC_FILE" ]]; then echo "[install] 缺少快照文件:$ENC_FILE" exit 43 fi TMP_DIR=$(mktemp -d) trap 'rm -rf "$TMP_DIR" || true' EXIT SQL_GZ="$TMP_DIR/latest.sql.gz" openssl enc -d -aes-256-cbc -pbkdf2 \ -pass env:SAASSHOP_DB_SNAPSHOT_KEY \ -in "$ENC_FILE" -out "$SQL_GZ" gzip -dc "$SQL_GZ" | mysql \ --host="$DB_HOST" --port="$DB_PORT" \ --user="$DB_USERNAME" --password="$DB_PASSWORD" echo "[install] 清理缓存 ..." php artisan optimize:clear echo "[done] 部署完成。接下来:配置 nginx 指向 public/,即可访问。" EOF chmod +x "$APP_DIR/install.sh" # 5) 打包 OUT_FILE="$DIST_DIR/saasshop_bundle_${STAMP}.tar.gz" LATEST_FILE="$DIST_DIR/saasshop_bundle_latest.tar.gz" echo "[bundle] creating tar.gz ..." ( cd "$BUNDLE_ROOT" tar -czf "$OUT_FILE" saasshop ) cp -f "$OUT_FILE" "$LATEST_FILE" echo "[bundle] output: $OUT_FILE" echo "[bundle] latest: $LATEST_FILE"