create-dwy 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +1 -1
- package/package.json +2 -2
- package/src/create.js +61 -37
- package/src/sync.js +212 -61
- package/templates/claude-global/CLAUDE.md +92 -0
- package/templates/claude-global/rules/dwy-android-core.md +610 -0
- package/templates/claude-global/rules/dwy-api-design.md +110 -0
- package/templates/claude-global/rules/dwy-db-migration.md +46 -0
- package/templates/claude-global/rules/dwy-flutter-core.md +564 -0
- package/templates/claude-global/rules/dwy-flutter-network.md +222 -0
- package/templates/claude-global/rules/dwy-flutter-router.md +97 -0
- package/templates/claude-global/rules/dwy-flutter-testing.md +94 -0
- package/templates/claude-global/rules/dwy-git-commit-convention.md +82 -0
- package/templates/claude-global/rules/dwy-git-security.md +53 -0
- package/templates/claude-global/rules/dwy-payload-limits.md +178 -0
- package/templates/claude-global/rules/{prefer-open-source.md → dwy-prefer-open-source.md} +4 -0
- package/templates/claude-global/rules/dwy-project-structure.md +181 -0
- package/templates/claude-global/rules/{python-code-style.md → dwy-python-core.md} +292 -358
- package/templates/claude-global/rules/dwy-python-fastapi.md +272 -0
- package/templates/claude-global/rules/dwy-python-orm.md +61 -0
- package/templates/claude-global/rules/{backend-security.md → dwy-python-security.md} +99 -72
- package/templates/claude-global/rules/dwy-python-testing.md +53 -0
- package/templates/claude-global/rules/{server-security.md → dwy-server-security.md} +1 -2
- package/templates/claude-global/rules/dwy-tdd-development.md +70 -0
- package/templates/claude-global/rules/dwy-tech-stack.md +60 -0
- package/templates/claude-global/rules/dwy-vue-api.md +146 -0
- package/templates/claude-global/rules/{vue-code-style.md → dwy-vue-core.md} +338 -152
- package/templates/claude-global/rules/dwy-vue-pinia.md +75 -0
- package/templates/claude-global/rules/dwy-vue-testing.md +102 -0
- package/templates/claude-global/skills/dwy-deploy-audit/SKILL.md +481 -0
- package/templates/claude-global/skills/dwy-deploy-audit/references/checks-data.md +117 -0
- package/templates/claude-global/skills/dwy-deploy-audit/references/checks-network.md +74 -0
- package/templates/claude-global/skills/dwy-deploy-audit/references/checks-runtime.md +184 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_base.sh +39 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_capacity.sh +278 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_db.sh +122 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_docker.sh +249 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_env.sh +64 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_https.sh +60 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_logs.sh +209 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +92 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_resilience.sh +157 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_secrets.sh +327 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_services.sh +77 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_ssh.sh +56 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/run_all.sh +105 -0
- package/templates/claude-global/skills/dwy-docker/SKILL.md +187 -0
- package/templates/claude-global/skills/dwy-docker/references/ask-templates.md +142 -0
- package/templates/claude-global/skills/dwy-docker/references/templates.md +479 -0
- package/templates/claude-global/skills/dwy-docker/references/version-rules.md +76 -0
- package/templates/claude-global/skills/dwy-docker/scripts/query_dockerhub.py +204 -0
- package/templates/claude-global/skills/dwy-docker/scripts/scan_images.sh +185 -0
- package/templates/claude-global/skills/dwy-eapi/SKILL.md +433 -0
- package/templates/claude-global/skills/dwy-eapi/references/tasks-integration-guide.md +387 -0
- package/templates/claude-global/skills/dwy-ekit/SKILL.md +85 -0
- package/templates/claude-global/skills/dwy-ekit/references/data.md +135 -0
- package/templates/claude-global/skills/dwy-ekit/references/form.md +68 -0
- package/templates/claude-global/skills/dwy-ekit/references/request.md +183 -0
- package/templates/claude-global/skills/dwy-ekit/references/ui.md +109 -0
- package/templates/claude-global/skills/{dwy-frontend-eui → dwy-eui}/SKILL.md +129 -15
- package/templates/claude-global/skills/dwy-eui/references/eui-design-guide.md +257 -0
- package/templates/claude-global/skills/dwy-eui/references/eui-integration-guide.md +302 -0
- package/templates/claude-global/skills/dwy-eui/references/eui-landing-design-guide.md +437 -0
- package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +214 -0
- package/templates/claude-global/skills/dwy-mirror-source/SKILL.md +275 -0
- package/templates/claude-global/skills/dwy-mirror-source/references/dockerfile-snippets.md +228 -0
- package/templates/claude-global/skills/dwy-mirror-source/references/mirror-providers.md +150 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/__pycache__/check_mirrors.cpython-313.pyc +0 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/apply_mirrors.py +666 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/check_mirrors.py +868 -0
- package/templates/claude-global/skills/dwy-pentest/SKILL.md +344 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__init__.py +1 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/__init__.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/config.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/executor.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/severity.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/validator.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/config.py +109 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/executor.py +157 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/severity.py +96 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/validator.py +113 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_auth.py +289 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_recon.py +217 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_report.py +343 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_scan.py +205 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_ssl.py +208 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_vuln.py +205 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_web.py +330 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pyproject.toml +14 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/setup_tools.sh +97 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/uv.lock +415 -0
- package/templates/claude-global/skills/{dwy-release → dwy-publish}/SKILL.md +42 -18
- package/templates/claude-global/skills/dwy-vue-core/SKILL.md +890 -0
- package/templates/project/backend/.dwy.hbs +5 -0
- package/templates/project/backend/.python-version +1 -0
- package/templates/project/backend/CLAUDE.md.hbs +40 -0
- package/templates/project/{web/backend → backend}/alembic/script.py.mako +2 -0
- package/templates/project/{web/backend → backend}/alembic.ini.hbs +0 -1
- package/templates/project/backend/deploy.sh.hbs +18 -0
- package/templates/project/{web → backend}/docker-compose.prod.yml.hbs +1 -1
- package/templates/project/{web/backend → backend}/pyproject.toml.hbs +4 -5
- package/templates/project/{web/backend → backend}/src/app/config.py.hbs +1 -1
- package/templates/project/backend/src/app/database.py +10 -0
- package/templates/project/backend/src/app/dependencies.py +5 -0
- package/templates/project/{web/backend → backend}/src/app/main.py.hbs +3 -3
- package/templates/project/backend/src/app/models/base.py +5 -0
- package/templates/project/web/CLAUDE.md.hbs +29 -36
- package/templates/project/web/{frontend → apps/web}/index.html.hbs +1 -0
- package/templates/project/web/{frontend → apps/web}/package.json.hbs +4 -3
- package/templates/project/web/apps/web/src/App.vue +9 -0
- package/templates/project/web/apps/web/src/api/client.ts +5 -0
- package/templates/project/web/apps/web/src/composables/.gitkeep +0 -0
- package/templates/project/web/apps/web/src/layouts/DefaultLayout.vue +8 -0
- package/templates/project/web/{frontend → apps/web}/src/router/index.ts +1 -1
- package/templates/project/web/{frontend → apps/web}/src/stores/auth.ts +4 -2
- package/templates/project/web/apps/web/src/style.css +2 -0
- package/templates/project/web/apps/web/src/types/router.d.ts +8 -0
- package/templates/project/web/apps/web/src/views/HomeView.vue +8 -0
- package/templates/project/web/apps/web/tsconfig.app.json +18 -0
- package/templates/project/web/apps/web/tsconfig.json +6 -0
- package/templates/project/web/{frontend → apps/web}/vite.config.ts.hbs +7 -1
- package/templates/project/web/package.json.hbs +16 -0
- package/templates/project/web/packages/.gitkeep +0 -0
- package/templates/project/web/pnpm-workspace.yaml +3 -0
- package/templates/claude-global/hooks/post-sync-claude-global.sh +0 -72
- package/templates/claude-global/rules/agent-team-execution.md +0 -138
- package/templates/claude-global/rules/agent-team-patterns.md +0 -157
- package/templates/claude-global/rules/git-commit-convention.md +0 -106
- package/templates/claude-global/rules/git-security.md +0 -144
- package/templates/claude-global/skills/dwy-backend-base/SKILL.md +0 -279
- package/templates/claude-global/skills/dwy-frontend-core/SKILL.md +0 -181
- package/templates/project/web/.claude/commands/deploy.md +0 -4
- package/templates/project/web/backend/src/app/models/base.py +0 -5
- package/templates/project/web/deploy.sh.hbs +0 -113
- package/templates/project/web/frontend/src/App.vue +0 -3
- package/templates/project/web/frontend/src/api/client.ts +0 -13
- package/templates/project/web/frontend/src/style.css +0 -1
- package/templates/project/web/frontend/src/views/HomeView.vue +0 -5
- package/templates/project/web/frontend/tsconfig.app.json +0 -14
- package/templates/project/web/frontend/tsconfig.json +0 -4
- /package/templates/{project/web/backend → claude-global/skills/dwy-pentest/scripts}/.python-version +0 -0
- /package/templates/project/{web/backend → backend}/Dockerfile +0 -0
- /package/templates/project/{web/backend → backend}/alembic/env.py.hbs +0 -0
- /package/templates/{claude-global/skills → project/backend/alembic/versions}/.gitkeep +0 -0
- /package/templates/project/{web → backend}/docker-compose.dev.yml.hbs +0 -0
- /package/templates/project/{web/backend → backend}/src/app/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/models/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/routers/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/routers/health.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/schemas/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/services/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/tests/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/tests/conftest.py.hbs +0 -0
- /package/templates/project/{web/backend/alembic/versions → mobile}/.gitkeep +0 -0
- /package/templates/project/web/{frontend → apps/web}/src/components/.gitkeep +0 -0
- /package/templates/project/web/{frontend → apps/web}/src/main.ts +0 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
# Docker 工程结构模板
|
|
2
|
+
|
|
3
|
+
新建 docker 工程或用户问"compose / Dockerfile / dev.sh 怎么写"时读这份文件。
|
|
4
|
+
|
|
5
|
+
## docker-compose.dev.yml(开发环境)
|
|
6
|
+
|
|
7
|
+
**核心原则**:快速启动、热更新、方便调试。基础设施容器化,应用代码在宿主机跑(这样 `uvicorn --reload` / `vite dev` 的文件监听才能生效,不用每次改代码都 rebuild 镜像)。
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
# docker-compose.dev.yml — 只跑基础设施,应用在宿主机跑
|
|
11
|
+
services:
|
|
12
|
+
postgres:
|
|
13
|
+
image: postgres:17.1 # 固定具体版本,按 query_dockerhub.py 推荐
|
|
14
|
+
ports:
|
|
15
|
+
- "${PG_PORT:-15432}:5432" # 映射到宿主机非标准端口,避免与其他项目冲突
|
|
16
|
+
environment:
|
|
17
|
+
POSTGRES_DB: ${DB_NAME:-app}
|
|
18
|
+
POSTGRES_USER: ${DB_USER:-postgres}
|
|
19
|
+
POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres}
|
|
20
|
+
volumes:
|
|
21
|
+
- pgdata:/var/lib/postgresql/data
|
|
22
|
+
|
|
23
|
+
redis:
|
|
24
|
+
image: redis:7.2.6
|
|
25
|
+
ports:
|
|
26
|
+
- "${REDIS_PORT:-16379}:6379"
|
|
27
|
+
|
|
28
|
+
minio:
|
|
29
|
+
image: minio/minio:RELEASE.2024-11-07T00-52-20Z
|
|
30
|
+
ports:
|
|
31
|
+
- "${MINIO_PORT:-19000}:9000"
|
|
32
|
+
- "${MINIO_CONSOLE_PORT:-19001}:9001"
|
|
33
|
+
environment:
|
|
34
|
+
MINIO_ROOT_USER: ${MINIO_USER:-minioadmin}
|
|
35
|
+
MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD:-minioadmin}
|
|
36
|
+
command: server /data --console-address ":9001"
|
|
37
|
+
volumes:
|
|
38
|
+
- miniodata:/data
|
|
39
|
+
|
|
40
|
+
volumes:
|
|
41
|
+
pgdata:
|
|
42
|
+
miniodata:
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**关键要点**:
|
|
46
|
+
- 端口映射到宿主机非标准端口(`15432` / `16379` / `19000`),用环境变量可配,方便多项目并行开发
|
|
47
|
+
- volume 持久化数据,避免 `docker compose down` 丢数据
|
|
48
|
+
- 应用本身在宿主机跑(`uvicorn --reload` / `vite dev`)— 这样改代码立刻生效
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## docker-compose.prod.yml(生产环境)
|
|
53
|
+
|
|
54
|
+
**核心原则**:安全、稳定、最小权限。
|
|
55
|
+
|
|
56
|
+
与 dev 关键差异:
|
|
57
|
+
- 基础设施**不暴露端口**(仅通过 docker 网络通信,对外只开 backend / frontend)
|
|
58
|
+
- 所有服务带 `restart: unless-stopped`
|
|
59
|
+
- 数据库 / 缓存有 `healthcheck`,应用 `depends_on` 用 `condition: service_healthy` 等待
|
|
60
|
+
- 后端容器 `read_only: true` + tmpfs,缩小攻击面
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
services:
|
|
64
|
+
postgres:
|
|
65
|
+
image: postgres:17.1
|
|
66
|
+
# 不暴露端口(仅内网通信)
|
|
67
|
+
environment:
|
|
68
|
+
POSTGRES_DB: ${DB_NAME}
|
|
69
|
+
POSTGRES_USER: ${DB_USER}
|
|
70
|
+
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
|
71
|
+
volumes:
|
|
72
|
+
- pgdata:/var/lib/postgresql/data
|
|
73
|
+
restart: unless-stopped
|
|
74
|
+
healthcheck:
|
|
75
|
+
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
|
|
76
|
+
interval: 10s
|
|
77
|
+
timeout: 5s
|
|
78
|
+
retries: 5
|
|
79
|
+
|
|
80
|
+
redis:
|
|
81
|
+
image: redis:7.2.6
|
|
82
|
+
restart: unless-stopped
|
|
83
|
+
healthcheck:
|
|
84
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
85
|
+
interval: 10s
|
|
86
|
+
|
|
87
|
+
minio:
|
|
88
|
+
image: minio/minio:RELEASE.2024-11-07T00-52-20Z
|
|
89
|
+
# 不暴露端口
|
|
90
|
+
environment:
|
|
91
|
+
MINIO_ROOT_USER: ${MINIO_USER}
|
|
92
|
+
MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD}
|
|
93
|
+
command: server /data
|
|
94
|
+
volumes:
|
|
95
|
+
- miniodata:/data
|
|
96
|
+
restart: unless-stopped
|
|
97
|
+
|
|
98
|
+
backend:
|
|
99
|
+
build:
|
|
100
|
+
context: ./backend
|
|
101
|
+
dockerfile: Dockerfile.prod
|
|
102
|
+
environment:
|
|
103
|
+
DATABASE_URL: postgresql+asyncpg://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
|
|
104
|
+
REDIS_URL: redis://redis:6379/0
|
|
105
|
+
SECRET_KEY: ${SECRET_KEY}
|
|
106
|
+
ports:
|
|
107
|
+
- "${API_PORT:-8000}:8000"
|
|
108
|
+
depends_on:
|
|
109
|
+
postgres:
|
|
110
|
+
condition: service_healthy
|
|
111
|
+
redis:
|
|
112
|
+
condition: service_healthy
|
|
113
|
+
read_only: true # 只读根文件系统
|
|
114
|
+
tmpfs:
|
|
115
|
+
- /tmp # 临时文件写 tmpfs
|
|
116
|
+
restart: unless-stopped
|
|
117
|
+
healthcheck:
|
|
118
|
+
test: ["CMD", "curl", "-f", "http://localhost:8000/api/health"]
|
|
119
|
+
interval: 30s
|
|
120
|
+
|
|
121
|
+
worker:
|
|
122
|
+
build:
|
|
123
|
+
context: ./backend
|
|
124
|
+
dockerfile: Dockerfile.prod
|
|
125
|
+
command: arq app.worker.WorkerSettings
|
|
126
|
+
environment:
|
|
127
|
+
DATABASE_URL: postgresql+asyncpg://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
|
|
128
|
+
REDIS_URL: redis://redis:6379/0
|
|
129
|
+
SECRET_KEY: ${SECRET_KEY}
|
|
130
|
+
depends_on:
|
|
131
|
+
postgres:
|
|
132
|
+
condition: service_healthy
|
|
133
|
+
redis:
|
|
134
|
+
condition: service_healthy
|
|
135
|
+
restart: unless-stopped
|
|
136
|
+
|
|
137
|
+
frontend:
|
|
138
|
+
build:
|
|
139
|
+
context: ./frontend
|
|
140
|
+
dockerfile: Dockerfile
|
|
141
|
+
ports:
|
|
142
|
+
- "${WEB_PORT:-3000}:80"
|
|
143
|
+
depends_on:
|
|
144
|
+
- backend
|
|
145
|
+
restart: unless-stopped
|
|
146
|
+
|
|
147
|
+
volumes:
|
|
148
|
+
pgdata:
|
|
149
|
+
miniodata:
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**关键要点**:
|
|
153
|
+
- 基础设施不暴露端口 → 攻击面缩到只有 `${API_PORT}` 和 `${WEB_PORT}`
|
|
154
|
+
- `restart: unless-stopped` → 单容器崩溃不影响整体,但人工 stop 后不会自启
|
|
155
|
+
- healthcheck → 让 `depends_on: service_healthy` 真正等到数据库可连,而不是容器一启就放行
|
|
156
|
+
- 环境变量从 `.env` 注入,不硬编码 → 凭证不进 git
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Dockerfile
|
|
161
|
+
|
|
162
|
+
### 后端 `Dockerfile.dev`(单阶段,包含 dev 依赖)
|
|
163
|
+
|
|
164
|
+
```dockerfile
|
|
165
|
+
FROM python:3.11-slim
|
|
166
|
+
WORKDIR /app
|
|
167
|
+
COPY pyproject.toml .
|
|
168
|
+
RUN pip install uv && uv pip install -e ".[dev]" --system
|
|
169
|
+
COPY . .
|
|
170
|
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 后端 `Dockerfile.prod`(多阶段 + non-root)
|
|
174
|
+
|
|
175
|
+
```dockerfile
|
|
176
|
+
# 多阶段构建
|
|
177
|
+
FROM python:3.11-slim AS builder
|
|
178
|
+
WORKDIR /app
|
|
179
|
+
COPY pyproject.toml .
|
|
180
|
+
RUN pip install uv && uv pip install -e "." --system
|
|
181
|
+
|
|
182
|
+
FROM python:3.11-slim
|
|
183
|
+
RUN useradd -r -s /bin/false appuser
|
|
184
|
+
WORKDIR /app
|
|
185
|
+
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
|
|
186
|
+
COPY --from=builder /usr/local/bin /usr/local/bin
|
|
187
|
+
COPY src/ ./src/
|
|
188
|
+
COPY alembic/ ./alembic/
|
|
189
|
+
COPY alembic.ini .
|
|
190
|
+
USER appuser
|
|
191
|
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**为什么这样写**:
|
|
195
|
+
- 多阶段:builder 装包(含编译工具),runtime 只复制 site-packages,镜像缩小一半以上
|
|
196
|
+
- `useradd -r -s /bin/false appuser` + `USER appuser`:容器逃逸时拿不到 root,缩小攻击面
|
|
197
|
+
- 不 COPY 整个项目,只 COPY `src/` / `alembic/` / `alembic.ini`:避免误带 `.git/` / `tests/` / `.env` 进生产镜像
|
|
198
|
+
|
|
199
|
+
### 前端 `Dockerfile`(多阶段 + nginx 静态托管)
|
|
200
|
+
|
|
201
|
+
```dockerfile
|
|
202
|
+
FROM node:20.18.0-slim AS builder
|
|
203
|
+
RUN npm i -g pnpm
|
|
204
|
+
WORKDIR /app
|
|
205
|
+
COPY package.json pnpm-lock.yaml .
|
|
206
|
+
RUN pnpm install --frozen-lockfile
|
|
207
|
+
COPY . .
|
|
208
|
+
RUN pnpm build
|
|
209
|
+
|
|
210
|
+
FROM nginx:1.26.2-alpine
|
|
211
|
+
COPY --from=builder /app/dist /usr/share/nginx/html
|
|
212
|
+
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 配套 `.dockerignore`(必须有)
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
node_modules/
|
|
219
|
+
.git/
|
|
220
|
+
.env
|
|
221
|
+
.env.*
|
|
222
|
+
__pycache__/
|
|
223
|
+
*.pyc
|
|
224
|
+
dist/
|
|
225
|
+
.pytest_cache/
|
|
226
|
+
.vscode/
|
|
227
|
+
.idea/
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
不写这个,构建上下文会把整个 `node_modules/` / `.git/` 上传给 Docker daemon,速度慢且可能泄露 `.env`。
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 一键启动脚本(`dev.sh` + `prod.sh`)
|
|
235
|
+
|
|
236
|
+
每个项目根放两份脚本:`dev.sh` 用于本地开发,`prod.sh` 用于生产环境管理。两个脚本都采用**子命令模式 + 显式传参**设计。
|
|
237
|
+
|
|
238
|
+
**两个脚本的统一约束**:
|
|
239
|
+
|
|
240
|
+
- **必须显式传子命令**(`start` / `stop` / `restart` / ...),不传参数时**只打印帮助文档不执行任何动作**
|
|
241
|
+
- 帮助文档必须列出所有子命令、参数含义、典型示例
|
|
242
|
+
- 子命令未匹配(拼错或不支持)时同样打印帮助并以非零退出码退出
|
|
243
|
+
|
|
244
|
+
**为什么强制传参 + 不传打印 help**:
|
|
245
|
+
|
|
246
|
+
`bash dev.sh` / `bash prod.sh` 不带参数直接拉起服务是**危险的默认行为**。运维人员在生产 SSH 里手抖少敲一个词就可能误启动服务、覆盖容器、拉新镜像。强制传子命令让每一次操作都是**有意识的**,并且帮助文档让新接手项目的人不用看 README 就知道怎么用。
|
|
247
|
+
|
|
248
|
+
**为什么要有这两个脚本**:
|
|
249
|
+
|
|
250
|
+
- `dev.sh` — 新人 clone 项目后一行 `bash dev.sh start` 跑起来全栈,不用查文档;同时支持 `infra` 模式让你在 IDE 里断点调试应用
|
|
251
|
+
- `prod.sh` — 运维人员(或部署 CI)用统一入口管理生产,避免每次手敲长串 `docker compose --env-file ... -f ... ...`,也避免漏 `--env-file` 等参数导致的事故
|
|
252
|
+
|
|
253
|
+
### `dev.sh`(开发环境管理)
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
#!/bin/bash
|
|
257
|
+
# 开发环境管理脚本
|
|
258
|
+
# 用法: bash dev.sh {start|stop|restart|infra|status|logs} [service]
|
|
259
|
+
|
|
260
|
+
set -e
|
|
261
|
+
|
|
262
|
+
COMPOSE_FILE="docker-compose.dev.yml"
|
|
263
|
+
|
|
264
|
+
# 前置检查
|
|
265
|
+
if [ ! -f "$COMPOSE_FILE" ]; then
|
|
266
|
+
echo "错误:当前目录找不到 $COMPOSE_FILE"
|
|
267
|
+
exit 1
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
# 统一调用 docker compose 的封装
|
|
271
|
+
dc() {
|
|
272
|
+
docker compose -f "$COMPOSE_FILE" "$@"
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
# Ctrl+C 时清理所有后台进程和容器
|
|
276
|
+
cleanup() {
|
|
277
|
+
echo ""
|
|
278
|
+
echo "→ 停止后台应用进程..."
|
|
279
|
+
jobs -p | xargs -r kill 2>/dev/null || true
|
|
280
|
+
echo "→ 停止基础设施容器..."
|
|
281
|
+
dc down
|
|
282
|
+
echo "✓ 已清理"
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
ACTION="${1:-}"
|
|
286
|
+
SERVICE="${2:-}"
|
|
287
|
+
|
|
288
|
+
case "$ACTION" in
|
|
289
|
+
start)
|
|
290
|
+
echo "→ 启动基础设施..."
|
|
291
|
+
dc up -d
|
|
292
|
+
|
|
293
|
+
echo "→ 等待 PostgreSQL 就绪..."
|
|
294
|
+
until dc exec postgres pg_isready -U postgres > /dev/null 2>&1; do
|
|
295
|
+
sleep 1
|
|
296
|
+
done
|
|
297
|
+
|
|
298
|
+
trap cleanup INT TERM EXIT
|
|
299
|
+
|
|
300
|
+
echo "→ 启动后端 (热更新)..."
|
|
301
|
+
(cd backend && uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload) &
|
|
302
|
+
|
|
303
|
+
echo "→ 启动 Worker..."
|
|
304
|
+
(cd backend && arq app.worker.WorkerSettings) &
|
|
305
|
+
|
|
306
|
+
echo "→ 启动前端 (热更新)..."
|
|
307
|
+
(cd frontend && pnpm dev) &
|
|
308
|
+
|
|
309
|
+
echo ""
|
|
310
|
+
echo "✓ 全栈已启动。Ctrl+C 退出会自动清理所有进程和容器"
|
|
311
|
+
wait
|
|
312
|
+
;;
|
|
313
|
+
|
|
314
|
+
stop)
|
|
315
|
+
echo "→ 停止基础设施..."
|
|
316
|
+
dc down
|
|
317
|
+
echo "✓ 已停止(volume 数据保留)"
|
|
318
|
+
;;
|
|
319
|
+
|
|
320
|
+
restart)
|
|
321
|
+
echo "→ 重启基础设施容器..."
|
|
322
|
+
dc restart
|
|
323
|
+
echo "✓ 基础设施已重启。应用层(uvicorn / vite / arq)请重新跑 'bash dev.sh start'"
|
|
324
|
+
;;
|
|
325
|
+
|
|
326
|
+
infra)
|
|
327
|
+
echo "→ 仅启动基础设施容器(不启动应用)..."
|
|
328
|
+
dc up -d
|
|
329
|
+
echo "✓ 基础设施已启动。应用请你自己在 IDE 里跑(方便断点调试)"
|
|
330
|
+
echo ""
|
|
331
|
+
dc ps
|
|
332
|
+
;;
|
|
333
|
+
|
|
334
|
+
status)
|
|
335
|
+
dc ps
|
|
336
|
+
;;
|
|
337
|
+
|
|
338
|
+
logs)
|
|
339
|
+
if [ -n "$SERVICE" ]; then
|
|
340
|
+
dc logs -f --tail=200 "$SERVICE"
|
|
341
|
+
else
|
|
342
|
+
dc logs -f --tail=200
|
|
343
|
+
fi
|
|
344
|
+
;;
|
|
345
|
+
|
|
346
|
+
*)
|
|
347
|
+
cat <<EOF
|
|
348
|
+
用法: bash dev.sh {start|stop|restart|infra|status|logs} [service]
|
|
349
|
+
|
|
350
|
+
start 一键拉起全栈:基础设施容器 + 后端 + Worker + 前端
|
|
351
|
+
前台运行,Ctrl+C 退出会自动清理所有进程和容器
|
|
352
|
+
stop 停止并清理基础设施容器(volume 保留,下次 start 数据还在)
|
|
353
|
+
restart 重启基础设施容器(应用层不动,需自行重新启动)
|
|
354
|
+
infra 只启动基础设施容器,应用让你在 IDE 里跑(推荐:断点调试场景)
|
|
355
|
+
status 查看基础设施容器状态
|
|
356
|
+
logs [service] 实时查看基础设施日志(最近 200 行起),可指定 service
|
|
357
|
+
|
|
358
|
+
示例:
|
|
359
|
+
bash dev.sh start # 一键全栈,适合纯命令行开发
|
|
360
|
+
bash dev.sh infra # 容器跑数据库,IDE 里跑应用(推荐用于断点调试)
|
|
361
|
+
bash dev.sh logs postgres # 看 postgres 日志
|
|
362
|
+
EOF
|
|
363
|
+
exit 1
|
|
364
|
+
;;
|
|
365
|
+
esac
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### `prod.sh`(生产环境管理)
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
#!/bin/bash
|
|
372
|
+
# 生产环境部署管理脚本
|
|
373
|
+
# 用法: bash prod.sh {start|stop|restart|status|logs|update} [service]
|
|
374
|
+
|
|
375
|
+
set -e
|
|
376
|
+
|
|
377
|
+
COMPOSE_FILE="docker-compose.prod.yml"
|
|
378
|
+
ENV_FILE=".env.prod"
|
|
379
|
+
|
|
380
|
+
# 前置检查
|
|
381
|
+
if [ ! -f "$COMPOSE_FILE" ]; then
|
|
382
|
+
echo "错误:当前目录找不到 $COMPOSE_FILE"
|
|
383
|
+
exit 1
|
|
384
|
+
fi
|
|
385
|
+
|
|
386
|
+
if [ ! -f "$ENV_FILE" ]; then
|
|
387
|
+
echo "错误:缺少 $ENV_FILE,请先从 .env.prod.example 复制并填写生产环境变量"
|
|
388
|
+
exit 1
|
|
389
|
+
fi
|
|
390
|
+
|
|
391
|
+
# 统一调用 docker compose 的封装(避免重复参数)
|
|
392
|
+
dc() {
|
|
393
|
+
docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" "$@"
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
ACTION="${1:-}"
|
|
397
|
+
SERVICE="${2:-}"
|
|
398
|
+
|
|
399
|
+
case "$ACTION" in
|
|
400
|
+
start)
|
|
401
|
+
echo "→ 启动生产环境(后台运行)..."
|
|
402
|
+
dc up -d
|
|
403
|
+
echo "✓ 已启动。用 'bash prod.sh status' 查看状态"
|
|
404
|
+
;;
|
|
405
|
+
|
|
406
|
+
stop)
|
|
407
|
+
echo "→ 停止生产环境..."
|
|
408
|
+
dc down
|
|
409
|
+
echo "✓ 已停止(volume 数据保留)"
|
|
410
|
+
;;
|
|
411
|
+
|
|
412
|
+
restart)
|
|
413
|
+
if [ -n "$SERVICE" ]; then
|
|
414
|
+
echo "→ 重启服务 $SERVICE ..."
|
|
415
|
+
dc restart "$SERVICE"
|
|
416
|
+
else
|
|
417
|
+
echo "→ 重启所有服务..."
|
|
418
|
+
dc restart
|
|
419
|
+
fi
|
|
420
|
+
echo "✓ 已重启"
|
|
421
|
+
;;
|
|
422
|
+
|
|
423
|
+
status)
|
|
424
|
+
dc ps
|
|
425
|
+
;;
|
|
426
|
+
|
|
427
|
+
logs)
|
|
428
|
+
if [ -n "$SERVICE" ]; then
|
|
429
|
+
dc logs -f --tail=200 "$SERVICE"
|
|
430
|
+
else
|
|
431
|
+
dc logs -f --tail=200
|
|
432
|
+
fi
|
|
433
|
+
;;
|
|
434
|
+
|
|
435
|
+
update)
|
|
436
|
+
echo "→ 拉取最新镜像并重建容器..."
|
|
437
|
+
dc pull
|
|
438
|
+
dc up -d --remove-orphans
|
|
439
|
+
echo "✓ 已更新到最新镜像"
|
|
440
|
+
;;
|
|
441
|
+
|
|
442
|
+
*)
|
|
443
|
+
cat <<EOF
|
|
444
|
+
用法: bash prod.sh {start|stop|restart|status|logs|update} [service]
|
|
445
|
+
|
|
446
|
+
start 启动所有服务(后台运行)
|
|
447
|
+
stop 停止所有服务并清理容器(volume 保留)
|
|
448
|
+
restart [service] 重启所有服务,或指定单个服务(不重建容器)
|
|
449
|
+
status 查看服务状态
|
|
450
|
+
logs [service] 实时查看日志(最近 200 行起),可指定 service
|
|
451
|
+
update 拉取最新镜像并重建容器(应用 compose 配置变更)
|
|
452
|
+
|
|
453
|
+
示例:
|
|
454
|
+
bash prod.sh start
|
|
455
|
+
bash prod.sh restart backend
|
|
456
|
+
bash prod.sh logs nginx
|
|
457
|
+
bash prod.sh update
|
|
458
|
+
EOF
|
|
459
|
+
exit 1
|
|
460
|
+
;;
|
|
461
|
+
esac
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
**两个脚本共同的设计**:
|
|
465
|
+
|
|
466
|
+
- **子命令模式**:所有操作必须显式传子命令(`start` / `stop` / `restart` / ...),不传或传错都只打印 help,不执行任何动作。生产环境最忌"默认行为"
|
|
467
|
+
- **`dc()` 封装**:所有 docker compose 命令都自动带 `--env-file ... -f ...` 参数,杜绝漏参事故
|
|
468
|
+
- **`restart` 走 `docker compose restart` 而不是 `down + up`**:重启容器但不删除(保留运行时状态),更快也更安全;只有 `update`(prod)/ 重新跑 `start`(dev)才会重建
|
|
469
|
+
- **`stop` 走 `down` 而非 `stop`**:彻底清理网络和容器,下次 `start` 是全新的;volume 默认保留,不丢数据
|
|
470
|
+
|
|
471
|
+
**`dev.sh` 特有设计**:
|
|
472
|
+
|
|
473
|
+
- **`trap cleanup INT TERM EXIT`**:`start` 子命令前台运行(`wait` 阻塞),用户按 Ctrl+C 时会自动 kill 所有后台子进程(uvicorn / arq / vite)并 `docker compose down` 清理容器。避免出现"应用进程已退出但容器还在跑"的孤儿状态
|
|
474
|
+
- **`infra` 子命令**:只启基础设施,应用让用户在 IDE 里跑。这是断点调试场景的最佳工作流,比"全栈启动后再 attach 调试器"轻量得多
|
|
475
|
+
|
|
476
|
+
**`prod.sh` 特有设计**:
|
|
477
|
+
|
|
478
|
+
- **前置检查 `.env.prod`**:生产环境变量缺失时第一时间退出,不会因为读到默认值跑起一个错误配置的服务
|
|
479
|
+
- **`update` 单独命令**:日常运维(重启 / 看日志)不会误触发镜像拉取,只有显式 `update` 才会拉新镜像并重建容器
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# 镜像版本选择规则(N-1 minor)
|
|
2
|
+
|
|
3
|
+
选具体版本号时读这份。先用 `scripts/query_dockerhub.py` 查实时数据,再按下面规则推算 N-1。
|
|
4
|
+
|
|
5
|
+
## 为什么是 N-1 minor 而不是最新
|
|
6
|
+
|
|
7
|
+
新版镜像刚发布时,回归 bug、性能问题、新依赖不兼容这些坑还没被社区踩过。等下一个 minor 出来,前一个 minor 系列已经经历了几轮 patch 修复,**稳定性 vs 新特性的折中点**就是 N-1。这是行业惯例,不是激进保守。
|
|
8
|
+
|
|
9
|
+
举例:nginx `1.27.0` 刚发布时有个 HTTP/3 模块的 segfault,3 周后 `1.27.1` 才修复。这期间用 `1.26.x` 系列的人完全没受影响。
|
|
10
|
+
|
|
11
|
+
## 强制规则
|
|
12
|
+
|
|
13
|
+
1. **禁止 `latest`** — FROM / image / containers.image / services.image 任何位置都不允许,省略 tag 等同于 `latest`
|
|
14
|
+
2. **必须固定 tag** — 必须是具体版本号(如 `17.1`、`1.26.2`、`3.20`),禁止浮动 tag
|
|
15
|
+
3. **优先选 N-1 minor** — 当前最新稳定 minor 的前一个 minor 系列最新 patch
|
|
16
|
+
4. **生产关键服务用 digest** — 数据库、消息队列、网关在 tag 后再钉 `@sha256:...`,让 image 100% 不可篡改
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 选法分类(按版本号格式)
|
|
21
|
+
|
|
22
|
+
### 1. semver 三段镜像(绝大多数)
|
|
23
|
+
|
|
24
|
+
格式 `MAJOR.MINOR.PATCH`,例如 `nginx:1.27.3`、`redis:7.4.1`。
|
|
25
|
+
|
|
26
|
+
步骤:
|
|
27
|
+
1. DockerHub 找当前最新稳定 minor(**忽略 rc / beta / alpha / preview / nightly**)
|
|
28
|
+
2. minor 减 1,得到目标 minor 系列
|
|
29
|
+
3. 取该 minor 系列的最新 patch
|
|
30
|
+
|
|
31
|
+
| 镜像 | 当前最新稳定 | N-1 minor 选择 | 说明 |
|
|
32
|
+
|------|-------------|---------------|------|
|
|
33
|
+
| `nginx` | `1.27.3` | `nginx:1.26.2` | 1.26 系列最新 patch |
|
|
34
|
+
| `redis` | `7.4.1` | `redis:7.2.6` | 7.4 上一个稳定 minor 是 7.2,跳过仅维护版 |
|
|
35
|
+
| `node` | `22.11.0`(LTS)| `node:20.18.0` | LTS 镜像例外,见下文 |
|
|
36
|
+
|
|
37
|
+
### 2. 两段版本号镜像
|
|
38
|
+
|
|
39
|
+
格式 `MAJOR.MINOR`,例如 `postgres:17.2`、`mysql:8.4`、`alpine:3.21`。这类镜像把 minor 当 patch 用。
|
|
40
|
+
|
|
41
|
+
选法:minor 减 1,取该系列最新 tag。
|
|
42
|
+
|
|
43
|
+
| 镜像 | 当前最新稳定 | N-1 minor 选择 |
|
|
44
|
+
|------|-------------|---------------|
|
|
45
|
+
| `postgres` | `17.2` | `postgres:17.1` |
|
|
46
|
+
| `mysql` | `8.4` | `mysql:8.3` |
|
|
47
|
+
| `alpine` | `3.21` | `alpine:3.20` |
|
|
48
|
+
|
|
49
|
+
### 3. 年份版本号镜像(Ubuntu / Debian)
|
|
50
|
+
|
|
51
|
+
格式 `YY.MM` 或 codename,例如 `ubuntu:24.04`、`debian:bookworm`。
|
|
52
|
+
|
|
53
|
+
选法:选**上一个 LTS / stable release**,**不要**用 codename(codename 是浮动 tag,会随 release 漂移),只用版本号。
|
|
54
|
+
|
|
55
|
+
| 镜像 | 当前最新 LTS | N-1 选择 |
|
|
56
|
+
|------|-------------|---------|
|
|
57
|
+
| `ubuntu` | `24.04` | `ubuntu:22.04` |
|
|
58
|
+
| `debian` | `12`(bookworm)| `debian:11`(bullseye)|
|
|
59
|
+
|
|
60
|
+
### 4. LTS 镜像例外(Node.js / OpenJDK)
|
|
61
|
+
|
|
62
|
+
LTS 镜像(如 Node.js 偶数 major 是 LTS)选**上一个 LTS major** 的最新 patch,**不要**机械 minor - 1。原因是非 LTS 版本生命周期短(6 个月),N-1 落到非 LTS 上反而很快就 EOL。
|
|
63
|
+
|
|
64
|
+
例:当前 `node:22.11.0`(22 是 LTS),N-1 选 `node:20.18.0`(20 是上一个 LTS)而不是 `node:21.x`。
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 例外情况(极少)
|
|
69
|
+
|
|
70
|
+
以下情况可以**短暂**使用浮动 tag,必须在 commit body 或代码注释中说明原因:
|
|
71
|
+
|
|
72
|
+
1. **临时调试** — 本地排查时拉一下 `latest` 看新版行为,**禁止**进入 commit
|
|
73
|
+
2. **官方明确推荐使用 codename** — 极少数镜像(如某些 ML 框架)只发 codename tag
|
|
74
|
+
3. **基础镜像本身就是浮动的** — 如 `gcr.io/distroless/static:nonroot`(distroless 没有版本号),这类镜像必须钉 digest
|
|
75
|
+
|
|
76
|
+
**禁止以"为了拿安全补丁"为由用 `latest`**。安全补丁应该通过定期升级 N-1 minor 实现,不是放任 image 漂移。
|