181 lines
6.4 KiB
Bash
Executable File
181 lines
6.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# 重新生成 release/ 与 release.tar.gz,供 Ubuntu Docker Compose 一键部署。
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
RELEASE_DIR="$ROOT_DIR/release"
|
|
STAGING_DIR="$(mktemp -d "${TMPDIR:-/tmp}/wordcloud-release.XXXXXX")"
|
|
ARCHIVE="$ROOT_DIR/release.tar.gz"
|
|
PKG_NAME="wordcloud"
|
|
|
|
cleanup() {
|
|
rm -rf "$STAGING_DIR"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
log() { echo "[release] $*"; }
|
|
|
|
log "staging at $STAGING_DIR"
|
|
mkdir -p "$STAGING_DIR/$PKG_NAME"
|
|
|
|
# ── root deploy files ───────────────────────────────────────
|
|
cp -a "$ROOT_DIR/docker-compose.yml" "$STAGING_DIR/$PKG_NAME/"
|
|
cp -a "$ROOT_DIR/Makefile" "$STAGING_DIR/$PKG_NAME/"
|
|
cp -a "$ROOT_DIR/DOCKER.md" "$STAGING_DIR/$PKG_NAME/"
|
|
cp -a "$ROOT_DIR/install-ubuntu.sh" "$STAGING_DIR/$PKG_NAME/"
|
|
cp -a "$ROOT_DIR/README.md" "$STAGING_DIR/$PKG_NAME/" 2>/dev/null || true
|
|
cp -a "$ROOT_DIR/CHANGELOG.md" "$STAGING_DIR/$PKG_NAME/" 2>/dev/null || true
|
|
|
|
cat > "$STAGING_DIR/$PKG_NAME/.gitignore" <<'GI'
|
|
.DS_Store
|
|
.idea/
|
|
.claude/
|
|
__pycache__/
|
|
*.py[cod]
|
|
.venv/
|
|
node_modules/
|
|
frontend/dist/
|
|
backend/service_workspace/
|
|
backend/service_assets/
|
|
backend/service_projects/
|
|
backend/service_fonts/
|
|
backend/EfficientWordCloud/build/
|
|
backend/EfficientWordCloud/dist/
|
|
backend/EfficientWordCloud/**/*.so
|
|
*.log
|
|
*.db
|
|
GI
|
|
|
|
# ── backend (source only) ───────────────────────────────────
|
|
mkdir -p "$STAGING_DIR/$PKG_NAME/backend"
|
|
rsync -a \
|
|
--exclude '.venv/' \
|
|
--exclude '.runtime/' \
|
|
--exclude '.matplotlib/' \
|
|
--exclude '.idea/' \
|
|
--exclude '.claude/' \
|
|
--exclude '.DS_Store' \
|
|
--exclude '__pycache__/' \
|
|
--exclude '*.py[cod]' \
|
|
--exclude '*.so' \
|
|
--exclude '*.o' \
|
|
--exclude '*.egg-info/' \
|
|
--exclude 'service_workspace/' \
|
|
--exclude 'service_assets/' \
|
|
--exclude 'service_projects/' \
|
|
--exclude 'service_fonts/' \
|
|
--exclude 'service_design_templates/' \
|
|
--exclude 'output/' \
|
|
--exclude 'output_cli_test/' \
|
|
--exclude 'ref/' \
|
|
--exclude 'docs/' \
|
|
--exclude 'EfficientWordCloud/build/' \
|
|
--exclude 'EfficientWordCloud/dist/' \
|
|
--exclude 'EfficientWordCloud/efficient_wordcloud.egg-info/' \
|
|
--exclude 'EfficientWordCloud/**/__pycache__/' \
|
|
--exclude 'EfficientWordCloud/**/*.so' \
|
|
--exclude 'EfficientWordCloud/**/*.o' \
|
|
"$ROOT_DIR/backend/" "$STAGING_DIR/$PKG_NAME/backend/"
|
|
|
|
# design template seeds (json only)
|
|
if [[ -d "$ROOT_DIR/backend/service_design_templates" ]]; then
|
|
mkdir -p "$STAGING_DIR/$PKG_NAME/backend/service_design_templates"
|
|
rsync -a \
|
|
--include '*/' \
|
|
--include 'template.json' \
|
|
--exclude '*' \
|
|
"$ROOT_DIR/backend/service_design_templates/" \
|
|
"$STAGING_DIR/$PKG_NAME/backend/service_design_templates/" || true
|
|
fi
|
|
|
|
# ensure runtime dirs exist as empty placeholders (volumes will mount over them)
|
|
mkdir -p \
|
|
"$STAGING_DIR/$PKG_NAME/backend/service_workspace" \
|
|
"$STAGING_DIR/$PKG_NAME/backend/service_assets" \
|
|
"$STAGING_DIR/$PKG_NAME/backend/service_projects" \
|
|
"$STAGING_DIR/$PKG_NAME/backend/service_fonts"
|
|
# keep design templates if seeded; otherwise empty
|
|
mkdir -p "$STAGING_DIR/$PKG_NAME/backend/service_design_templates"
|
|
|
|
# ── frontend ────────────────────────────────────────────────
|
|
mkdir -p "$STAGING_DIR/$PKG_NAME/frontend"
|
|
rsync -a \
|
|
--exclude 'node_modules/' \
|
|
--exclude 'dist/' \
|
|
--exclude '.DS_Store' \
|
|
--exclude '.claude/' \
|
|
--exclude '.idea/' \
|
|
"$ROOT_DIR/frontend/" "$STAGING_DIR/$PKG_NAME/frontend/"
|
|
|
|
# ── private AI converter ────────────────────────────────────
|
|
mkdir -p "$STAGING_DIR/$PKG_NAME/ai-converter"
|
|
rsync -a \
|
|
--exclude '__pycache__/' \
|
|
--exclude '*.py[cod]' \
|
|
"$ROOT_DIR/ai-converter/" "$STAGING_DIR/$PKG_NAME/ai-converter/"
|
|
|
|
# ── docs ────────────────────────────────────────────────────
|
|
if [[ -d "$ROOT_DIR/docs" ]]; then
|
|
mkdir -p "$STAGING_DIR/$PKG_NAME/docs"
|
|
rsync -a \
|
|
--exclude '.DS_Store' \
|
|
"$ROOT_DIR/docs/" "$STAGING_DIR/$PKG_NAME/docs/" || true
|
|
fi
|
|
|
|
chmod +x "$STAGING_DIR/$PKG_NAME/install-ubuntu.sh"
|
|
chmod +x "$STAGING_DIR/$PKG_NAME/backend/start-dev.sh" 2>/dev/null || true
|
|
|
|
# sanity: refuse if venv/workspace leaked in
|
|
if [[ -e "$STAGING_DIR/$PKG_NAME/backend/.venv" ]]; then
|
|
echo "[release] ERROR: .venv leaked into staging" >&2
|
|
exit 1
|
|
fi
|
|
if find "$STAGING_DIR/$PKG_NAME/backend/service_workspace" -mindepth 1 | read -r _; then
|
|
echo "[release] ERROR: service_workspace not empty in staging" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# ── overwrite release/ ──────────────────────────────────────
|
|
log "overwrite $RELEASE_DIR"
|
|
rm -rf "$RELEASE_DIR"
|
|
mkdir -p "$RELEASE_DIR"
|
|
rsync -a "$STAGING_DIR/$PKG_NAME/" "$RELEASE_DIR/"
|
|
|
|
# ── overwrite release.tar.gz ────────────────────────────────
|
|
log "write $ARCHIVE"
|
|
rm -f "$ARCHIVE"
|
|
# write to temp then atomic move to avoid truncated archive on interrupt
|
|
TMP_ARCHIVE="$(mktemp "${TMPDIR:-/tmp}/wordcloud-release-tar.XXXXXX").tar.gz"
|
|
tar -C "$STAGING_DIR" -czf "$TMP_ARCHIVE" "$PKG_NAME"
|
|
mv -f "$TMP_ARCHIVE" "$ARCHIVE"
|
|
|
|
# verify archive
|
|
tar -tzf "$ARCHIVE" >/dev/null
|
|
python3 - <<PY
|
|
import tarfile
|
|
tf = tarfile.open("$ARCHIVE", "r:gz")
|
|
names = tf.getnames()
|
|
assert "wordcloud/docker-compose.yml" in names
|
|
assert "wordcloud/install-ubuntu.sh" in names
|
|
assert "wordcloud/backend/wordcloud_generate_hybrid.py" in names
|
|
assert "wordcloud/backend/service/app.py" in names
|
|
assert "wordcloud/backend/service/line_spacing.py" in names
|
|
assert "wordcloud/frontend/src/pages/CanvasStudio.tsx" in names
|
|
assert "wordcloud/ai-converter/Dockerfile" in names
|
|
assert "wordcloud/ai-converter/app.py" in names
|
|
assert not any(".venv" in n for n in names), "venv leaked into archive"
|
|
assert not any("/service_workspace/" in n and not n.endswith("/service_workspace") for n in names), "workspace leaked"
|
|
print("archive entries:", len(names))
|
|
print("archive ok")
|
|
PY
|
|
|
|
log "done"
|
|
echo "---- release root ----"
|
|
ls -la "$RELEASE_DIR"
|
|
echo "---- sizes ----"
|
|
du -sh "$RELEASE_DIR" "$ARCHIVE"
|
|
echo "---- backend top ----"
|
|
ls -la "$RELEASE_DIR/backend"
|
|
echo "---- frontend src files ----"
|
|
find "$RELEASE_DIR/frontend/src" -type f | wc -l
|