# Source-Only Deployment Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Remove every active Docker dependency and make Ubuntu source deployment, external infrastructure, and local pre-existing Redis the only supported runtime path. **Architecture:** The root workspace keeps one release gate that first runs the existing complete verification and then validates source artifacts against systemd and Nginx templates. Playwright's API launcher performs a read-only TCP preflight against the developer-managed Redis at `127.0.0.1:6379` and fails without starting infrastructure. Historical Docker evidence remains unchanged as history, while current architecture, governance, and GOV-009 decision records explicitly supersede it. **Tech Stack:** Node.js 22.x, pnpm 10.25.0, Node test runner, Vue 3/Vite, NestJS, Playwright, Redis, MySQL 5.7.42, RocketMQ 5.x, Ubuntu 22.04/24.04 LTS, systemd, Nginx. ## Global Constraints - Production supports Ubuntu source deployment only; Docker is absent from development, test, build, and production paths. - Redis, MySQL 5.7.42, and RocketMQ 5.x are external services; project scripts never install, start, stop, or delete them. - Local development and browser E2E use the existing Redis at `127.0.0.1:6379`; unavailability must fail fast without fallback. - MySQL is fixed to 5.7.42 with `utf8mb4`, `utf8mb4_general_ci`, and `+08:00`; no production database may be connected, probed, or read. - Node.js is fixed to 22.x and pnpm to 10.25.0; the repository does not install or replace system Node.js. - Source releases use immutable `/opt/a-share-analysis/releases//` directories and atomically switch `/opt/a-share-analysis/current`. - API, Scheduler, and Worker use separate systemd units and separate `/etc/a-share-analysis/*.env` files under the non-root `a-share` account. - Existing GOV-006 and GOV-009 records of past Docker decisions and successful validation remain truthful historical evidence; current records must state that the source-only decision supersedes them. - No `v0.0.8` tag is created until the user separately confirms the implemented architecture. - Every implementation commit uses Chinese Conventional Commit with `GOV-009`, passes GitNexus change detection, and is pushed immediately to the project Gitea remote. --- ## File Map - `tools/deployment/verify-source-deployment.mjs`: source-release artifact and deployment-template verifier. - `tools/deployment/verify-source-deployment.test.mjs`: verifier RED/GREEN coverage using isolated fixture directories. - `tools/governance/environment-baseline.test.mjs`: active environment, documentation, systemd, Nginx, and no-Docker policy tests. - `server/tools/run-test-api.mjs`: Playwright API launcher and external local Redis preflight. - `server/tools/run-test-api.test.mjs`: launcher behavior tests proving unavailable Redis fails without Docker fallback. - `package.json`: source-only test and release commands. - `deploy/systemd/*.service.example`: API/Scheduler/Worker production process definitions, retained and verified. - `deploy/nginx/a-share-analysis.conf.example`: source Web static hosting and `/api/` reverse proxy, retained and verified. - `README.md`, `docs/index.md`, `docs/architecture/system-architecture.md`: current public architecture and deployment entry points. - `docs/governance/code-development-standards.md`, `docs/governance/development-workflow.md`: mandatory source-only engineering and release rules. - `docs/deployment/local-development.md`, `docs/deployment/ubuntu-source-production.md`: the only current development and production runbooks. - `docs/features/GOV-009-application-scaffold-baseline/*.md`: requirements, PRD, design, test, release, rollback, architecture review, and traceability updates. - `docs/features/GOV-006-environment-deployment-baseline/`: historical archive; only add a supersession pointer if a current-status statement would otherwise be ambiguous. - `CHANGELOG.md`: unreleased policy change, preserving earlier Docker entries as history. - `.dockerignore`, `web/Dockerfile`, `web/nginx.conf`, `server/Dockerfile`, `deploy/compose/*`, `docs/deployment/docker-compose-production.md`, `tools/deployment/verify-docker.mjs`, `tools/deployment/verify-image-references*`: remove obsolete active Docker artifacts. --- ### Task 1: Establish Source-Only Deployment Tests **Files:** - Create: `tools/deployment/verify-source-deployment.test.mjs` - Modify: `tools/governance/environment-baseline.test.mjs` - Modify: `package.json` **Interfaces:** - Consumes: repository paths and the existing `deploy/systemd/`, `deploy/nginx/`, `web/dist/`, and `server/dist/` conventions. - Produces: root scripts `test:deployment` and `verify:source`; failing tests that define the source-only repository contract before implementation. - [x] **Step 1: Add failing fixture tests for the source verifier** Create tests that import the not-yet-existing module and define these exact behaviors: ```js import assert from 'node:assert/strict'; import { mkdtemp, mkdir, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import path from 'node:path'; import test from 'node:test'; import { verifySourceDeployment } from './verify-source-deployment.mjs'; test('完整源码发布夹具通过', async () => { const root = await createSourceReleaseFixture(); await assert.doesNotReject(() => verifySourceDeployment(root)); }); test('任一 Docker 活动文件会阻断源码发布', async () => { const root = await createSourceReleaseFixture(); await writeFile(path.join(root, 'Dockerfile'), 'FROM node:22\n'); await assert.rejects(() => verifySourceDeployment(root), /禁止存在 Docker 活动文件.*Dockerfile/); }); test('Server 构建入口缺失会阻断源码发布', async () => { const root = await createSourceReleaseFixture({ omit: 'server/dist/apps/worker/main.js' }); await assert.rejects(() => verifySourceDeployment(root), /Worker 构建入口不存在/); }); test('systemd 配置不是最小权限时会阻断源码发布', async () => { const root = await createSourceReleaseFixture({ apiUser: 'root' }); await assert.rejects(() => verifySourceDeployment(root), /API systemd 必须使用 User=a-share/); }); test('Nginx 未代理 API 或未指向 Web dist 时会阻断源码发布', async () => { const root = await createSourceReleaseFixture({ invalidNginx: true }); await assert.rejects(() => verifySourceDeployment(root), /Nginx/); }); ``` The local `createSourceReleaseFixture` helper must create only the minimum paths needed by the verifier: four build outputs, three unit files with distinct `EnvironmentFile`, and one Nginx file. It must accept `omit`, `apiUser`, and `invalidNginx` options so each negative test changes one condition. - [x] **Step 2: Replace Docker assertions in the environment baseline with source-only assertions** Keep env-template and RocketMQ tests unchanged. Replace Compose/Docker tests with explicit active-path assertions: ```js const forbiddenActivePaths = [ '.dockerignore', 'web/Dockerfile', 'web/nginx.conf', 'server/Dockerfile', 'deploy/compose/development.yml', 'deploy/compose/production.yml.example', 'docs/deployment/docker-compose-production.md', 'tools/deployment/verify-docker.mjs', 'tools/deployment/verify-image-references.mjs', 'tools/deployment/verify-image-references.test.mjs', ]; test('活动仓库不再包含 Docker 运行构建或部署入口', async () => { for (const relativePath of forbiddenActivePaths) { await assert.rejects(access(new URL(relativePath, repoRoot)), { code: 'ENOENT' }); } }); ``` Add focused checks that current docs describe external local Redis, Ubuntu 22.04/24.04, Node 22.x, pnpm 10.25.0, immutable releases, health checks, and rollback. Do not scan historical feature archives for the word Docker because those files are intentional evidence. - [x] **Step 3: Point root deployment scripts at the not-yet-existing source verifier** Change root scripts to the following contract: ```json "test:deployment": "node --test tools/deployment/verify-source-deployment.test.mjs", "verify:source": "node tools/deployment/verify-source-deployment.mjs", "verify:release": "pnpm verify && pnpm verify:source" ``` Remove `check:images` and `verify:docker`. - [x] **Step 4: Run RED tests and record expected failures** Run: ```bash pnpm test:deployment pnpm test:environment-baseline ``` Expected: the deployment test fails because `verify-source-deployment.mjs` does not exist; the environment baseline fails while active Docker artifacts and current Docker documentation still exist. Record both failures in `docs/features/GOV-009-application-scaffold-baseline/qa/test-results.md` as the RED phase. --- ### Task 2: Implement the Source Release Verifier and Remove Docker Artifacts **Files:** - Create: `tools/deployment/verify-source-deployment.mjs` - Delete: `.dockerignore` - Delete: `web/Dockerfile` - Delete: `web/nginx.conf` - Delete: `server/Dockerfile` - Delete: `deploy/compose/development.yml` - Delete: `deploy/compose/production.yml.example` - Delete: `docs/deployment/docker-compose-production.md` - Delete: `tools/deployment/verify-docker.mjs` - Delete: `tools/deployment/verify-image-references.mjs` - Delete: `tools/deployment/verify-image-references.test.mjs` **Interfaces:** - Consumes: `verifySourceDeployment(repositoryRoot?: string | URL): Promise` inputs and the existing build/deployment paths. - Produces: an importable verifier plus the CLI command `pnpm verify:source`, which exits non-zero on incomplete or unsafe source releases. - [x] **Step 1: Run GitNexus impact before modifying or deleting deployment symbols** Run upstream impact for `assertImmutableImageReferences`, `docker`, and `verifyLongRunningProcess`, including tests. Report direct callers, affected processes, and risk to the user. If risk is HIGH or CRITICAL, stop before editing and ask for confirmation as required by `AGENTS.md`. - [x] **Step 2: Implement the minimal source verifier** Implement these exports in one readable file: ```js export const FORBIDDEN_DOCKER_PATHS = Object.freeze([ '.dockerignore', 'Dockerfile', 'web/Dockerfile', 'web/nginx.conf', 'server/Dockerfile', 'deploy/compose/development.yml', 'deploy/compose/production.yml.example', 'tools/deployment/verify-docker.mjs', 'tools/deployment/verify-image-references.mjs', ]); export async function verifySourceDeployment(repositoryRoot = defaultRepositoryRoot) { await assertDockerArtifactsAbsent(repositoryRoot); await assertBuildOutputsPresent(repositoryRoot); await assertSystemdUnits(repositoryRoot); await assertNginxConfig(repositoryRoot); } ``` The implementation must: - use `node:fs/promises` structured file APIs; - reject every listed Docker path if it exists; - require `web/dist/index.html` and `server/dist/apps/{api,scheduler,worker}/main.js`; - parse each unit sufficiently to verify `User=a-share`, `Group=a-share`, `WorkingDirectory=/opt/a-share-analysis/current`, the process-specific `EnvironmentFile`, `ExecStartPre` env readability, and the exact built `ExecStart` entry; - verify Nginx root `/opt/a-share-analysis/current/web/dist`, `location /api/`, and `proxy_pass http://127.0.0.1:3000`; - produce errors that identify the missing or invalid file without printing env values; - print `源码发布产物与 systemd/Nginx 配置验证通过` only in direct CLI mode. - [x] **Step 3: Delete the obsolete Docker artifacts** Delete only the paths listed in this task. Do not delete env templates, systemd units, the production Nginx template, or historical GOV-006/GOV-009 evidence. - [x] **Step 4: Run GREEN deployment tests after creating build outputs** Run: ```bash pnpm build pnpm test:deployment pnpm test:environment-baseline pnpm verify:source ``` Expected: all commands pass; `verify:source` confirms all four build outputs and the deployment templates. - [x] **Step 5: Detect scope, commit, and push the executable deployment change** Run GitNexus `detect_changes(scope: "all")`. Review that only deployment validation and environment-policy flows are affected, then run: ```bash git add package.json tools/deployment tools/governance/environment-baseline.test.mjs deploy web/Dockerfile web/nginx.conf server/Dockerfile .dockerignore docs/deployment/docker-compose-production.md docs/features/GOV-009-application-scaffold-baseline/qa/test-results.md git commit -m "refactor(GOV-009): 切换为仅源码部署门禁" git push origin "$(git branch --show-current)" ``` --- ### Task 3: Make Playwright Depend Only on Existing Local Redis **Files:** - Modify: `server/tools/run-test-api.mjs` - Create: `server/tools/run-test-api.test.mjs` - Modify: `server/package.json` **Interfaces:** - Consumes: TCP endpoint `127.0.0.1:6379` and the existing public production API fixture. - Produces: `checkLocalRedis({ host, port, timeoutMs }): Promise` and `runTestApi(): Promise`; direct execution launches only the API process after Redis succeeds. - [x] **Step 1: Run GitNexus upstream impact for launcher symbols** Run impact for `forwardSignal` and the `run-test-api.mjs` file, including tests. Report the Playwright web-server dependency and any API health flows before editing. Stop for confirmation only if the graph reports HIGH or CRITICAL risk. - [x] **Step 2: Write failing launcher tests** Refactor-safe tests must verify behavior rather than depend on Docker availability: ```js test('Redis 不可达时给出外部服务提示并拒绝启动', async () => { await assert.rejects( checkLocalRedis({ host: '127.0.0.1', port: unusedPort, timeoutMs: 100 }), /本机 Redis.*127\.0\.0\.1.*6379.*启动后重试/, ); }); test('Redis TCP 可达时预检通过', async () => { const server = net.createServer(); // Bind to 127.0.0.1:0, call checkLocalRedis with the assigned port, then close. }); test('API 测试启动器不包含 Docker 子进程路径', async () => { const source = await readFile(new URL('./run-test-api.mjs', import.meta.url), 'utf8'); assert.doesNotMatch(source, /execFileSync|docker|Container/i); }); ``` Use an OS-assigned loopback port for deterministic tests. The user-facing failure message may state the fixed required Redis endpoint even when the injected test port differs. - [x] **Step 3: Run the launcher RED test** Run: ```bash node --test server/tools/run-test-api.test.mjs ``` Expected: FAIL because `checkLocalRedis` and guarded direct-execution behavior do not yet exist. - [x] **Step 4: Refactor the launcher without infrastructure fallback** Remove `execFileSync`, container naming, dynamic Redis port publication, ownership flags, and cleanup. Export and use: ```js export async function checkLocalRedis({ host = '127.0.0.1', port = 6379, timeoutMs = 500, } = {}) { /* TCP connect or throw a sanitized Error */ } export async function runTestApi() { await checkLocalRedis(); const child = spawn(/* existing API command and public fixture */); // Forward SIGINT/SIGTERM only to the child and preserve its exit result. } ``` Guard execution with the existing `process.argv[1]` and `fileURLToPath(import.meta.url)` pattern so tests can import the functions without starting API. Keep `REDIS_PORT: '6379'`; never derive or override it from a container. - [x] **Step 5: Include the new test in the Server test command** Change the Server test script to run both Vitest and the Node launcher test: ```json "test": "vitest run && node --test tools/run-test-api.test.mjs" ``` - [x] **Step 6: Run GREEN and real local Redis checks** Run: ```bash node --test server/tools/run-test-api.test.mjs redis-cli -h 127.0.0.1 -p 6379 ping pnpm --filter @a-share-analysis/server test pnpm verify:e2e ``` Expected: unit tests pass, Redis prints `PONG`, and both Playwright viewports pass using the already-running local Redis. If Redis is unavailable, stop with the new explicit error; do not start or install it. - [x] **Step 7: Detect scope, commit, and push the launcher change** Run GitNexus `detect_changes(scope: "all")`, confirm the impact is limited to Playwright/API startup, then run: ```bash git add server/package.json server/tools/run-test-api.mjs server/tools/run-test-api.test.mjs git commit -m "fix(GOV-009): 移除测试 Redis 容器兜底" git push origin "$(git branch --show-current)" ``` --- ### Task 4: Align Current Architecture, Governance, and Traceability **Files:** - Modify: `README.md` - Modify: `docs/index.md` - Modify: `docs/architecture/system-architecture.md` - Modify: `docs/governance/code-development-standards.md` - Modify: `docs/governance/development-workflow.md` - Modify: `docs/deployment/local-development.md` - Modify: `docs/deployment/ubuntu-source-production.md` - Modify: `CHANGELOG.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/manifest.yaml` - Modify: `docs/features/GOV-009-application-scaffold-baseline/prd.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/requirements.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/design.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/test-plan.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/release-notes.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/rollback.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/architecture-review.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/qa/test-results.md` - Optional modify: `docs/features/GOV-006-environment-deployment-baseline/manifest.yaml` **Interfaces:** - Consumes: the confirmed `source-only-deployment-design.md` decision and actual Task 2/3 behavior. - Produces: one unambiguous current deployment policy with intact historical evidence and a traceable GOV-009 record. - [x] **Step 1: Update current public docs and architecture** Make these statements exact and consistent: - `deploy/` contains env, systemd, and Nginx source-deployment configuration, not a dual deployment entry. - local setup runs `redis-cli -h 127.0.0.1 -p 6379 ping` before source processes and never starts Redis; - Ubuntu source deployment is the sole production path; - production prerequisites are Ubuntu 22.04/24.04 LTS x86_64, Node.js 22.x, pnpm 10.25.0, Nginx, and external MySQL 5.7.42/Redis/RocketMQ 5.x; - release verification runs `pnpm verify:release` before an immutable release directory is activated; - rollback only switches `/opt/a-share-analysis/current` to a prior complete release and restarts systemd services. Remove the active Docker deployment link from `README.md` and `docs/index.md`. - [x] **Step 2: Update mandatory engineering and workflow rules** In code standards, replace the dual-deployment variable rule with: ```text 开发源码进程与 Ubuntu 生产源码部署复用同一变量契约;项目脚本不得安装、启动、停止或删除 Redis、MySQL、RocketMQ 等外部基础设施。 ``` Replace `开发、测试、Docker 和生产环境` with `开发、测试和生产环境`. In workflow rules, require env/systemd/Nginx synchronization and immutable release-directory rollback; remove Compose/image language. State that `verify:release` is the source-release gate and that unavailable external infrastructure is a failed prerequisite, never a container fallback. - [x] **Step 3: Update GOV-009 current records without falsifying history** Revise PRD scope, R7, design configuration/deployment sections, automated test plan, current release notes, rollback, and architecture review to match actual source-only implementation. Add `source_only_deployment_implementation_plan` to `manifest.yaml`. Keep `status: reviewing`, `release: unreleased`, and the architecture confirmation completion gate. In `qa/test-results.md`, preserve dated Docker validation as historical fact and add a later source-only section explaining it is superseded and no longer executable. Do not rewrite old commit results as if Docker was never tested. If GOV-006's manifest presents its Docker approach as current, add only a dated `superseded_by` pointer to the GOV-009 source-only decision; do not rewrite its original PRD/design/test evidence. - [x] **Step 4: Add the unreleased changelog decision** At the top current section, record that the project removed Docker/Compose/image validation and now supports only Ubuntu source deployment with external infrastructure. Leave older entries about when Docker was added unchanged below it. - [x] **Step 5: Run active-document policy tests and scan for contradictions** Run: ```bash pnpm test:environment-baseline rg -n '双部署|同时支持 Docker|Compose 只|Docker Compose 生产|verify:docker|check:images' README.md docs/index.md docs/architecture docs/governance docs/deployment docs/features/GOV-009-application-scaffold-baseline package.json ``` Expected: tests pass. Search results may remain only in explicitly dated historical results or supersession explanations; no current instruction may expose a Docker command or claim Docker support. - [x] **Step 6: Detect scope, commit, and push documentation alignment** Run GitNexus `detect_changes(scope: "all")`, then: ```bash git add README.md CHANGELOG.md docs git commit -m "docs(GOV-009): 统一仅源码部署规范" git push origin "$(git branch --show-current)" ``` --- ### Task 5: Complete Verification, GitNexus, and Two Reviews **Files:** - Modify: `docs/features/GOV-009-application-scaffold-baseline/gitnexus/after.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/gitnexus/comparison.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/gitnexus/detected-changes.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/qa/test-results.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/review/code-review.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/review/standards-review.md` - Modify: `docs/features/GOV-009-application-scaffold-baseline/source-only-deployment-design.md` **Interfaces:** - Consumes: the completed implementation, root verification scripts, GitNexus graph, and Review standards. - Produces: final evidence for the deployment-policy adjustment while GOV-009 remains `reviewing/unreleased` pending user architecture confirmation. - [x] **Step 1: Run focused tests and full source release verification** Run without Docker: ```bash pnpm test:deployment pnpm test:environment-baseline node --test server/tools/run-test-api.test.mjs pnpm verify:release ``` Expected: all pass. `verify:release` must execute the full root verification, desktop/mobile Playwright checks, builds, and `verify:source`; it must not invoke Docker. - [x] **Step 2: Perform visual verification at both configured viewports** Use Playwright/browser screenshots at 1440x900 and 390x844. Confirm the page is nonblank, API health data renders, no content overlaps, and browser console/network contains no blocking error. Save screenshots under the existing ignored/output convention and record results, not binary artifacts, in `qa/test-results.md`. - [x] **Step 3: Refresh GitNexus and record before/after evidence** Run: ```bash npx gitnexus analyze npx gitnexus status npm run governance:finish -- GOV-009 application-scaffold-baseline ``` Then use GitNexus `detect_changes(scope: "compare", base_ref: "cfcd7f13d9a6a35173d01bd8ca1458911190e70f")`. Record node/edge/flow counts, removed Docker verifier symbols, added source verifier symbols, and affected processes in the three GitNexus records. Do not commit `.gitnexus/`. - [x] **Step 4: Conduct code Review** Review for functional regressions, release-gate bypasses, unsafe process spawning, Redis failure semantics, missing build artifacts, and test gaps. Resolve every blocking finding, rerun affected checks, and finish `review/code-review.md` with the exact line: ```text 最终结论:passed ``` - [x] **Step 5: Conduct development-standards Review** Review source-only policy consistency, Chinese comments for non-obvious logic, file responsibility, no secrets, MySQL 5.7 constraints, no production access, historical traceability, Chinese commits, Gitea push state, and the absence of a `v0.0.8` tag. Finish `review/standards-review.md` with the exact line: ```text 最终结论:passed ``` - [x] **Step 6: Re-run final verification after Review edits** Run: ```bash pnpm format:check pnpm verify:release git status --short ``` Expected: formatting and release verification pass. Only intended Review/evidence files remain uncommitted. - [x] **Step 7: Detect final scope, commit, and push evidence** Run GitNexus `detect_changes(scope: "all")`, verify no unexpected application flow changed, then: ```bash git add docs/features/GOV-009-application-scaffold-baseline git commit -m "docs(GOV-009): 封存仅源码部署复核证据" git push origin "$(git branch --show-current)" ``` - [x] **Step 8: Confirm remote consistency without releasing** Run: ```bash git status --short --branch git ls-remote --heads origin "$(git branch --show-current)" git tag --list v0.0.8 ``` Expected: worktree clean, local HEAD equals the remote branch SHA, and `v0.0.8` has no output. Present the implemented architecture and remaining GOV-009 completion gate to the user for the separately required confirmation. --- ## Self-Review Record - Spec coverage: every confirmed deletion, external-service boundary, source gate, Ubuntu release rule, Redis failure behavior, historical evidence rule, Review requirement, and no-tag constraint maps to a task above. - Placeholder scan: the plan contains no deferred implementation placeholders; code interfaces, commands, expected RED/GREEN results, and commit boundaries are explicit. - Type and name consistency: `verifySourceDeployment`, `checkLocalRedis`, `runTestApi`, `test:deployment`, `verify:source`, and `verify:release` retain the same names across production, tests, and documentation tasks. - Scope: this plan changes deployment policy and validation only; it does not add market features, user systems, infrastructure installation, database migrations, or RocketMQ message sending.