diff --git a/.github/workflows/deploy-tencent.yml b/.github/workflows/deploy-tencent.yml index c6c905a..cc74175 100644 --- a/.github/workflows/deploy-tencent.yml +++ b/.github/workflows/deploy-tencent.yml @@ -29,9 +29,8 @@ env: jobs: build: - name: Build Web image + name: Validate deployment image runs-on: ubuntu-latest - environment: tencent steps: - name: Checkout uses: actions/checkout@v4 @@ -40,47 +39,27 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Build image + if: github.event_name == 'pull_request' uses: docker/build-push-action@v6 with: context: apps/web file: apps/web/Dockerfile platforms: linux/amd64 - outputs: type=docker,dest=${{ runner.temp }}/meteortest-image.tar tags: ${{ env.IMAGE_NAME }}:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max - - name: Compress image artifact - if: github.event_name != 'pull_request' - run: gzip -1 < "${RUNNER_TEMP}/meteortest-image.tar" > deploy/meteortest-image.tar.gz - - - name: Upload image artifact to Tencent - if: github.event_name != 'pull_request' - env: - DEPLOY_KEY: ${{ secrets.TENCENT_ARTIFACT_SSH_KEY }} - DEPLOY_KNOWN_HOSTS: ${{ secrets.TENCENT_ARTIFACT_KNOWN_HOSTS }} - DEPLOY_HOST: ${{ vars.TENCENT_ARTIFACT_HOST }} - DEPLOY_USER: ${{ vars.TENCENT_ARTIFACT_USER }} - run: | - key_file="${RUNNER_TEMP}/artifact-upload-key" - known_hosts_file="${RUNNER_TEMP}/artifact-known-hosts" - printf '%s\n' "${DEPLOY_KEY}" > "${key_file}" - printf '%s\n' "${DEPLOY_KNOWN_HOSTS}" > "${known_hosts_file}" - chmod 0600 "${key_file}" "${known_hosts_file}" - ssh -i "${key_file}" -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes \ - -o UserKnownHostsFile="${known_hosts_file}" \ - "${DEPLOY_USER}@${DEPLOY_HOST}" "${GITHUB_SHA}" \ - < deploy/meteortest-image.tar.gz - rm -f deploy/meteortest-image.tar.gz "${key_file}" "${known_hosts_file}" - - - name: Upload deployment manifest + - name: Upload validated source artifact if: github.event_name != 'pull_request' uses: actions/upload-artifact@v4 with: - name: meteortest-deployment-${{ github.sha }} + name: meteortest-source-${{ github.sha }} path: | + apps/web deploy/docker-compose.yml deploy/deploy-container.sh + !apps/web/node_modules + !apps/web/.next retention-days: 7 deploy: @@ -90,11 +69,11 @@ jobs: runs-on: [self-hosted, linux, x64, tencent, meteortest] environment: tencent steps: - - name: Download deployment manifest + - name: Download validated source artifact uses: actions/download-artifact@v4 with: - name: meteortest-deployment-${{ github.sha }} - path: deployment + name: meteortest-source-${{ github.sha }} + path: source - name: Resolve target id: target @@ -114,8 +93,8 @@ jobs: echo "public_url=https://mt-pre.jcmeteor.com/" >> "$GITHUB_OUTPUT" fi - - name: Load image artifact - run: gzip -dc "/srv/deploy-inbox/meteortest/${GITHUB_SHA}.tar.gz" | docker load + - name: Build image from artifact + run: docker build --tag "meteortest-web:${GITHUB_SHA}" --file source/apps/web/Dockerfile source/apps/web - name: Deploy env: @@ -128,9 +107,5 @@ jobs: RUNTIME_ENV_FILE: /etc/meteortest/meteortest-web.env SHADOW_PORT: ${{ steps.target.outputs.shadow_port }} run: | - chmod +x deployment/deploy-container.sh - deployment/deploy-container.sh - - - name: Remove uploaded image artifact - if: success() - run: rm -f "/srv/deploy-inbox/meteortest/${GITHUB_SHA}.tar.gz" + chmod +x source/deploy/deploy-container.sh + source/deploy/deploy-container.sh diff --git a/README.md b/README.md index fe084ca..5e77394 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,7 @@ main -> mt-pre.jcmeteor.com release -> meteortest.jcmeteor.com ``` -The detailed Docker image artifact architecture, runner, branch, environment, port mapping, PM2 migration, and rollback flow lives in `docs/tencent-docker-deployment.md`. +The detailed Docker/source artifact architecture, runner, branch, environment, port mapping, PM2 migration, and rollback flow lives in `docs/tencent-docker-deployment.md`. Production publishing is automated by the GitHub Actions `Release Manager` workflow: diff --git a/deploy/receive-image-upload.sh b/deploy/receive-image-upload.sh deleted file mode 100644 index c6f03c6..0000000 --- a/deploy/receive-image-upload.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -app_name="${1:-}" -image_sha="${SSH_ORIGINAL_COMMAND:-}" - -if [[ ! "${app_name}" =~ ^(meteorvoice|meteortest)$ ]]; then - echo "Unsupported application" >&2 - exit 1 -fi - -if [[ ! "${image_sha}" =~ ^[0-9a-f]{40}$ ]]; then - echo "Invalid image SHA" >&2 - exit 1 -fi - -target_dir="/srv/deploy-inbox/${app_name}" -target_file="${target_dir}/${image_sha}.tar.gz" -temporary_file="${target_file}.uploading" - -mkdir -p "${target_dir}" -umask 0027 -ulimit -f 262144 -timeout 900 tee "${temporary_file}" >/dev/null -test -s "${temporary_file}" -mv "${temporary_file}" "${target_file}" -printf 'Stored %s\n' "${target_file}" diff --git a/docs/README.md b/docs/README.md index cf2cfd5..0f1ffa1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,7 +23,7 @@ This is the single entry point for MeteorTest documentation. Start here for new | Supabase runbook | `supabase-account-data-runbook.md` / `supabase-account-data-runbook.zh-CN.md` | Auth/RLS, preferences, AI history, display refs SQL execution and verification. | | Local Agent operations | `local-agent-operations.md` / `local-agent-operations.zh-CN.md` | Agent daemon, check interval, heartbeat, logs, OpenClaw checks. | | Public preview deployment | `vercel-public-preview.md` / `vercel-public-preview.zh-CN.md` | Vercel public preview deployment and safety checks. | -| Tencent Docker deployment | `tencent-docker-deployment.md` / `tencent-docker-deployment.zh-CN.md` | Docker image artifact architecture, main/release mapping, PM2 migration, and rollback. | +| Tencent Docker deployment | `tencent-docker-deployment.md` / `tencent-docker-deployment.zh-CN.md` | Docker/source artifact architecture, main/release mapping, PM2 migration, and rollback. | | Release automation | `release-manager.md` | GitHub Actions release workflow, release PR automation, and recovery commands. | | Private Agent loop | `private-agent-preview-loop.md` / `private-agent-preview-loop.zh-CN.md` | Validation flow for public Web plus private Agent execution. | | Data exposure boundary | `internal-id-exposure-hardening.md` / `internal-id-exposure-hardening.zh-CN.md` | Internal UUIDs, public refs, DTO/View Model rules. | diff --git a/docs/README.zh-CN.md b/docs/README.zh-CN.md index f11ba54..f926314 100644 --- a/docs/README.zh-CN.md +++ b/docs/README.zh-CN.md @@ -23,7 +23,7 @@ | Supabase 手册 | `supabase-account-data-runbook.zh-CN.md` / `supabase-account-data-runbook.md` | Auth/RLS、账号偏好、AI 历史、display refs 的 SQL 执行与验证。 | | Local Agent 运维 | `local-agent-operations.zh-CN.md` / `local-agent-operations.md` | Agent 常驻、检查频率、心跳、日志、OpenClaw 巡检。 | | 公网预览部署 | `vercel-public-preview.zh-CN.md` / `vercel-public-preview.md` | Vercel 公网预览部署和安全检查。 | -| 腾讯云 Docker 部署 | `tencent-docker-deployment.zh-CN.md` / `tencent-docker-deployment.md` | Docker 镜像制品架构、main/release 映射、PM2 迁移和回滚。 | +| 腾讯云 Docker 部署 | `tencent-docker-deployment.zh-CN.md` / `tencent-docker-deployment.md` | Docker + 源码 Artifact 架构、main/release 映射、PM2 迁移和回滚。 | | 发布自动化 | `release-manager.md` | GitHub Actions 发布入口、release PR 自动化和中断恢复命令。 | | 私有 Agent 闭环 | `private-agent-preview-loop.zh-CN.md` / `private-agent-preview-loop.md` | 私有 Agent 连接公网 Web 后端的验证流程。 | | 数据暴露边界 | `internal-id-exposure-hardening.zh-CN.md` / `internal-id-exposure-hardening.md` | 内部 UUID、公开引用、DTO/View Model 规则。 | diff --git a/docs/tencent-docker-deployment.md b/docs/tencent-docker-deployment.md index 9fc44c3..bd4893d 100644 --- a/docs/tencent-docker-deployment.md +++ b/docs/tencent-docker-deployment.md @@ -17,9 +17,9 @@ Nginx MUST continue binding public ports 80/443. Containers MUST publish only to 1. A GitHub-hosted runner checks out the requested commit. 2. CI installs dependencies in `apps/web`, then runs lint and the production build. Repository-wide validation continues to cover the Python Agent separately. -3. CI exports the multi-stage Next.js standalone image as a compressed Docker image artifact. +3. CI validates the Docker build on pull requests and uploads compact source as a GitHub Actions artifact after merge. 4. The image is tagged with an immutable commit SHA. Branch and release tags MAY be aliases, but deployment MUST resolve to the SHA tag. -5. The MeteorTest Tencent runner loads the uploaded artifact into Docker and updates only the matching Compose project. +5. The MeteorTest Tencent runner builds from the artifact with the server-side Docker layer cache and updates only the matching Compose project. 6. The runner waits for container health and verifies the public domain. 7. A failed health check MUST restore the previous image SHA. @@ -46,7 +46,7 @@ Docker migration covers only `apps/web`. The private Python Local Agent MUST rem ## Configuration and secrets -Real Web credentials remain in `/etc/meteortest/meteortest-web.env`. Compose injects the file at container startup. The GitHub-hosted runner sends the compressed image directly to a Tencent inbox through an SSH key restricted to that upload command. The self-hosted runner only loads and deploys it. No container-registry password or interactive upload shell is required. Supabase service-role, AI provider, Agent, and project execution secrets MUST NOT enter the image artifact. +Real Web credentials remain in `/etc/meteortest/meteortest-web.env`. Compose injects the file at container startup. The self-hosted runner downloads the validated source artifact, builds with the server-side Docker layer cache, and deploys it. It does not pull the Git repository and requires no container-registry password. Supabase service-role, AI provider, Agent, and project execution secrets MUST NOT enter the source artifact or image. ## Compose requirements @@ -79,7 +79,7 @@ Do not run `pm2 kill`. Keep PM2 definitions and source directories until both en 1. resolve the immutable image SHA; 2. record the currently running SHA; -3. download and load the image artifact; +3. download the source artifact and build the image with Docker layer cache; 4. update the matching Compose project from the local immutable image; 5. wait for container health; 6. verify the localhost port and public domain; diff --git a/docs/tencent-docker-deployment.zh-CN.md b/docs/tencent-docker-deployment.zh-CN.md index 07c744b..3e80fc5 100644 --- a/docs/tencent-docker-deployment.zh-CN.md +++ b/docs/tencent-docker-deployment.zh-CN.md @@ -17,7 +17,7 @@ Nginx MUST 继续监听公网 80/443 端口。容器 MUST 只发布到 `127.0.0. 1. GitHub 托管 Runner 检出目标提交。 2. CI 在 `apps/web` 安装依赖并执行 lint 和生产构建;仓库级验证继续单独覆盖 Python Agent。 -3. CI 将多阶段 Next.js standalone 镜像导出为压缩的 Docker 镜像制品。 +3. CI 在 PR 中验证 Docker 构建;合并后将精简源码作为 GitHub Actions Artifact 上传。 4. 镜像使用不可变 commit SHA 标签;分支和版本标签 MAY 作为别名,但部署 MUST 最终解析到 SHA 标签。 5. MeteorTest 专属腾讯 Runner 下载 Artifact、将不可变镜像加载到 Docker,并只更新对应 Compose 项目。 6. Runner 等待容器健康并验证公网域名。 @@ -46,7 +46,7 @@ Nginx MUST 继续监听公网 80/443 端口。容器 MUST 只发布到 `127.0.0. ## 配置与密钥 -真实 Web 凭据继续保存在 `/etc/meteortest/meteortest-web.env`,由 Compose 在容器启动时注入。GitHub 托管 Runner 使用只允许写入指定目录的 SSH 密钥,将压缩镜像直传到腾讯云制品收件箱;自托管 Runner 只负责加载和部署。该通道不需要镜像仓库密码,上传账号也不能获得交互式 Shell。Supabase service-role、AI provider、Agent 和项目执行密钥 MUST NOT 进入镜像制品。 +真实 Web 凭据继续保存在 `/etc/meteortest/meteortest-web.env`,由 Compose 在容器启动时注入。自托管 Runner 下载经验证的源码 Artifact,利用服务器 Docker 层缓存构建并部署,不拉取 Git 仓库,也不需要镜像仓库密码。Supabase service-role、AI provider、Agent 和项目执行密钥 MUST NOT 进入源码 Artifact 或镜像。 ## Compose 要求 @@ -79,7 +79,7 @@ Nginx MUST 继续监听公网 80/443 端口。容器 MUST 只发布到 `127.0.0. 1. 解析新的不可变镜像 SHA; 2. 记录当前运行 SHA; -3. 下载并加载镜像 Artifact; +3. 下载源码 Artifact,并利用 Docker 层缓存构建镜像; 4. 使用本地不可变镜像更新对应 Compose 项目; 5. 等待容器健康; 6. 验证 localhost 端口和公网域名;