@wneng/create-keel 0.3.4 → 0.4.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/dist/index.js +211 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/templates/ci-gitee/files/PULL_REQUEST_TEMPLATE.md +13 -1
- package/src/templates/ci-gitee/files/pipeline.yml +71 -0
- package/src/templates/ci-github/files/PULL_REQUEST_TEMPLATE.md +13 -1
- package/src/templates/ci-github/files/ci.yml +169 -0
- package/src/templates/contracts-events/fragment.yaml +1 -1
- package/src/templates/contracts-rest-events/fragment.yaml +1 -1
- package/src/templates/db-go-elasticsearch/files/Makefile +18 -0
- package/src/templates/db-go-elasticsearch/files/apply_templates.go +80 -0
- package/src/templates/db-go-elasticsearch/files/db-README.md +57 -0
- package/src/templates/db-go-elasticsearch/files/go.mod +7 -0
- package/src/templates/db-go-elasticsearch/files/index-template-init.json +25 -0
- package/src/templates/db-go-elasticsearch/fragment.yaml +22 -0
- package/src/templates/db-go-migrate-mysql/files/000001_init.down.sql +3 -0
- package/src/templates/db-go-migrate-mysql/files/000001_init.up.sql +35 -0
- package/src/templates/db-go-migrate-mysql/files/Makefile +33 -0
- package/src/templates/db-go-migrate-mysql/files/db-README.md +77 -0
- package/src/templates/db-go-migrate-mysql/files/go.mod +8 -0
- package/src/templates/db-go-migrate-mysql/fragment.yaml +22 -0
- package/src/templates/db-go-migrate-postgres/files/000001_init.down.sql +3 -0
- package/src/templates/db-go-migrate-postgres/files/000001_init.up.sql +32 -0
- package/src/templates/db-go-migrate-postgres/files/Makefile +31 -0
- package/src/templates/db-go-migrate-postgres/files/db-README.md +71 -0
- package/src/templates/db-go-migrate-postgres/files/go.mod +8 -0
- package/src/templates/db-go-migrate-postgres/fragment.yaml +22 -0
- package/src/templates/db-java-elasticsearch/files/EsTemplateApplier.java +86 -0
- package/src/templates/db-java-elasticsearch/files/db-README.md +63 -0
- package/src/templates/db-java-elasticsearch/files/index-template-init.json +25 -0
- package/src/templates/db-java-elasticsearch/files/pom.xml +134 -0
- package/src/templates/db-java-elasticsearch/fragment.yaml +19 -0
- package/src/templates/db-java-flyway-mysql/files/V1__init.sql +44 -0
- package/src/templates/db-java-flyway-mysql/files/application.yaml +39 -0
- package/src/templates/db-java-flyway-mysql/files/db-README.md +102 -0
- package/src/templates/db-java-flyway-mysql/files/pom.xml +172 -0
- package/src/templates/db-java-flyway-mysql/fragment.yaml +19 -0
- package/src/templates/db-java-flyway-postgres/files/V1__init.sql +40 -0
- package/src/templates/db-java-flyway-postgres/files/application.yaml +37 -0
- package/src/templates/db-java-flyway-postgres/files/db-README.md +75 -0
- package/src/templates/db-java-flyway-postgres/files/pom.xml +166 -0
- package/src/templates/db-java-flyway-postgres/fragment.yaml +19 -0
- package/src/templates/db-node-elasticsearch/files/apply-templates.cjs +60 -0
- package/src/templates/db-node-elasticsearch/files/db-README.md +76 -0
- package/src/templates/db-node-elasticsearch/files/index-template-init.json +26 -0
- package/src/templates/db-node-elasticsearch/files/package.json +26 -0
- package/src/templates/db-node-elasticsearch/fragment.yaml +19 -0
- package/src/templates/db-node-knex-mysql/files/db-README.md +90 -0
- package/src/templates/db-node-knex-mysql/files/knexfile.cjs +72 -0
- package/src/templates/db-node-knex-mysql/files/migrations-init.cjs +42 -0
- package/src/templates/db-node-knex-mysql/files/package.json +31 -0
- package/src/templates/db-node-knex-mysql/files/seeds-dev-fixtures.cjs +38 -0
- package/src/templates/db-node-knex-mysql/files/seeds-prod-dictionaries.cjs +25 -0
- package/src/templates/db-node-knex-mysql/fragment.yaml +25 -0
- package/src/templates/db-node-knex-postgres/files/db-README.md +81 -0
- package/src/templates/db-node-knex-postgres/files/knexfile.cjs +67 -0
- package/src/templates/db-node-knex-postgres/files/migrations-init.cjs +42 -0
- package/src/templates/db-node-knex-postgres/files/package.json +31 -0
- package/src/templates/db-node-knex-postgres/files/seeds-dev-fixtures.cjs +36 -0
- package/src/templates/db-node-knex-postgres/files/seeds-prod-dictionaries.cjs +26 -0
- package/src/templates/db-node-knex-postgres/fragment.yaml +25 -0
- package/src/templates/db-python-alembic-mysql/files/0001_init.py +70 -0
- package/src/templates/db-python-alembic-mysql/files/alembic.ini +47 -0
- package/src/templates/db-python-alembic-mysql/files/db-README.md +87 -0
- package/src/templates/db-python-alembic-mysql/files/env.py +71 -0
- package/src/templates/db-python-alembic-mysql/files/pyproject.toml +52 -0
- package/src/templates/db-python-alembic-mysql/files/script.py.mako +26 -0
- package/src/templates/db-python-alembic-mysql/fragment.yaml +25 -0
- package/src/templates/db-python-alembic-postgres/files/0001_init.py +62 -0
- package/src/templates/db-python-alembic-postgres/files/alembic.ini +45 -0
- package/src/templates/db-python-alembic-postgres/files/db-README.md +70 -0
- package/src/templates/db-python-alembic-postgres/files/env.py +62 -0
- package/src/templates/db-python-alembic-postgres/files/pyproject.toml +52 -0
- package/src/templates/db-python-alembic-postgres/files/script.py.mako +25 -0
- package/src/templates/db-python-alembic-postgres/fragment.yaml +25 -0
- package/src/templates/db-python-elasticsearch/files/apply_templates.py +55 -0
- package/src/templates/db-python-elasticsearch/files/db-README.md +57 -0
- package/src/templates/db-python-elasticsearch/files/index-template-init.json +25 -0
- package/src/templates/db-python-elasticsearch/files/pyproject.toml +50 -0
- package/src/templates/db-python-elasticsearch/fragment.yaml +19 -0
- package/src/templates/docs-skeleton/files/governance-change-tiers.md +135 -0
- package/src/templates/docs-skeleton/files/governance-database.md +150 -0
- package/src/templates/docs-skeleton/fragment.yaml +6 -0
- package/src/templates/root-files/files/AGENTS.md +42 -7
- package/src/templates/server-node/files/_eslintrc.cjs +5 -1
- package/src/templates/server-python/files/README.md +75 -2
- package/src/templates/server-python/files/app-init.py +11 -1
- package/src/templates/server-python/files/config.py +40 -0
- package/src/templates/server-python/files/main.py +62 -0
- package/src/templates/server-python/files/pyproject.toml +14 -1
- package/src/templates/server-python/files/test_healthz.py +36 -0
- package/src/templates/server-python/fragment.yaml +10 -1
- package/src/templates/web-react/files/_eslintrc.cjs +5 -1
- package/src/templates/web-vue/files/_eslintrc.cjs +5 -1
package/package.json
CHANGED
|
@@ -7,12 +7,24 @@
|
|
|
7
7
|
|
|
8
8
|
<!-- 一句话讲清楚这个 PR 改了什么、为什么 -->
|
|
9
9
|
|
|
10
|
+
## 变更分级(Tier)
|
|
11
|
+
|
|
12
|
+
> 完整定义见 [`docs/governance/change-tiers.md`](../docs/governance/change-tiers.md)。**勾选一项**。
|
|
13
|
+
|
|
14
|
+
- [ ] **Tier 0** trivial — typo / 格式化 / patch 升级 / 补测试。无需契约 / ADR / PRD
|
|
15
|
+
- [ ] **Tier 1** bugfix — 修缺陷,无新行为。本 PR 描述含:重现 + 根因 + 修复 + 回归测试
|
|
16
|
+
- [ ] **Tier 2** small change — 局部小改(加按钮 / 排序 / 配置)。本 PR 描述含动机 + 方案 + 影响面
|
|
17
|
+
- [ ] **Tier 3** contract change — 改了 `contracts/`(加 API / 字段 / 错误码 / 事件)。已附契约锚点 + CHANGELOG 已同步
|
|
18
|
+
- [ ] **Tier 4** architecture / breaking — 跨服务 / 鉴权变化 / 删字段。已附 ADR + 详细设计 + 迁移 / 回滚预案
|
|
19
|
+
- [ ] **Tier 5** spike — 探索性改动。分支为 `spike/*`,**不会**合入 `main`(合入需重新走 Tier 1-4)
|
|
20
|
+
|
|
10
21
|
## 关联引用
|
|
11
22
|
|
|
12
|
-
<!--
|
|
23
|
+
<!-- Tier 0/1/2 可省略;Tier 3+ 必须填 -->
|
|
13
24
|
|
|
14
25
|
- 契约锚点:`contracts/openapi/api.yaml#/paths/...` 或 `contracts/events/event-catalog.yaml#/events/...`
|
|
15
26
|
- 设计文档:`docs/04-后端详细设计/<slug>.md` 或 `docs/05-前端客户端详细设计/<slug>-<platform>.md`
|
|
27
|
+
- ADR(Tier 4 必填):`docs/02-系统方案与架构/adr-<NNNN>-<slug>.md`
|
|
16
28
|
- spec:`.kiro/specs/<feature>/`(如适用)
|
|
17
29
|
|
|
18
30
|
## 变更类型
|
|
@@ -28,6 +28,15 @@ jobs:
|
|
|
28
28
|
else
|
|
29
29
|
echo "tools/governance-lint/ not present; skipping"
|
|
30
30
|
fi
|
|
31
|
+
- |
|
|
32
|
+
# CODEOWNERS @TBD gate (AGENTS.md §3.1, docs/governance/git-workflow.md).
|
|
33
|
+
# The CODEOWNERS file ships with @TBD placeholders; CI must reject any
|
|
34
|
+
# merge that still contains them so reviewers are forced to assign real owners.
|
|
35
|
+
if [ -f CODEOWNERS ] && grep -q '@TBD' CODEOWNERS; then
|
|
36
|
+
echo "ERROR: CODEOWNERS still contains @TBD placeholder(s); replace with real Git accounts before merging."
|
|
37
|
+
grep -n '@TBD' CODEOWNERS || true
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
31
40
|
|
|
32
41
|
contract-lint:
|
|
33
42
|
stage: lint
|
|
@@ -127,6 +136,68 @@ jobs:
|
|
|
127
136
|
script:
|
|
128
137
|
- gitleaks detect --redact --no-git -v
|
|
129
138
|
|
|
139
|
+
<% if (it.options.database === 'mysql' || it.options.database === 'postgres') { %> db-migrate-smoke:
|
|
140
|
+
stage: test
|
|
141
|
+
<% if (it.options.database === 'postgres') { %> image: postgres:16-alpine
|
|
142
|
+
services:
|
|
143
|
+
- name: postgres:16-alpine
|
|
144
|
+
alias: postgres
|
|
145
|
+
env:
|
|
146
|
+
POSTGRES_PASSWORD: postgres
|
|
147
|
+
POSTGRES_DB: ci_smoke
|
|
148
|
+
<% } else { %> image: mysql:8
|
|
149
|
+
services:
|
|
150
|
+
- name: mysql:8
|
|
151
|
+
alias: mysql
|
|
152
|
+
env:
|
|
153
|
+
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
|
|
154
|
+
MYSQL_DATABASE: ci_smoke
|
|
155
|
+
<% } %> script:
|
|
156
|
+
- cd server
|
|
157
|
+
<% if (it.options.backend === 'node') { %> - apk add --no-cache nodejs npm || true
|
|
158
|
+
- npm ci
|
|
159
|
+
<% if (it.options.database === 'postgres') { %> - DB_HOST=postgres DB_PORT=5432 DB_USER=postgres DB_PASSWORD=postgres DB_NAME=ci_smoke npm run db:migrate
|
|
160
|
+
- DB_HOST=postgres DB_PORT=5432 DB_USER=postgres DB_PASSWORD=postgres DB_NAME=ci_smoke npm run db:rollback
|
|
161
|
+
<% } else { %> - DB_HOST=mysql DB_PORT=3306 DB_USER=root DB_PASSWORD= DB_NAME=ci_smoke npm run db:migrate
|
|
162
|
+
- DB_HOST=mysql DB_PORT=3306 DB_USER=root DB_PASSWORD= DB_NAME=ci_smoke npm run db:rollback
|
|
163
|
+
<% } %><% } %><% if (it.options.backend === 'java') { %> - apk add --no-cache maven openjdk21 || true
|
|
164
|
+
<% if (it.options.database === 'postgres') { %> - SPRING_DATASOURCE_URL='jdbc:postgresql://postgres:5432/ci_smoke' SPRING_DATASOURCE_USERNAME=postgres SPRING_DATASOURCE_PASSWORD=postgres mvn -B flyway:migrate
|
|
165
|
+
- SPRING_DATASOURCE_URL='jdbc:postgresql://postgres:5432/ci_smoke' SPRING_DATASOURCE_USERNAME=postgres SPRING_DATASOURCE_PASSWORD=postgres mvn -B flyway:validate
|
|
166
|
+
<% } else { %> - SPRING_DATASOURCE_URL='jdbc:mysql://mysql:3306/ci_smoke?useSSL=false&serverTimezone=UTC' SPRING_DATASOURCE_USERNAME=root SPRING_DATASOURCE_PASSWORD= mvn -B flyway:migrate
|
|
167
|
+
- SPRING_DATASOURCE_URL='jdbc:mysql://mysql:3306/ci_smoke?useSSL=false&serverTimezone=UTC' SPRING_DATASOURCE_USERNAME=root SPRING_DATASOURCE_PASSWORD= mvn -B flyway:validate
|
|
168
|
+
<% } %><% } %><% if (it.options.backend === 'python') { %> - apk add --no-cache python3 py3-pip || true
|
|
169
|
+
- pip install -e .
|
|
170
|
+
<% if (it.options.database === 'postgres') { %> - DATABASE_URL='postgresql+psycopg://postgres:postgres@postgres:5432/ci_smoke' alembic upgrade head
|
|
171
|
+
- DATABASE_URL='postgresql+psycopg://postgres:postgres@postgres:5432/ci_smoke' alembic downgrade base
|
|
172
|
+
<% } else { %> - DATABASE_URL='mysql+pymysql://root:@mysql:3306/ci_smoke' alembic upgrade head
|
|
173
|
+
- DATABASE_URL='mysql+pymysql://root:@mysql:3306/ci_smoke' alembic downgrade base
|
|
174
|
+
<% } %><% } %><% if (it.options.backend === 'go') { %> - apk add --no-cache go || true
|
|
175
|
+
- go install -tags '<%= it.options.database %>' github.com/golang-migrate/migrate/v4/cmd/migrate@v4.18.1
|
|
176
|
+
<% if (it.options.database === 'postgres') { %> - DATABASE_URL='postgres://postgres:postgres@postgres:5432/ci_smoke?sslmode=disable' migrate -path db/migrations -database "$DATABASE_URL" up
|
|
177
|
+
- DATABASE_URL='postgres://postgres:postgres@postgres:5432/ci_smoke?sslmode=disable' migrate -path db/migrations -database "$DATABASE_URL" down -all
|
|
178
|
+
<% } else { %> - DATABASE_URL='mysql://root:@tcp(mysql:3306)/ci_smoke?multiStatements=true' migrate -path db/migrations -database "$DATABASE_URL" up
|
|
179
|
+
- DATABASE_URL='mysql://root:@tcp(mysql:3306)/ci_smoke?multiStatements=true' migrate -path db/migrations -database "$DATABASE_URL" down -all
|
|
180
|
+
<% } %><% } %><% } %>
|
|
181
|
+
<% if (it.options.database === 'elasticsearch') { %> db-es-template-apply:
|
|
182
|
+
stage: test
|
|
183
|
+
image: <%= it.options.backend === 'node' ? 'node:20-alpine' : it.options.backend === 'java' ? 'maven:3.9-eclipse-temurin-21' : it.options.backend === 'python' ? 'python:3.12-slim' : 'golang:1.22-alpine' %>
|
|
184
|
+
services:
|
|
185
|
+
- name: elasticsearch:8.15.1
|
|
186
|
+
alias: elasticsearch
|
|
187
|
+
env:
|
|
188
|
+
discovery.type: single-node
|
|
189
|
+
xpack.security.enabled: 'false'
|
|
190
|
+
ES_JAVA_OPTS: '-Xms512m -Xmx512m'
|
|
191
|
+
script:
|
|
192
|
+
- cd server
|
|
193
|
+
<% if (it.options.backend === 'node') { %> - npm ci
|
|
194
|
+
- ELASTICSEARCH_URL=http://elasticsearch:9200 npm run es:apply
|
|
195
|
+
<% } %><% if (it.options.backend === 'java') { %> - SPRING_ELASTICSEARCH_URIS=http://elasticsearch:9200 mvn -B test -Dtest=*ApplicationTests
|
|
196
|
+
<% } %><% if (it.options.backend === 'python') { %> - pip install -e .
|
|
197
|
+
- ELASTICSEARCH_URL=http://elasticsearch:9200 python db/apply_templates.py
|
|
198
|
+
<% } %><% if (it.options.backend === 'go') { %> - go mod download
|
|
199
|
+
- ELASTICSEARCH_URL=http://elasticsearch:9200 make es-apply
|
|
200
|
+
<% } %><% } %>
|
|
130
201
|
<% if (it.options.backend === 'node' || it.options.frontend === 'react' || it.options.frontend === 'vue' || it.options.mobile === 'react-native' || it.options.miniapp === 'wechat') { %> npm-audit:
|
|
131
202
|
stage: scan
|
|
132
203
|
image: node:20-alpine
|
|
@@ -7,12 +7,24 @@
|
|
|
7
7
|
|
|
8
8
|
<!-- 一句话讲清楚这个 PR 改了什么、为什么 -->
|
|
9
9
|
|
|
10
|
+
## 变更分级(Tier)
|
|
11
|
+
|
|
12
|
+
> 完整定义见 [`docs/governance/change-tiers.md`](../docs/governance/change-tiers.md)。**勾选一项**。
|
|
13
|
+
|
|
14
|
+
- [ ] **Tier 0** trivial — typo / 格式化 / patch 升级 / 补测试。无需契约 / ADR / PRD
|
|
15
|
+
- [ ] **Tier 1** bugfix — 修缺陷,无新行为。本 PR 描述含:重现 + 根因 + 修复 + 回归测试
|
|
16
|
+
- [ ] **Tier 2** small change — 局部小改(加按钮 / 排序 / 配置)。本 PR 描述含动机 + 方案 + 影响面
|
|
17
|
+
- [ ] **Tier 3** contract change — 改了 `contracts/`(加 API / 字段 / 错误码 / 事件)。已附契约锚点 + CHANGELOG 已同步
|
|
18
|
+
- [ ] **Tier 4** architecture / breaking — 跨服务 / 鉴权变化 / 删字段。已附 ADR + 详细设计 + 迁移 / 回滚预案
|
|
19
|
+
- [ ] **Tier 5** spike — 探索性改动。分支为 `spike/*`,**不会**合入 `main`(合入需重新走 Tier 1-4)
|
|
20
|
+
|
|
10
21
|
## 关联引用
|
|
11
22
|
|
|
12
|
-
<!--
|
|
23
|
+
<!-- Tier 0/1/2 可省略;Tier 3+ 必须填 -->
|
|
13
24
|
|
|
14
25
|
- 契约锚点:`contracts/openapi/api.yaml#/paths/...` 或 `contracts/events/event-catalog.yaml#/events/...`
|
|
15
26
|
- 设计文档:`docs/04-后端详细设计/<slug>.md` 或 `docs/05-前端客户端详细设计/<slug>-<platform>.md`
|
|
27
|
+
- ADR(Tier 4 必填):`docs/02-系统方案与架构/adr-<NNNN>-<slug>.md`
|
|
16
28
|
- spec:`.kiro/specs/<feature>/`(如适用)
|
|
17
29
|
|
|
18
30
|
## 变更类型
|
|
@@ -31,6 +31,15 @@ jobs:
|
|
|
31
31
|
else
|
|
32
32
|
echo "tools/governance-lint/ not present; skipping"
|
|
33
33
|
fi
|
|
34
|
+
- name: codeowners-tbd-gate
|
|
35
|
+
run: |
|
|
36
|
+
# AGENTS.md §3.1 + docs/governance/git-workflow.md require real
|
|
37
|
+
# owners; @TBD placeholders must be replaced before merge.
|
|
38
|
+
if [ -f CODEOWNERS ] && grep -q '@TBD' CODEOWNERS; then
|
|
39
|
+
echo "ERROR: CODEOWNERS still contains @TBD placeholder(s); replace with real Git accounts before merging."
|
|
40
|
+
grep -n '@TBD' CODEOWNERS || true
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
34
43
|
|
|
35
44
|
secret-scan:
|
|
36
45
|
runs-on: ubuntu-latest
|
|
@@ -208,3 +217,163 @@ jobs:
|
|
|
208
217
|
- run: cargo clippy --all-targets -- -D warnings
|
|
209
218
|
- run: cargo test
|
|
210
219
|
<% } %>
|
|
220
|
+
<% if (it.options.database === 'mysql' || it.options.database === 'postgres') { %> db-migrate-smoke:
|
|
221
|
+
runs-on: ubuntu-latest
|
|
222
|
+
defaults:
|
|
223
|
+
run:
|
|
224
|
+
working-directory: server
|
|
225
|
+
services:
|
|
226
|
+
<% if (it.options.database === 'postgres') { %> postgres:
|
|
227
|
+
image: postgres:16-alpine
|
|
228
|
+
env:
|
|
229
|
+
POSTGRES_PASSWORD: postgres
|
|
230
|
+
POSTGRES_DB: ci_smoke
|
|
231
|
+
ports: ['5432:5432']
|
|
232
|
+
options: >-
|
|
233
|
+
--health-cmd pg_isready
|
|
234
|
+
--health-interval 10s
|
|
235
|
+
--health-timeout 5s
|
|
236
|
+
--health-retries 5
|
|
237
|
+
<% } else { %> mysql:
|
|
238
|
+
image: mysql:8
|
|
239
|
+
env:
|
|
240
|
+
MYSQL_ROOT_PASSWORD: ''
|
|
241
|
+
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
|
|
242
|
+
MYSQL_DATABASE: ci_smoke
|
|
243
|
+
ports: ['3306:3306']
|
|
244
|
+
options: >-
|
|
245
|
+
--health-cmd "mysqladmin ping -h localhost"
|
|
246
|
+
--health-interval 10s
|
|
247
|
+
--health-timeout 5s
|
|
248
|
+
--health-retries 5
|
|
249
|
+
<% } %> steps:
|
|
250
|
+
- uses: actions/checkout@v4
|
|
251
|
+
<% if (it.options.backend === 'node') { %> - uses: actions/setup-node@v4
|
|
252
|
+
with:
|
|
253
|
+
node-version: '20'
|
|
254
|
+
cache: npm
|
|
255
|
+
cache-dependency-path: server/package-lock.json
|
|
256
|
+
- run: npm ci
|
|
257
|
+
- name: migrate up
|
|
258
|
+
env:
|
|
259
|
+
<% if (it.options.database === 'postgres') { %> DB_HOST: 127.0.0.1
|
|
260
|
+
DB_PORT: '5432'
|
|
261
|
+
DB_USER: postgres
|
|
262
|
+
DB_PASSWORD: postgres
|
|
263
|
+
DB_NAME: ci_smoke
|
|
264
|
+
<% } else { %> DB_HOST: 127.0.0.1
|
|
265
|
+
DB_PORT: '3306'
|
|
266
|
+
DB_USER: root
|
|
267
|
+
DB_PASSWORD: ''
|
|
268
|
+
DB_NAME: ci_smoke
|
|
269
|
+
<% } %> run: npm run db:migrate
|
|
270
|
+
- name: migrate down
|
|
271
|
+
env:
|
|
272
|
+
<% if (it.options.database === 'postgres') { %> DB_HOST: 127.0.0.1
|
|
273
|
+
DB_PORT: '5432'
|
|
274
|
+
DB_USER: postgres
|
|
275
|
+
DB_PASSWORD: postgres
|
|
276
|
+
DB_NAME: ci_smoke
|
|
277
|
+
<% } else { %> DB_HOST: 127.0.0.1
|
|
278
|
+
DB_PORT: '3306'
|
|
279
|
+
DB_USER: root
|
|
280
|
+
DB_PASSWORD: ''
|
|
281
|
+
DB_NAME: ci_smoke
|
|
282
|
+
<% } %> run: npm run db:rollback
|
|
283
|
+
<% } %><% if (it.options.backend === 'java') { %> - uses: actions/setup-java@v4
|
|
284
|
+
with:
|
|
285
|
+
distribution: temurin
|
|
286
|
+
java-version: '21'
|
|
287
|
+
cache: maven
|
|
288
|
+
- name: flyway migrate + validate
|
|
289
|
+
env:
|
|
290
|
+
<% if (it.options.database === 'postgres') { %> SPRING_DATASOURCE_URL: jdbc:postgresql://127.0.0.1:5432/ci_smoke
|
|
291
|
+
SPRING_DATASOURCE_USERNAME: postgres
|
|
292
|
+
SPRING_DATASOURCE_PASSWORD: postgres
|
|
293
|
+
<% } else { %> SPRING_DATASOURCE_URL: jdbc:mysql://127.0.0.1:3306/ci_smoke?useSSL=false&serverTimezone=UTC
|
|
294
|
+
SPRING_DATASOURCE_USERNAME: root
|
|
295
|
+
SPRING_DATASOURCE_PASSWORD: ''
|
|
296
|
+
<% } %> run: |
|
|
297
|
+
mvn -B flyway:migrate
|
|
298
|
+
mvn -B flyway:validate
|
|
299
|
+
<% } %><% if (it.options.backend === 'python') { %> - uses: actions/setup-python@v5
|
|
300
|
+
with:
|
|
301
|
+
python-version: '3.12'
|
|
302
|
+
- run: pip install -e .
|
|
303
|
+
- name: alembic upgrade head + downgrade base
|
|
304
|
+
env:
|
|
305
|
+
<% if (it.options.database === 'postgres') { %> DATABASE_URL: postgresql+psycopg://postgres:postgres@127.0.0.1:5432/ci_smoke
|
|
306
|
+
<% } else { %> DATABASE_URL: mysql+pymysql://root:@127.0.0.1:3306/ci_smoke
|
|
307
|
+
<% } %> run: |
|
|
308
|
+
alembic upgrade head
|
|
309
|
+
alembic downgrade base
|
|
310
|
+
<% } %><% if (it.options.backend === 'go') { %> - uses: actions/setup-go@v5
|
|
311
|
+
with:
|
|
312
|
+
go-version: '1.22'
|
|
313
|
+
- name: install golang-migrate
|
|
314
|
+
run: |
|
|
315
|
+
go install -tags '<%= it.options.database %>' github.com/golang-migrate/migrate/v4/cmd/migrate@v4.18.1
|
|
316
|
+
- name: migrate up + down
|
|
317
|
+
env:
|
|
318
|
+
<% if (it.options.database === 'postgres') { %> DATABASE_URL: postgres://postgres:postgres@127.0.0.1:5432/ci_smoke?sslmode=disable
|
|
319
|
+
<% } else { %> DATABASE_URL: mysql://root:@tcp(127.0.0.1:3306)/ci_smoke?multiStatements=true
|
|
320
|
+
<% } %> run: |
|
|
321
|
+
migrate -path db/migrations -database "$DATABASE_URL" up
|
|
322
|
+
migrate -path db/migrations -database "$DATABASE_URL" down -all
|
|
323
|
+
<% } %><% } %>
|
|
324
|
+
<% if (it.options.database === 'elasticsearch') { %> db-es-template-apply:
|
|
325
|
+
runs-on: ubuntu-latest
|
|
326
|
+
defaults:
|
|
327
|
+
run:
|
|
328
|
+
working-directory: server
|
|
329
|
+
services:
|
|
330
|
+
elasticsearch:
|
|
331
|
+
image: elasticsearch:8.15.1
|
|
332
|
+
env:
|
|
333
|
+
discovery.type: single-node
|
|
334
|
+
xpack.security.enabled: 'false'
|
|
335
|
+
ES_JAVA_OPTS: '-Xms512m -Xmx512m'
|
|
336
|
+
ports: ['9200:9200']
|
|
337
|
+
options: >-
|
|
338
|
+
--health-cmd "curl -fsS http://localhost:9200/_cluster/health"
|
|
339
|
+
--health-interval 10s
|
|
340
|
+
--health-timeout 5s
|
|
341
|
+
--health-retries 12
|
|
342
|
+
steps:
|
|
343
|
+
- uses: actions/checkout@v4
|
|
344
|
+
<% if (it.options.backend === 'node') { %> - uses: actions/setup-node@v4
|
|
345
|
+
with:
|
|
346
|
+
node-version: '20'
|
|
347
|
+
cache: npm
|
|
348
|
+
cache-dependency-path: server/package-lock.json
|
|
349
|
+
- run: npm ci
|
|
350
|
+
- name: apply index templates
|
|
351
|
+
env:
|
|
352
|
+
ELASTICSEARCH_URL: http://127.0.0.1:9200
|
|
353
|
+
run: npm run es:apply
|
|
354
|
+
<% } %><% if (it.options.backend === 'java') { %> - uses: actions/setup-java@v4
|
|
355
|
+
with:
|
|
356
|
+
distribution: temurin
|
|
357
|
+
java-version: '21'
|
|
358
|
+
cache: maven
|
|
359
|
+
- name: spring boot starts and applies templates
|
|
360
|
+
env:
|
|
361
|
+
SPRING_ELASTICSEARCH_URIS: http://127.0.0.1:9200
|
|
362
|
+
run: mvn -B test -Dtest=*ApplicationTests
|
|
363
|
+
<% } %><% if (it.options.backend === 'python') { %> - uses: actions/setup-python@v5
|
|
364
|
+
with:
|
|
365
|
+
python-version: '3.12'
|
|
366
|
+
- run: pip install -e .
|
|
367
|
+
- name: apply index templates
|
|
368
|
+
env:
|
|
369
|
+
ELASTICSEARCH_URL: http://127.0.0.1:9200
|
|
370
|
+
run: python db/apply_templates.py
|
|
371
|
+
<% } %><% if (it.options.backend === 'go') { %> - uses: actions/setup-go@v5
|
|
372
|
+
with:
|
|
373
|
+
go-version: '1.22'
|
|
374
|
+
- run: go mod download
|
|
375
|
+
- name: apply index templates
|
|
376
|
+
env:
|
|
377
|
+
ELASTICSEARCH_URL: http://127.0.0.1:9200
|
|
378
|
+
run: make es-apply
|
|
379
|
+
<% } %><% } %>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.PHONY: build test lint run es-apply
|
|
2
|
+
|
|
3
|
+
ELASTICSEARCH_URL ?= http://127.0.0.1:9200
|
|
4
|
+
|
|
5
|
+
build:
|
|
6
|
+
go build -o ./bin/server ./cmd/server
|
|
7
|
+
|
|
8
|
+
run:
|
|
9
|
+
go run ./cmd/server
|
|
10
|
+
|
|
11
|
+
test:
|
|
12
|
+
go test ./...
|
|
13
|
+
|
|
14
|
+
lint:
|
|
15
|
+
golangci-lint run
|
|
16
|
+
|
|
17
|
+
es-apply:
|
|
18
|
+
go run ./cmd/apply-templates
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Idempotent Elasticsearch index-template applier (Go).
|
|
2
|
+
//
|
|
3
|
+
// ES 没有事务性 DDL;"migration" 退化为把 db/index-templates/*.json 全部 PUT 一次。
|
|
4
|
+
// ES 自身保证 PUT 同名 template 的幂等性。
|
|
5
|
+
//
|
|
6
|
+
// 改动 tier(参见 docs/governance/change-tiers.md):
|
|
7
|
+
// - 新增 template 文件:Tier 3
|
|
8
|
+
// - 修改既有 template 的 mapping:Tier 4(破坏性 - 可能 reindex)
|
|
9
|
+
// - 仅改 priority / 新增 alias:Tier 2
|
|
10
|
+
package main
|
|
11
|
+
|
|
12
|
+
import (
|
|
13
|
+
"bytes"
|
|
14
|
+
"context"
|
|
15
|
+
"fmt"
|
|
16
|
+
"log"
|
|
17
|
+
"os"
|
|
18
|
+
"path/filepath"
|
|
19
|
+
"sort"
|
|
20
|
+
"strings"
|
|
21
|
+
"time"
|
|
22
|
+
|
|
23
|
+
"github.com/elastic/go-elasticsearch/v8"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
const templateDir = "db/index-templates"
|
|
27
|
+
|
|
28
|
+
func main() {
|
|
29
|
+
url := os.Getenv("ELASTICSEARCH_URL")
|
|
30
|
+
if url == "" {
|
|
31
|
+
url = "http://127.0.0.1:9200"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
cfg := elasticsearch.Config{Addresses: []string{url}}
|
|
35
|
+
if u := os.Getenv("ELASTICSEARCH_USERNAME"); u != "" {
|
|
36
|
+
cfg.Username = u
|
|
37
|
+
cfg.Password = os.Getenv("ELASTICSEARCH_PASSWORD")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
es, err := elasticsearch.NewClient(cfg)
|
|
41
|
+
if err != nil {
|
|
42
|
+
log.Fatalf("✗ es client: %v", err)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 简易健康检查
|
|
46
|
+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
47
|
+
defer cancel()
|
|
48
|
+
if _, err := es.Cluster.Health(es.Cluster.Health.WithContext(ctx)); err != nil {
|
|
49
|
+
log.Fatalf("✗ es health: %v", err)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
files, err := filepath.Glob(filepath.Join(templateDir, "*.json"))
|
|
53
|
+
if err != nil {
|
|
54
|
+
log.Fatalf("✗ glob: %v", err)
|
|
55
|
+
}
|
|
56
|
+
sort.Strings(files)
|
|
57
|
+
if len(files) == 0 {
|
|
58
|
+
fmt.Println("no index templates found in", templateDir)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for _, path := range files {
|
|
63
|
+
body, err := os.ReadFile(path)
|
|
64
|
+
if err != nil {
|
|
65
|
+
log.Fatalf("✗ read %s: %v", path, err)
|
|
66
|
+
}
|
|
67
|
+
name := strings.TrimSuffix(filepath.Base(path), ".json")
|
|
68
|
+
res, err := es.Indices.PutIndexTemplate(name, bytes.NewReader(body))
|
|
69
|
+
if err != nil {
|
|
70
|
+
log.Fatalf("✗ apply %s: %v", name, err)
|
|
71
|
+
}
|
|
72
|
+
if res.IsError() {
|
|
73
|
+
log.Fatalf("✗ apply %s: %s", name, res.String())
|
|
74
|
+
}
|
|
75
|
+
_ = res.Body.Close()
|
|
76
|
+
fmt.Printf("✓ applied %s\n", name)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
fmt.Printf("done; %d template(s) applied to %s\n", len(files), url)
|
|
80
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# server/db/
|
|
2
|
+
|
|
3
|
+
Elasticsearch index-template 入口(Go 后端)。
|
|
4
|
+
|
|
5
|
+
> 跨项目的数据库归属规则见 [`docs/governance/database.md`](../../docs/governance/database.md)。
|
|
6
|
+
|
|
7
|
+
## "Migration" 的语义
|
|
8
|
+
|
|
9
|
+
**Elasticsearch 没有事务性 DDL,本目录里的 JSON 不是迁移文件。**
|
|
10
|
+
|
|
11
|
+
每个 `db/index-templates/*.json` 是一个 [Index Template v2](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-templates.html)。`cmd/apply-templates/main.go` 按文件名字典序遍历目录,对每个文件 PUT;ES 自身保证幂等。
|
|
12
|
+
|
|
13
|
+
## 工具与版本
|
|
14
|
+
|
|
15
|
+
| 项 | 值 |
|
|
16
|
+
|---|---|
|
|
17
|
+
| Elasticsearch | 8.15+ |
|
|
18
|
+
| Go 客户端 | `github.com/elastic/go-elasticsearch/v8` `8.15.0` |
|
|
19
|
+
|
|
20
|
+
## 目录布局
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
server/
|
|
24
|
+
├── go.mod
|
|
25
|
+
├── Makefile # `make es-apply` 入口
|
|
26
|
+
├── cmd/apply-templates/
|
|
27
|
+
│ └── main.go # 幂等 applier
|
|
28
|
+
└── db/
|
|
29
|
+
├── README.md
|
|
30
|
+
└── index-templates/
|
|
31
|
+
└── <NNNNNN>_<description>.json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 常用命令
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
make es-apply # 把 db/index-templates/*.json 全部 PUT 一次
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
环境变量 `ELASTICSEARCH_URL`(默认 `http://127.0.0.1:9200`)、`ELASTICSEARCH_USERNAME` / `ELASTICSEARCH_PASSWORD`(可选)。
|
|
41
|
+
|
|
42
|
+
## 改动分级(参见 `docs/governance/change-tiers.md`)
|
|
43
|
+
|
|
44
|
+
| 改动 | Tier |
|
|
45
|
+
|---|---|
|
|
46
|
+
| 新增 template 文件 | 3 |
|
|
47
|
+
| 修改既有 template 的 mapping 字段 | 4(破坏性 - 可能 reindex) |
|
|
48
|
+
| 仅改 priority / 新增 alias / 改 settings.refresh_interval | 2 |
|
|
49
|
+
| 删除 template | 4 |
|
|
50
|
+
|
|
51
|
+
## 与 `contracts/asyncapi/` 的同步
|
|
52
|
+
|
|
53
|
+
事件 ingest 的 schema 真值在 `contracts/asyncapi/asyncapi.yaml`;index-template 的 mapping 应当与之**对齐**。当前手动同步。
|
|
54
|
+
|
|
55
|
+
## CI 行为
|
|
56
|
+
|
|
57
|
+
CI 起一个 ES 8 service container;`make es-apply` 失败 = CI 红。
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"index_patterns": ["events-*"],
|
|
3
|
+
"priority": 100,
|
|
4
|
+
"template": {
|
|
5
|
+
"settings": {
|
|
6
|
+
"number_of_shards": 1,
|
|
7
|
+
"number_of_replicas": 1,
|
|
8
|
+
"refresh_interval": "5s"
|
|
9
|
+
},
|
|
10
|
+
"mappings": {
|
|
11
|
+
"properties": {
|
|
12
|
+
"@timestamp": { "type": "date" },
|
|
13
|
+
"tenant_id": { "type": "keyword" },
|
|
14
|
+
"event_type": { "type": "keyword" },
|
|
15
|
+
"user_id": { "type": "keyword" },
|
|
16
|
+
"payload": { "type": "object", "dynamic": true },
|
|
17
|
+
"trace_id": { "type": "keyword" }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"_meta": {
|
|
22
|
+
"description": "Sample event-stream template. Replace mappings with your domain fields.",
|
|
23
|
+
"managed_by": "@wneng/create-keel"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: db-go-elasticsearch
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
appliesWhen:
|
|
4
|
+
backend: go
|
|
5
|
+
database: elasticsearch
|
|
6
|
+
priority: 40
|
|
7
|
+
files:
|
|
8
|
+
- from: files/go.mod
|
|
9
|
+
to: server/go.mod
|
|
10
|
+
render: true
|
|
11
|
+
- from: files/Makefile
|
|
12
|
+
to: server/Makefile
|
|
13
|
+
render: true
|
|
14
|
+
- from: files/apply_templates.go
|
|
15
|
+
to: server/cmd/apply-templates/main.go
|
|
16
|
+
render: false
|
|
17
|
+
- from: files/index-template-init.json
|
|
18
|
+
to: server/db/index-templates/000001_init.json
|
|
19
|
+
render: false
|
|
20
|
+
- from: files/db-README.md
|
|
21
|
+
to: server/db/README.md
|
|
22
|
+
render: true
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
-- 000001_init.up.sql — 初始化迁移
|
|
2
|
+
--
|
|
3
|
+
-- 命名规则:<NNNNNN>_<description>.up.sql / .down.sql。
|
|
4
|
+
-- 已合入 main 不可修改;改 schema 写新文件 NNNNNN+1。
|
|
5
|
+
|
|
6
|
+
CREATE TABLE IF NOT EXISTS roles (
|
|
7
|
+
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
8
|
+
code VARCHAR(64) NOT NULL,
|
|
9
|
+
name VARCHAR(128) NOT NULL,
|
|
10
|
+
description TEXT NULL,
|
|
11
|
+
created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
12
|
+
updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
|
13
|
+
PRIMARY KEY (id),
|
|
14
|
+
UNIQUE KEY uk_roles_code (code)
|
|
15
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
16
|
+
|
|
17
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
18
|
+
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
19
|
+
email VARCHAR(255) NOT NULL,
|
|
20
|
+
password_hash VARCHAR(255) NOT NULL,
|
|
21
|
+
display_name VARCHAR(128) NULL,
|
|
22
|
+
role_id BIGINT UNSIGNED NOT NULL,
|
|
23
|
+
created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
24
|
+
updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
|
25
|
+
deleted_at TIMESTAMP(6) NULL,
|
|
26
|
+
PRIMARY KEY (id),
|
|
27
|
+
UNIQUE KEY uk_users_email (email),
|
|
28
|
+
KEY idx_users_role_id (role_id),
|
|
29
|
+
KEY idx_users_deleted_at (deleted_at),
|
|
30
|
+
CONSTRAINT fk_users_role FOREIGN KEY (role_id) REFERENCES roles (id)
|
|
31
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
32
|
+
|
|
33
|
+
INSERT IGNORE INTO roles (code, name, description) VALUES
|
|
34
|
+
('admin', '系统管理员', '可执行所有管理操作'),
|
|
35
|
+
('user', '普通用户', '默认注册角色');
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
.PHONY: build test lint run db-up db-down db-status db-create
|
|
2
|
+
|
|
3
|
+
# Default DSN: localhost MySQL with the project name as DB. Production
|
|
4
|
+
# overrides via DATABASE_URL env var.
|
|
5
|
+
DATABASE_URL ?= mysql://root:@tcp(127.0.0.1:3306)/<%= it.options.projectName %>_dev?multiStatements=true
|
|
6
|
+
|
|
7
|
+
# golang-migrate CLI: install with `go install github.com/golang-migrate/migrate/v4/cmd/migrate@v4.18.1`
|
|
8
|
+
MIGRATE := migrate -path db/migrations -database "$(DATABASE_URL)"
|
|
9
|
+
|
|
10
|
+
build:
|
|
11
|
+
go build -o ./bin/server ./cmd/server
|
|
12
|
+
|
|
13
|
+
run:
|
|
14
|
+
go run ./cmd/server
|
|
15
|
+
|
|
16
|
+
test:
|
|
17
|
+
go test ./...
|
|
18
|
+
|
|
19
|
+
lint:
|
|
20
|
+
golangci-lint run
|
|
21
|
+
|
|
22
|
+
db-up:
|
|
23
|
+
$(MIGRATE) up
|
|
24
|
+
|
|
25
|
+
db-down:
|
|
26
|
+
$(MIGRATE) down 1
|
|
27
|
+
|
|
28
|
+
db-status:
|
|
29
|
+
$(MIGRATE) version
|
|
30
|
+
|
|
31
|
+
# Usage: make db-create name=create_orders
|
|
32
|
+
db-create:
|
|
33
|
+
$(MIGRATE) create -ext sql -dir db/migrations -seq $(name)
|