#!/usr/bin/env bash # deploy.sh —— 零构建发布:生成前缀 → 静态体检 → 模块图爬取(本地)→ 上传 OBS → 线上复验。 # # 前缀只有一个来源:deploy/prefix/.txt(由 scripts/config.mjs 写出); # / importmap / 上传前缀 三处全部由它派生(pitfalls.md 第五条)。 # # 用法: # ./deploy/prefix.sh prod # 只生成 prod 的 index.html + 前缀文件 # OBS_BUCKET=xxx ./scripts/deploy.sh prod # 体检 + 上传 + 复验(需 obsutil 已配置) # ./scripts/deploy.sh prod --check-only # 只做静态体检与本地模块图爬取 # # 环境变量: # OBS_BUCKET 桶名(必填,除非 --check-only) # OBS_ENDPOINT OBS 端点(默认 obs.cn-north-4.myhuaweicloud.com,按实际改) # CDN_BASE 线上地址根,如 https://kangshifu.fmode.cn(用于发布后复验) set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$REPO_ROOT" ENV_NAME="${1:-}" CHECK_ONLY="${2:-}" if [[ -z "$ENV_NAME" ]]; then echo "用法:scripts/deploy.sh [--check-only]" >&2; exit 2; fi echo "== 1/5 生成前缀(唯一来源)" node scripts/config.mjs "$ENV_NAME" PREFIX="$(cat "deploy/prefix/${ENV_NAME}.txt")" echo " PREFIX=$PREFIX" echo "== 2/5 静态体检(A 组 1–9 + 凭据)" node scripts/check.mjs --env "$ENV_NAME" node --test 'test/*.test.js' >/dev/null && echo " core 单测 OK" echo "== 3/5 本地模块图爬取" node scripts/verify-modules.mjs --serve >/tmp/deploy-modules.log 2>&1 || { cat /tmp/deploy-modules.log; exit 1; } tail -1 /tmp/deploy-modules.log if [[ "$CHECK_ONLY" == "--check-only" ]]; then echo "== 仅体检,未上传"; exit 0; fi : "${OBS_BUCKET:?OBS_BUCKET 未设置}" OBS_ENDPOINT="${OBS_ENDPOINT:-obs.cn-north-4.myhuaweicloud.com}" KEY_PREFIX="${PREFIX#/}"; KEY_PREFIX="${KEY_PREFIX%/}" echo "== 4/5 上传 OBS(前缀 ${KEY_PREFIX}/)" upload() { # upload <本地文件> obsutil cp "$1" "obs://${OBS_BUCKET}/${2}" -e "$OBS_ENDPOINT" -f -meta="Content-Type:$3#Cache-Control:public,max-age=3600" >/dev/null } # 先删后传 index.html(sync 按 size+mtime 会跳过它,见坑 6) obsutil rm "obs://${OBS_BUCKET}/${KEY_PREFIX}/index.html" -e "$OBS_ENDPOINT" -f >/dev/null 2>&1 || true upload index.html "${KEY_PREFIX}/index.html" "text/html; charset=utf-8" while IFS= read -r -d '' f; do rel="${f#./}" case "$rel" in *.js) ct="text/javascript; charset=utf-8" ;; *.json) ct="application/json; charset=utf-8" ;; *.css) ct="text/css; charset=utf-8" ;; *.woff2) ct="font/woff2" ;; *) ct="application/octet-stream" ;; esac upload "$f" "${KEY_PREFIX}/${rel}" "$ct" done < <(find src lib/vendor -type f \( -name '*.js' -o -name '*.json' -o -name '*.css' -o -name '*.woff2' \) -print0) # index.html 单独设为 no-cache(CDN 回退拿到的永远是新壳) obsutil setmeta "obs://${OBS_BUCKET}/${KEY_PREFIX}/index.html" -e "$OBS_ENDPOINT" \ -meta="Content-Type:text/html; charset=utf-8#Cache-Control:no-cache, no-store, must-revalidate" >/dev/null if [[ -n "${CDN_BASE:-}" ]]; then echo "== 5/5 线上复验(模块图 + 深链接)" node scripts/verify-modules.mjs "${CDN_BASE%/}${PREFIX}" else echo "== 5/5 跳过线上复验(未设置 CDN_BASE)" echo " 上传完成后请:node scripts/verify-modules.mjs https://${PREFIX}" echo " 并确认 CDN 回退规则:命中真实资源直返;其余路径 200 返回 index.html(禁 301/302)" fi