From 3dc5f02aafa02e60aec3091fde5cb161779d9c1c Mon Sep 17 00:00:00 2001 From: Ash Date: Tue, 12 May 2026 22:18:49 +0800 Subject: [PATCH] =?UTF-8?q?ci:=20=E5=8A=A0=20dev=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E9=83=A8=E7=BD=B2=E5=B7=A5=E4=BD=9C=E6=B5=81(push=20dev=20?= =?UTF-8?q?=E2=86=92=20=E9=87=8D=E5=BB=BA=20content-harvester=20=E5=AE=B9?= =?UTF-8?q?=E5=99=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .gitea/workflows/deploy.yml:push 到 dev 分支(或 workflow_dispatch)→ dev 开发机 runner (标签 srv1563674)→ checkout → docker build → docker rm -f → docker run(env 走 --env-file .env, 内容来自 gitea repo secret CH_ENV_FILE)→ healthz 检查 → image prune。只触发 dev,master/prod 不碰。 部署参数(端口 8019 / 网络 ai-admin-cuiyue_default / 卷 content-harvester-profile / mem 2g)在 yml 的 env 块里。 DEPLOY.md / README.md 同步更新(dev 自动、prod 仍手动;前置:配 CH_ENV_FILE secret)。 Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitea/workflows/deploy.yml | 90 +++++++++++++++++++++++++++++++++++++ DEPLOY.md | 31 ++++++++++--- README.md | 12 +---- 3 files changed, 117 insertions(+), 16 deletions(-) create mode 100644 .gitea/workflows/deploy.yml diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..9215d9d --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,90 @@ +name: Deploy + +# push 到 dev 分支 → 在 dev 开发机上重新 build 镜像、重建 content-harvester 容器。 +# (只管 dev;prod 仍手动部署 —— 见 DEPLOY.md。) +on: + push: + branches: + - dev + workflow_dispatch: + +jobs: + deploy: + name: Build and deploy (dev) + # dev 开发机的 gitea runner 标签(与主站 AIautomation 的 deploy.yml 一致) + runs-on: srv1563674 + + env: + IMAGE: content-harvester + CONTAINER: content-harvester + HOST_PORT: "8019" # 主机端口(主要给本机调试用) + DOCKER_NETWORK: ai-admin-cuiyue_default # 跟主站 backend 同一个 docker 网络 + PROFILE_VOLUME: content-harvester-profile # 持久浏览器资料目录(过 Cloudflare 用) + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Prepare env file + env: + CH_ENV_FILE: ${{ secrets.CH_ENV_FILE }} + run: | + set -euo pipefail + if [ -n "${CH_ENV_FILE:-}" ]; then + printf '%s\n' "$CH_ENV_FILE" > .env + fi + test -f .env || { + echo "Missing .env — 请在该仓库的 Gitea Actions secrets 里加一个 'CH_ENV_FILE',内容形如:" + echo " HARVESTER_TOKEN=..." + echo " MINIO_ENDPOINT=https://minio.aidfxn.com" + echo " MINIO_BUCKET=clawlerdata" + echo " MINIO_ACCESS_KEY=..." + echo " MINIO_SECRET_KEY=..." + exit 1 + } + # 只打 key 名,不打值 + echo "env keys: $(grep -oE '^[A-Za-z_][A-Za-z0-9_]*' .env | paste -sd, -)" + + - name: Show docker info + run: docker --version + + - name: Build image + run: docker build -t "$IMAGE:latest" . + + - name: Recreate container + run: | + set -euo pipefail + docker rm -f "$CONTAINER" >/dev/null 2>&1 || true + docker run -d \ + --name "$CONTAINER" \ + --restart unless-stopped \ + --network "$DOCKER_NETWORK" \ + -p "${HOST_PORT}:8000" \ + --env-file .env \ + -v "${PROFILE_VOLUME}:/data/browser_profile" \ + --memory=2g --cpus=2 \ + "$IMAGE:latest" + + - name: Health check + run: | + set -e + for i in $(seq 1 20); do + if docker exec "$CONTAINER" python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=3).read()" >/dev/null 2>&1; then + echo "healthz ok" + docker ps --filter "name=$CONTAINER" --format '{{.Names}} | {{.Status}} | {{.Ports}}' + exit 0 + fi + echo "waiting for healthz... ($i/20)" + sleep 3 + done + echo "health check failed" + docker logs --tail 80 "$CONTAINER" || true + exit 1 + + - name: Cleanup env file + if: always() + run: rm -f .env + + - name: Prune dangling images + if: always() + run: docker image prune -f >/dev/null 2>&1 || true diff --git a/DEPLOY.md b/DEPLOY.md index 80bb98e..970798e 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -8,27 +8,46 @@ - 代码:`git clone -b dev https://git.aidfxn.com/admin/content-harvester.git`,以后 `git pull origin dev`。 - 对接的主站 Skill:「内容网站采集(content-harvester)」(`executor_type=lobster`,`lobster_api_url=http://content-harvester:8000`,`lobster_api_key` = `.env.dev` 里的 `HARVESTER_TOKEN`)。 -## 重新部署 / 更新(改了代码、push 了 dev 之后) +## 重新部署 / 更新 -本仓库**没有自动部署 workflow**,要手动: +### dev:自动部署(push `dev` 分支即触发) + +`.gitea/workflows/deploy.yml` —— **push 到 `dev` 分支**(或在 gitea 上手动 `workflow_dispatch`)→ dev 开发机的 gitea runner(标签 `srv1563674`,跟主站 AIautomation 的 deploy.yml 用同一个)自动:`actions/checkout` → `docker build -t content-harvester .` → `docker rm -f content-harvester` → `docker run -d ...`(env 走 `--env-file .env`)→ healthz 检查 → `docker image prune`。**只管 dev**;`master`/prod 不在 `on.push.branches` 里,不会被这个工作流碰(prod 仍走下面的手动流程)。 + +**前置条件(一次性)**:在该仓库的 Gitea Actions secrets 里加一个 **`CH_ENV_FILE`**,内容就是要喂给容器的 env 文件全文(等价于 `.env.dev`),至少: + +``` +HARVESTER_TOKEN=<和主站采集 Skill 的 lobster_api_key 一致> +MINIO_ENDPOINT=https://minio.aidfxn.com +MINIO_BUCKET=clawlerdata +MINIO_ACCESS_KEY=<...> +MINIO_SECRET_KEY=<...> +# 可选:HEADLESS=0 / BROWSER_CHANNEL=chrome / MAX_PAGES=... / DEFAULT_MAX_IMAGES=... 等 +``` + +没配 `CH_ENV_FILE` 的话工作流会在「Prepare env file」那步报错退出(不会拿一份残缺的 env 去跑)。工作流里硬编码的部署参数(非密钥):host 端口 `8019`、docker 网络 `ai-admin-cuiyue_default`、卷 `content-harvester-profile:/data/browser_profile`、`--memory=2g --cpus=2`、`--restart unless-stopped` —— 要改这些就改 `deploy.yml` 顶部的 `env:` 块。 + +> 工作流在 runner 的 workspace 里 build(`actions/checkout` 取的那份代码),**不动 `/opt/content-harvester`** —— 那个目录会逐渐过时,「现在 dev 跑的是哪份代码」要看运行中的容器(`docker exec content-harvester ...`),别看 `/opt/content-harvester` 的 git。 + +### 手动部署(prod;或 dev 想临时绕过工作流) ```bash cd /opt/content-harvester -git pull -q origin dev +git pull -q origin # prod 用 master docker build -t content-harvester . docker rm -f content-harvester docker run -d --name content-harvester \ --restart unless-stopped \ - --network <主站 backend 所在的 docker 网络> \ + --network ai-admin-cuiyue_default \ # 跟主站 backend 同一个 docker 网络 -p 8019:8000 \ - --env-file /opt/content-harvester/.env.dev \ # 这个文件要先放到服务器上(本地 scp 过去,或在服务器上照样写一份) + --env-file /opt/content-harvester/.env.dev \ # 这个文件要先放到服务器上(本地 scp 过去,或在服务器上照样写一份;内容同 CH_ENV_FILE) -v content-harvester-profile:/data/browser_profile \ --memory=2g --cpus=2 \ content-harvester sleep 6 && curl -s localhost:8019/healthz # 期望 {"ok":true,"headless":false,...} ``` -> 注:`.env.dev` 被 `.gitignore` 排除,`git pull` 不会带它过去。第一次在服务器上部署时手动把它放到 `/opt/content-harvester/.env.dev`(从本地 scp,或照本地内容写一份)。或者也可以不用 env-file、把那几个 `-e KEY=VALUE` 直接展开写在 `docker run` 里(但 token/凭据就会出现在 shell history / `docker inspect`)。 +> 注:`.env.dev` 被 `.gitignore` 排除,`git pull` 不会带它过去。第一次在服务器上手动部署时把它放到 `/opt/content-harvester/.env.dev`(从本地 scp,或照本地内容写一份)。或者也可以不用 env-file、把那几个 `-e KEY=VALUE` 直接展开写在 `docker run` 里(但 token/凭据就会出现在 shell history / `docker inspect`)。 如果一时没法在服务器上 build,也可以本地 `docker build` 后 `docker save content-harvester | gzip | ssh ... 'gunzip | docker load'` —— 但**注意 CPU 架构**:Mac 一般是 arm64,服务器多是 x86_64,本地构建的 arm64 镜像在服务器跑不了。所以一般还是在服务器上 build。 diff --git a/README.md b/README.md index bb8e7cb..08f1fe7 100644 --- a/README.md +++ b/README.md @@ -201,16 +201,8 @@ curl -s localhost:8019/healthz # {"ok":true,"headless":false,...} ### 更新 -本仓库**没有 gitea 自动部署 workflow**,要手动: - -```bash -cd <部署目录> && git pull origin dev -docker build -t content-harvester . -docker rm -f content-harvester -docker run -d ... content-harvester # 同上面那串参数 -``` - -> 各环境的具体服务器、目录、容器、token / 凭据见 `DEPLOY.md` 与本地运维记录(不入库)。 +- **dev**:有自动部署 —— `.gitea/workflows/deploy.yml`,**push 到 `dev` 分支**(或 gitea 上 `workflow_dispatch`)→ dev 开发机的 runner 自动 `docker build` + 重建 `content-harvester` 容器 + healthz 检查。前置:在该仓库的 Gitea Actions secrets 里配一个 `CH_ENV_FILE`(等价于 `.env.dev` 全文,`HARVESTER_TOKEN` + 4 个 `MINIO_*` + 可选项)。 +- **prod / 手动**:`master`/prod 不走那个工作流,仍手动:`cd <部署目录> && git pull origin && docker build -t content-harvester . && docker rm -f content-harvester && docker run -d ... content-harvester`(完整命令、各环境的服务器/目录/容器/凭据见 `DEPLOY.md` 与本地运维记录,不入库)。 ---