Files
a-share-analysis/docs/features/GOV-009-application-scaffold-baseline/source-only-deployment-implementation-plan.md

26 KiB

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/<version-or-commit>/ 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.

  • Step 1: Add failing fixture tests for the source verifier

Create tests that import the not-yet-existing module and define these exact behaviors:

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.

  • 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:

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.

  • Step 3: Point root deployment scripts at the not-yet-existing source verifier

Change root scripts to the following contract:

"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.

  • Step 4: Run RED tests and record expected failures

Run:

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<void> 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.

  • 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.

  • Step 2: Implement the minimal source verifier

Implement these exports in one readable file:

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.

  • 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.

  • Step 4: Run GREEN deployment tests after creating build outputs

Run:

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.

  • 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:

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<void> and runTestApi(): Promise<void>; direct execution launches only the API process after Redis succeeds.

  • 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.

  • Step 2: Write failing launcher tests

Refactor-safe tests must verify behavior rather than depend on Docker availability:

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.

  • Step 3: Run the launcher RED test

Run:

node --test server/tools/run-test-api.test.mjs

Expected: FAIL because checkLocalRedis and guarded direct-execution behavior do not yet exist.

  • Step 4: Refactor the launcher without infrastructure fallback

Remove execFileSync, container naming, dynamic Redis port publication, ownership flags, and cleanup. Export and use:

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.

  • 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:

"test": "vitest run && node --test tools/run-test-api.test.mjs"
  • Step 6: Run GREEN and real local Redis checks

Run:

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.

  • 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:

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.

  • 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.

  • Step 2: Update mandatory engineering and workflow rules

In code standards, replace the dual-deployment variable rule with:

开发源码进程与 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.

  • 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.

  • 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.

  • Step 5: Run active-document policy tests and scan for contradictions

Run:

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.

  • Step 6: Detect scope, commit, and push documentation alignment

Run GitNexus detect_changes(scope: "all"), then:

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.

  • Step 1: Run focused tests and full source release verification

Run without Docker:

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.

  • 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.

  • Step 3: Refresh GitNexus and record before/after evidence

Run:

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/.

  • 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:

最终结论:passed
  • 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:

最终结论:passed
  • Step 6: Re-run final verification after Review edits

Run:

pnpm format:check
pnpm verify:release
git status --short

Expected: formatting and release verification pass. Only intended Review/evidence files remain uncommitted.

  • Step 7: Detect final scope, commit, and push evidence

Run GitNexus detect_changes(scope: "all"), verify no unexpected application flow changed, then:

git add docs/features/GOV-009-application-scaffold-baseline
git commit -m "docs(GOV-009): 封存仅源码部署复核证据"
git push origin "$(git branch --show-current)"
  • Step 8: Confirm remote consistency without releasing

Run:

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.