Add advanced canvas editing and Ubuntu deployment
This commit is contained in:
Executable
+171
@@ -0,0 +1,171 @@
|
||||
#!/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/"
|
||||
|
||||
# ── 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 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
|
||||
Reference in New Issue
Block a user