From 74a592024c084953240a49d9bd213f694f172965 Mon Sep 17 00:00:00 2001 From: butubb <1422726308@qq.com> Date: Wed, 7 Oct 2026 11:13:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=83=A8=E7=BD=B2=E6=94=B9=E4=B8=BA=20?= =?UTF-8?q?git=20=E9=A9=B1=E5=8A=A8=EF=BC=8C=E5=AE=B9=E5=99=A8=E4=BB=A5?= =?UTF-8?q?=E5=AE=BF=E4=B8=BB=E7=94=A8=E6=88=B7=E8=BF=90=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 服务器实测可经 Cloudflare 443 访问 Gitea 且 git 协议正常(此前我只测了 13000 端口就断言不可达,是错的),因此不再需要 tar + SFTP。 - docker-compose: 增加 user: "1000:1000"。容器此前以 root 运行,写进挂载目录的每轮 jsonl 产物都是 root 属主,导致宿主用户连自己的部署目录都挪不动 —— 这在把部署迁到 /mnt/data 时实际发生了 - deploy.sh: 一条命令走完 拉代码 →(webui/ 有改动时)重建前端 → 重启容器。前端产物 api/webui 是 gitignore 的,git pull 带不过来,必须在服务器上重建一次 - Dockerfile: 补 npm 包。corepack 只管 yarn/pnpm 不管 npm,而前端要在服务器上重建;这样服务器只需要 Docker,不必另配 Node 环境 --- Dockerfile | 6 +++-- deploy.sh | 60 ++++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 14 +++++++---- 3 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 deploy.sh diff --git a/Dockerfile b/Dockerfile index 308d272..e8abe0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,9 @@ ENV PYTHONUNBUFFERED=1 \ # every stored timestamp in UTC. nodejs is for PyExecJS: douyin/help.py compiles # libs/douyin.js at *import* time, and because main.py imports every platform, # that single platform being importable-or-not decides whether the whole app -# (and the environment self-check) comes up. +# (and the environment self-check) comes up. npm rides along so the WebUI can be +# rebuilt on the server (see deploy.sh) instead of only on a workstation -- +# corepack is present but does not cover npm, only yarn and pnpm. # # The pip mirror is set for the same reason as the apt one: this host's route to # the public index is slow. @@ -44,7 +46,7 @@ RUN set -eux; \ apt-get install -y --no-install-recommends \ build-essential pkg-config default-libmysqlclient-dev \ libgl1 libglib2.0-0 libsm6 libxext6 libxrender1 libxcb1 libgomp1 \ - tzdata nodejs; \ + tzdata nodejs npm; \ rm -rf /var/lib/apt/lists/* WORKDIR /app diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..e2ff21d --- /dev/null +++ b/deploy.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# +# 更新这台机器上的部署。用法: +# +# ./deploy.sh +# +# 为什么需要脚本而不是一句 `git pull && docker compose up -d`: +# 前端产物 api/webui 是 gitignore 的(它由 vite 生成),git pull 带不过来。 +# 所以代码更新之后必须在服务器上重建一次前端,否则页面还是旧的。 +# 这一步在容器里做,好处是服务器不需要装 Node —— 只有 Docker。 +# +# node_modules 和 npm 缓存都留在挂载目录内,重复构建不会重新下载。 +set -euo pipefail +cd "$(dirname "$0")" + +# 用镜像里的解释器跑,宿主机的 Python 版本无关。 +IMAGE=mediacrawler:latest + +before=$(git rev-parse HEAD) +git pull --ff-only +after=$(git rev-parse HEAD) + +if [ "$before" = "$after" ]; then + echo "== 代码已是最新($after)" +else + echo "== 代码更新 $before -> $after" + git --no-pager log --oneline "$before..$after" | sed 's/^/ /' +fi + +# 前端重建的两种情况:产物根本不存在(首次部署),或 webui/ 有改动。 +if [ ! -f api/webui/index.html ]; then + need_build=1 + reason="前端产物不存在" +elif [ "$before" != "$after" ] && ! git diff --quiet "$before" "$after" -- webui/; then + need_build=1 + reason="webui/ 有改动" +else + need_build=0 + reason="" +fi + +if [ "$need_build" = "1" ]; then + echo "== 重建前端($reason)" + # -u 1000:1000 而不是 root:这里产出的文件要留在这个目录里给后面用, + # 以 root 生成的 node_modules 会让下次构建和人工清理都变得别扭。 + # HOME 指向挂载目录,这样 npm 的缓存在宿主机上,重建时能复用。 + docker run --rm \ + -u 1000:1000 \ + -w /app/webui \ + -v "$PWD:/app" \ + -e HOME=/app/webui \ + -e npm_config_registry=https://registry.npmmirror.com \ + "$IMAGE" sh -c 'npm ci --no-audit --no-fund && npm run build' +else + echo "== 前端无改动,跳过构建" +fi + +echo "== 重启容器" +docker compose up -d +docker compose ps diff --git a/docker-compose.yml b/docker-compose.yml index 16db640..2fee7db 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,13 @@ services: container_name: mediacrawler restart: unless-stopped + # Run as the user that owns this checkout. Without it the container is root, + # and every file it writes into the mounted tree -- the crawler's per-run + # jsonl output above all -- comes out root-owned. That does not break the app, + # but it does lock the operator out of moving or deleting their own + # deployment, which is exactly what happened the first time this was deployed. + user: "1000:1000" + # host networking is a requirement, not a convenience: the crawler attaches # to the operator's Chrome at 127.0.0.1:9222, and inside a bridge network # that loopback is the container's own, where no browser is listening. @@ -20,8 +27,7 @@ services: volumes: # The code is mounted rather than baked in, so shipping a change is - # "re-upload the tarball, restart" instead of an image rebuild. Only the - # dependencies live in the image, because those are the expensive part and - # they change rarely -- rebuild only when requirements.txt or the - # Dockerfile itself changes. + # "git pull, restart" instead of an image rebuild. Only the dependencies + # live in the image, because those are the expensive part and they change + # rarely -- rebuild only when requirements.txt or the Dockerfile changes. - ./:/app