agentleFS
nieyuanqingnieyuanqing/software-engineering-skills/.claude/skills/new-java-project/SKILL.md

new-java-project

Skill4 starsChanged 24 days agoApache-2.0View on GitHub

为 Java/Spring Boot 工程生成完整的标准化部署配置:deploy.sh、apply-ssl.sh、手工连库入口 db-sql.sh(默认只读、写需 --apply)、Flyway 迁移目录(src/main/resources/db/migration)、nginx 站点配置(dev/test/prod 三套)、三套键集对齐的 .env、Spring Boot 配置与健康检查端点、specs/deployment.md、标准 .gitignore(含 env 与 SQL 文件忽略)、sql 备份目录。数据库结构由 Flyway 在应用启动时自动迁移(版本记账用 flyway_schema_history,Hibernate 只 validate)。supervisord 配置由 deploy.sh 部署时 inline 生成。所有产物遵循共享主机部署通用规范(统一目录、健康检查、日志格式、supervisord 安全操作规范)。当用户要求"初始化部署"、"创建部署脚本"、"配置 nginx/supervisor"、"配置 Flyway 数据库迁移"、"新建 Java 工程部署"时触发。支持 /new-java-project -h 查看帮助。

  • Reads credentials

The file

Files
SKILL.md

new-java-project

为新工程生成完整的标准化部署配置,包括部署脚本、手工连库入口 db-sql.sh、nginx 站点配置、 环境变量模板、Spring Boot 配置(含 Flyway 自动迁移)与健康检查端点、部署规范文档和 .gitignore。所有产物严格遵循本 skill 目录下 specs/deployment-common.md 的共享主机部署通用规范, 不包含任何硬编码的项目信息。

触发条件:用户要求为某工程创建部署配置、部署脚本、nginx/supervisor 配置,或说"初始化部署"。

与 /new-nginx-conf 的分工:deploy-conf/nginx/ 这棵目录树由两个 skill 共同拥有—— 主机级基础配置(nginx.conf、subconf/、mime.types、错误页)归 /new-nginx-conf, 本 skill 只生成本站点的 <SERVICE_NAME>.{dev,test,prod}.conf 三份配置,不生成、不覆盖主机级文件。 目标主机上缺 nginx.conf 不影响部署:deploy.sh 只同步站点配置,遇到主配置缺失会跳过并打警告。


零、参数处理

如果用户传入 -h 或 --help,不执行任何文件操作,直接把下面的帮助信息原样输出在本次回复正文里后结束(斜杠命令把 SKILL.md 注入我的上下文不等于已展示给用户,正文里只回一句"已输出"就是没输出)。-h 与其它参数或说明文字同时出现时,一律只出帮助、忽略其余参数:本轮不猜附加要求,需要同时执行就把 -h 去掉分两次调用:


用法: /new-java-project [SERVICE_NAME] [选项]

  SERVICE_NAME    可选位置参数,直接指定服务名。
                  不传时提示采用当前目录名称作为默认值。

选项(通过命令行传入的参数直接使用,不再交互询问)
  --nginx-port=N          nginx 对外监听端口(如 --nginx-port=50000)
  --app-port=N            Spring Boot 内部端口(如 --app-port=50001)
  --db-port=N             PostgreSQL 端口(如 --db-port=5432)
  --db-name=NAME          数据库名(如 --db-name=my-service)
  --test-domain=DOMAIN    test 环境域名(如 --test-domain=svc.test.example.com)
  --prod-domain=DOMAIN    prod 环境域名(如 --prod-domain=svc.example.com)
  --has-web=true|false    是否有前端静态资源,默认 true(如 --has-web=false)
  --web-path=PATH         前端路径前缀,默认 /<SERVICE_NAME>/web,不带结尾斜杠(如 --web-path=/admin)
  --api-prefix=PREFIX     API 路径前缀,默认 /<SERVICE_NAME>/api,不带结尾斜杠(如 --api-prefix=/v1)
  --base-package=PKG      Java 根包名,默认 com.<SERVICE_NAME 去掉连字符>,
                          用于 application-<profile>.yml 的日志级别键与 RootController 的 package
                          (如 --base-package=com.example.myservice)
  --jdk-version=N         JDK 基线版本,默认 21(如 --jdk-version=21)
  --pg-version=N          PostgreSQL 基线版本,默认 17(如 --pg-version=17)
  -h, --help              显示本帮助(出现即忽略其它参数,只出帮助)

功能
  为 Java/Spring Boot 工程生成完整的标准化部署配置,包括:
    - scripts/deploy.sh                    部署脚本(Maven 构建、supervisord 管理、健康检查、
                                           远程部署、多微服务/多前端、nginx 站点自动同步、数据库同步)
    - scripts/apply-ssl.sh                 SSL 证书申请脚本(Let's Encrypt + acme.sh,
                                           证书按域名装到 /etc/nginx/ssl/<域名>.{pem,key})
    - scripts/db-sql.sh                    手工连库入口:取连接参数、拼 ssh、起 psql;
                                           默认只读(数据库侧强制 read_only),写只认 --apply,
                                           prod 手工写还要 --prod-approved。**只做数据订正与查询,
                                           不改结构**——结构归 Flyway
    - src/backend/<name>/src/main/resources/db/migration/   Flyway 迁移脚本目录(占位 .gitkeep;
                                           V<n>__<主题>.sql 写在这里,随 jar 打包、入库)
    - deploy-conf/nginx/<name>.dev.conf    nginx 站点配置(dev,HTTP/IP+端口)
    - deploy-conf/nginx/<name>.test.conf   nginx 站点配置(test,HTTPS/域名)
    - deploy-conf/nginx/<name>.prod.conf   nginx 站点配置(prod,HTTPS/域名)
    - src/backend/<name>/.env              环境变量(dev,同时是键集基准)
    - src/backend/<name>/.env.test         环境变量(test),键集与 .env 一致
    - src/backend/<name>/.env.prod         环境变量(prod),键集与 .env 一致
    - src/backend/<name>/src/main/resources/application.yml       公共配置(端口/地址、时区、
                                           上传上限、JPA、Flyway 自动迁移、Actuator、Swagger 开关)
    - src/backend/<name>/src/main/resources/application-dev.yml   dev profile(数据源、show-sql、DEBUG)
    - src/backend/<name>/src/main/resources/application-test.yml  test profile(数据源、INFO)
    - src/backend/<name>/src/main/resources/application-prod.yml  prod profile(数据源、WARN)
    - src/backend/<name>/src/main/java/<包>/config/RootController.java
                                           健康检查端点 /api/<name>/health + 服务说明端点
    - sql/README.md + sql/backup/          数据库备份目录(备份导出文件不入库)
    - specs/deployment.md                  本工程专属部署规范文档
    - specs/baseline-versions.md           基线版本规范(JDK、PostgreSQL、Spring Boot 等)
    - .gitignore                           标准忽略清单(env/证书/备份不入库,迁移 SQL 入库)

  所有产物遵循随本 skill 分发的 specs/deployment-common.md 跨项目通用规范:
    - 统一目录约定:/opt/soft/apps/<name>/、/data/logs/apps/<name>/
    - 健康检查端点:应用内 /api/<name>/health,经 nginx 为 /<name>/api/health
      (startsecs=10 只防立即崩溃;就绪与否由健康检查判定,最长等待 420s,每 5s 一轮)
    - 部署日志格式:[YYYY-MM-DD HH:MM:SS] [deploy.sh] <message>,阶段日志 Phase N/M,
      结尾 [STATUS] OK|ERROR - <结论> 供 CI/agent 机器读取
      (db-sql.sh 的日志一律走 stderr,stdout 只出 psql 结果表;结尾同样有 [STATUS] 行)
    - supervisord 配置由 deploy.sh 在部署时 inline 生成(不从静态 ini 文件复制),
      文件后缀现问目标主机 [include] files= 模式决定(.conf 或 .ini;写错则配置永不加载)
    - JAR 版本化落盘(<name>-<版本>.jar)+ 稳定软链(<name>.jar),回滚只改软链
    - 支持 --remote USER@HOST 远程部署(本地构建,rsync 上传,SSH 远程重启)
    - 后端部署后自动同步 nginx 站点配置;完整 nginx 安装走 --target ssl
    - 跨域在 nginx 层用 map $http_origin 白名单处理;访问日志里 Bearer 令牌只记前 8 位
    - 三套 .env 键集必须一致,deploy.sh 部署前比对并告警缺失的键
    - schema 只有一条写入路径:Flyway(迁移脚本在 classpath:db/migration,应用启动时自动迁移,
      版本记账在目标库的 flyway_schema_history);Hibernate 只 validate,手工脚本不碰结构

示例
  /new-java-project
      交互式向导,以当前目录名为默认服务名,逐步询问所有参数

  /new-java-project my-service
      指定服务名,其余参数交互询问

  /new-java-project my-service --nginx-port=50000 --app-port=50001 --db-port=5432
      指定服务名和端口,其余参数(域名、HAS_WEB 等)仍交互询问

  /new-java-project my-service --nginx-port=50000 --app-port=50001 --db-port=5432 \
      --db-name=my-service --test-domain=svc.test.example.com \
      --prod-domain=svc.example.com --has-web=false
      所有参数均通过命令行指定,直接回显确认后生成文件,不需要任何交互

  /new-java-project -h
      显示本帮助

生成后自动执行
  chmod +x scripts/deploy.sh scripts/apply-ssl.sh scripts/db-sql.sh
  替换完成后校验:bash -n 逐个脚本(应为替换后的真实值,不再含 <占位符>)
  校验 db-sql.sh 顶部的 SERVICES 表与 deploy.sh 一致
  在目标工程 specs/deployment-common.md 的端口登记表中追加新端口行
  (如该文件不存在,询问用户是否用本 skill 自带副本初始化后再登记)

注意
  - 如目标文件已存在,会展示差异并询问是否覆盖,不会静默覆盖
  - .env / .env.test / .env.prod 里的密码保持占位符 changeme,不会写入真实凭证;
    这三份文件被 .gitignore 忽略,不入库
  - Flyway 迁移脚本(src/main/resources/db/migration/*.sql)入库,与代码同版本演进;
    结构变更不需要人工执行步骤 —— 部署(重启进程)本身就是迁移
  - db-sql.sh 只做查询与数据订正,不承担结构变更(避免和 flyway_schema_history 两套记账)
  - 不会自动创建 CLAUDE.md,但如已存在会在其中追加 specs/deployment.md 引用

一、收集参数

1.1 解析命令行参数

按以下规则从用户输入中提取参数,已提取到的参数跳过后续交互询问:

命令行写法对应参数
第一个非 --/- 开头的词SERVICE_NAME
--nginx-port=NNGINX_PORT
--app-port=NAPP_PORT
--db-port=NDB_PORT
--db-name=NAMEDB_NAME
--test-domain=DOMAINTEST_DOMAIN
--prod-domain=DOMAINPROD_DOMAIN
--has-web=true|falseHAS_WEB(默认 true)
--web-path=PATHWEB_PATH(默认 /<SERVICE_NAME>/web,去掉结尾斜杠)
--api-prefix=PREFIXAPI_PATH_PREFIX(默认 /<SERVICE_NAME>/api,去掉结尾斜杠)
--base-package=PKGBASE_PACKAGE(默认 com. + SERVICE_NAME 去掉 -/_/空格)
--jdk-version=NJDK_VERSION(默认 21)
--pg-version=NPG_VERSION(默认 17)

用户传入带结尾斜杠的 --web-path / --api-prefix 时,去掉结尾斜杠再替换进模板 (模板里所有引用处都自己补 /,见第三节占位符说明)。

1.2 SERVICE_NAME 确定(优先级从高到低)

  1. 命令行位置参数(如 /new-java-project my-service):直接使用,不再询问。
  2. 无位置参数:读取当前工作目录名,转为小写、空格替换为连字符,作为建议默认值,提示:

    SERVICE_NAME 未指定,建议使用当前目录名 "<目录名>" 作为服务名,直接回车确认或输入新名称:

  3. 用户手动输入:用户输入其他名称则使用输入值。

SERVICE_NAME 会同时用作 Maven artifactId、spring.application.name、supervisord 程序名、 数据库名与用户名(通用规范第一节要求全同名)。含连字符的名称在 Java 包名与 upstream 名称里 不合法,因此 BASE_PACKAGE 默认值与 nginx 的 map/log_format/upstream 名称会把连字符换成下划线。

1.3 交互询问缺失参数

SERVICE_NAME 确定后,将所有未通过命令行提供的必填参数一次性列出,统一询问(不要一个一个问):

参数说明默认值
NGINX_PORTnginx 对外监听端口,需通过 ss -tln 确认未被占用无
APP_PORTSpring Boot 内部端口(只绑 127.0.0.1),通常 NGINX_PORT + 1无
DB_PORTPostgreSQL 端口5432
DB_NAME数据库名与 SERVICE_NAME 相同
TEST_DOMAINtest 环境域名无
PROD_DOMAINprod 环境域名无
HAS_WEB是否有前端静态资源(true/false)true
WEB_PATH前端路径前缀(HAS_WEB=true 时有效),不带结尾斜杠/<SERVICE_NAME>/web
API_PATH_PREFIXnginx 反代 API 路径前缀,不带结尾斜杠/<SERVICE_NAME>/api
BASE_PACKAGEJava 根包名(日志级别键 + RootController 的 package)com.<SERVICE_NAME 去连字符>
JDK_VERSIONJDK 基线版本21
PG_VERSIONPostgreSQL 基线版本17

所有参数确定后,向用户回显完整参数列表,确认无误后再生成文件。


二、生成文件清单

以下文件全部在目标工程根目录下生成(即用户当前在操作的工程,不是 skill 安装目录本身)。 deploy-conf/nginx/ 下的主机级基础配置(nginx.conf、subconf/ 等)属于 /new-nginx-conf skill, 本 skill 不生成、不覆盖,只写本站点的三份 <SERVICE_NAME>.*.conf:

scripts/deploy.sh
scripts/apply-ssl.sh
scripts/db-sql.sh
.gitignore
deploy-conf/nginx/<SERVICE_NAME>.dev.conf
deploy-conf/nginx/<SERVICE_NAME>.test.conf
deploy-conf/nginx/<SERVICE_NAME>.prod.conf
src/backend/<SERVICE_NAME>/.env
src/backend/<SERVICE_NAME>/.env.test
src/backend/<SERVICE_NAME>/.env.prod
src/backend/<SERVICE_NAME>/src/main/resources/application.yml
src/backend/<SERVICE_NAME>/src/main/resources/application-dev.yml
src/backend/<SERVICE_NAME>/src/main/resources/application-test.yml
src/backend/<SERVICE_NAME>/src/main/resources/application-prod.yml
src/backend/<SERVICE_NAME>/src/main/java/<BASE_PACKAGE 的路径段>/config/RootController.java
src/backend/<SERVICE_NAME>/src/main/resources/db/migration/.gitkeep
sql/README.md
specs/deployment.md
specs/baseline-versions.md

sql/backup/ 同时创建(空目录,整体被 .gitignore 忽略,不需要占位文件)。 db/migration/.gitkeep 是空占位文件,保证迁移目录可提交;工程第一个真实结构变更就是往这里 放 V1__<主题>.sql(Flyway 在 locations 里找不到脚本时不会报错,空目录是合法的"还没有迁移")。


三、生成规则

每个文件的内容来自本 skill 目录下的对应模板文件(随 skill 一起分发,不依赖任何仓库克隆或本地工程), 将所有占位符替换为实际参数值。

占位符替换表

占位符替换为
<SERVICE_NAME>SERVICE_NAME 参数值
<NGINX_PORT>NGINX_PORT 参数值
<APP_PORT>APP_PORT 参数值
<DB_PORT>DB_PORT 参数值
<DB_NAME>DB_NAME 参数值
<TEST_DOMAIN>TEST_DOMAIN 参数值
<PROD_DOMAIN>PROD_DOMAIN 参数值
<API_PATH_PREFIX>API_PATH_PREFIX 参数值,不带结尾斜杠(如 /my-service/api)
<WEB_PATH>WEB_PATH 参数值,不带结尾斜杠(如 /my-service/web)
<BASE_PACKAGE>BASE_PACKAGE 参数值(如 com.example.myservice)
<JDK_VERSION>JDK_VERSION 参数值(默认 21)
<PG_VERSION>PG_VERSION 参数值(默认 17)
<CREATE_DATE>当前日期,格式 YYYY-MM-DD

模板里的引用处一律自己补 /(location <API_PATH_PREFIX>/ {、<API_PATH_PREFIX>/health、 try_files ... <WEB_PATH>/index.html),所以替换值带结尾斜杠会生成双斜杠路径。 nginx 站点配置里 map/log_format/upstream 的名称写作 <SERVICE_NAME>_xxx, 含连字符的服务名要换成下划线(Java 标识符与 nginx 变量名都不接受 -)。

各文件来源

以下"模板来源"路径均相对于本 skill 目录(安装后通常为 ~/.qoder/skills/new-java-project/)。

目标文件模板来源
scripts/deploy.shtemplates/scripts/deploy.sh
scripts/apply-ssl.shtemplates/scripts/apply-ssl.sh
scripts/db-sql.shtemplates/scripts/db-sql.sh
.gitignoretemplates/gitignore
deploy-conf/nginx/<SERVICE_NAME>.dev.conftemplates/deploy-conf/nginx/service.dev.conf
deploy-conf/nginx/<SERVICE_NAME>.test.conftemplates/deploy-conf/nginx/service.test.conf
deploy-conf/nginx/<SERVICE_NAME>.prod.conftemplates/deploy-conf/nginx/service.prod.conf
src/backend/<SERVICE_NAME>/.envtemplates/src/backend/service/.env
src/backend/<SERVICE_NAME>/.env.testtemplates/src/backend/service/.env.test
src/backend/<SERVICE_NAME>/.env.prodtemplates/src/backend/service/.env.prod
.../src/main/resources/application.ymltemplates/src/backend/service/src/main/resources/application.yml
.../src/main/resources/application-dev.ymltemplates/src/backend/service/src/main/resources/application-dev.yml
.../src/main/resources/application-test.ymltemplates/src/backend/service/src/main/resources/application-test.yml
.../src/main/resources/application-prod.ymltemplates/src/backend/service/src/main/resources/application-prod.yml
.../src/main/java/<包路径>/config/RootController.javatemplates/src/backend/service/src/main/java/config/RootController.java(<BASE_PACKAGE> 按 . 拆成目录)
sql/README.mdtemplates/sql/README.md
specs/deployment.mdspecs/deployment-template.md
specs/baseline-versions.mdspecs/baseline-versions-template.md

templates/scripts/db-sql.sh 里的 <SERVICE_NAME>(SERVICES 表与 -s <SERVICE_NAME> 示例) 按参数替换,替换后脚本认得的服务名与 src/backend/<SERVICE_NAME>/ 目录名同源。

.gitignore 生成规则

  • 目标工程没有 .gitignore → 按 templates/gitignore 模板整体生成(含 Java/Maven、IDE、日志、 env 环境变量文件、SSL 证书与私钥、SQL/数据库备份、Flyway 迁移脚本入库例外、 前端与构建产物、Android 签名与 SDK 路径等条目)。
  • 目标工程已有 .gitignore → 不覆盖,仅将模板中缺失的条目合并追加(重点确保: .env / .env.* / *.env / application-local.yml,deploy-conf/nginx/cert/*.pem|*.key, *.sql / *.dump / *.sqlite / *.db 配合 sql/backup/ 忽略, !src/backend/**/src/main/resources/db/migration/*.sql(Flyway 迁移脚本要入库), runtime/ / web-tars/ / mobile-apps/),已存在的条目不重复添加。
  • 注意:src/backend/**/.env* 三份环境变量文件必须被忽略(含真实凭证); application*.yml 是被跟踪文件,里面只允许 ${VAR:} 形式的空默认值。
  • 注意 ! 例外的位置:git 无法重新包含"被排除目录里面"的文件,所以工程里若把 target/ 之外的 某级源码目录整体排除,迁移脚本的例外会静默失效——追加条目后要用 git check-ignore -v src/backend/<服务>/src/main/resources/db/migration/V1__x.sql 实测一次(应无输出)。

HAS_WEB=false 时的处理

如果 HAS_WEB=false:

  1. 在生成的三份 nginx 配置中删除 # ── 前端静态资源 注释块及其下方到块尾的 location <WEB_PATH>/ { ... } 与 location ^~ <WEB_PATH>/_next/static/ { ... } 两块。
  2. 把 scripts/deploy.sh 顶部的 HAS_WEB=true 改为 HAS_WEB=false,并把 WEB_APPS=("<SERVICE_NAME>") 置为 WEB_APPS=()(四张 WEB_APP_* 表保留但不会被用到)。改完后 --target web 与 all 会明确报错"本服务无前端",而不是静默什么也不做。

多微服务与多前端

本 skill 一次只为一个服务生成配置。目标工程已有微服务时(src/backend/ 下不止一个模块), 询问用户是把新服务并入已有 deploy.sh 的服务表还是新生成一份脚本,默认并入:

  • 在 scripts/deploy.sh 顶部服务表追加一条:SERVICES、SERVICE_PORTS、SERVICE_HEALTH_PATHS、 SERVICE_DBS 各加一项(新服务用独立端口,并登记到端口总表)
  • 同步 scripts/db-sql.sh 顶部的 SERVICES 表:两份脚本的服务名单必须完全一致,否则会出现 "deploy.sh 能部、db-sql.sh 说这服务不在册"的裂口;同时为新服务建 src/backend/<新服务>/src/main/resources/db/migration/ 目录(独立库就独立一套 flyway_schema_history)
  • 在 deploy-conf/nginx/<站点名>.{dev,test,prod}.conf 里为该服务追加 upstream + location (按路径前缀分流),站点配置本身是一个站点一份,多微服务共用
  • 新服务自己的 src/backend/<新服务>/.env* 三份文件照常生成

前端按端拆分(如管理台 + 门店端)时,把 WEB_APPS 改成多项并填齐 WEB_APP_SOURCE / WEB_APP_DEPLOY / WEB_APP_BASE_PATH / WEB_APP_PROJECT_ID 四张表;只往 WEB_APPS 加名字时, 脚本会按 src/web/<名字>、web-<名字>、/<名字> 的目录约定兜底补全。 WEB_APP_BASE_PATH 必须与 nginx 里对应的 location 前缀一致,否则静态资源 404。

目标工程已有配置文件时

application.yml 与 .env* 属于工程自身可能已经存在的文件。逐个比对:

  • 不存在 → 按模板生成
  • 已存在 → 展示差异并询问;用户选择保留时,只需确认已有文件里 server.port、server.address、健康检查路径与 .env 键集与本次生成的部署配置口径一致, 不一致要指出,不能各留一套

工程已有自己的结构迁移机制时,先弄清是哪一种,不能两边并行:

  • 已经在用 Flyway(库里有 flyway_schema_history)→ 迁移脚本目录沿用工程现有的 locations, 不新建 db/migration/;application.yml 里只补 baseline-on-migrate / baseline-version 这类接管所需的位,db-sql.sh 照常生成(它只做查询与订正)
  • 用的是另一套迁移机制(自研脚本、Liquibase、手工 SQL)→ 问用户要留哪一条。同一个库有两个 记账来源时,"这条变更到底跑没跑"会有两个互相矛盾的答案,两边都留不如一次定下唯一入口
  • 库里已有结构但没有 flyway_schema_history → 靠 baseline-on-migrate: true + baseline-version: 0 让 Flyway 补基线,或让用户先手工 flyway baseline;两种做法都要在 specs/deployment.md 里写清这次接管把库记成了哪一版,否则下一个 V 文件的编号没有参照

四、生成后处理

设置文件权限并校验替换结果

chmod +x scripts/deploy.sh scripts/apply-ssl.sh scripts/db-sql.sh
for f in deploy apply-ssl db-sql; do bash -n "scripts/$f.sh" || echo "语法检查失败: $f"; done
grep -rn '<[A-Z_]\{3,\}>' scripts/ deploy-conf/nginx/ src/backend/<SERVICE_NAME>/   # 应无输出

bash -n 在模板原文件上必然失败(NGINX_PORT=<NGINX_PORT> 不是合法 bash), 只有替换成真实值之后才用它来验证替换是否完整。

校验迁移链路可用(不连库的这几步都要过)

bash scripts/db-sql.sh -h                     # 应打出完整用法(含与 Flyway 的分工说明)
grep -n '^SERVICES=' scripts/deploy.sh scripts/db-sql.sh    # 两份脚本的名单一致
grep -n -A4 'flyway:' src/backend/<SERVICE_NAME>/src/main/resources/application.yml \
    | grep -E 'enabled|locations'             # enabled 默认 true、locations 指 classpath:db/migration
ls src/backend/<SERVICE_NAME>/src/main/resources/db/migration/    # 目录存在(哪怕是空的 .gitkeep)
git check-ignore -v src/backend/<SERVICE_NAME>/src/main/resources/db/migration/V1__x.sql  # 应无输出
grep -rn 'flyway-core' pom.xml src/backend/<SERVICE_NAME>/pom.xml 2>/dev/null \
    || echo "缺依赖:pom.xml 必须显式加 org.flywaydb:flyway-core(+ flyway-database-postgresql)"

最后一行是"迁移脚本会不会被 .gitignore 的 *.sql 吃掉"的实测:被吃掉的话,脚本只活在作者本机, 换个人 clone 出来部署就静默少一次迁移 —— 这条一定要跑,不要靠眼看。

校验三份 env 键集一致

生成后立刻比对一次,作为交付质量检查(键集不齐会让后面每次部署都打警告):

for f in .env .env.test .env.prod; do
  grep -oE '^[A-Z_]+=' src/backend/<SERVICE_NAME>/$f | tr -d '=' | sort > /tmp/k_$f.txt
done
comm -3 /tmp/k_.env.txt /tmp/k_.env.test.txt && comm -3 /tmp/k_.env.txt /tmp/k_.env.prod.txt   # 应无输出

更新目标工程 specs/deployment-common.md 端口登记

在目标工程的 specs/deployment-common.md「端口分配总表」中追加一行:

| `<NGINX_PORT>-<APP_PORT>` | <SERVICE_NAME>(见 [deployment.md](./deployment.md) 第三节) |

如果目标工程没有 specs/deployment-common.md:

  1. 询问用户:目标工程没有 specs/deployment-common.md(共享主机端口登记表)。是否用本 skill 自带的通用规范副本初始化该文件后再登记端口?(若这台主机上其他工程已有该文件,建议改为登记到那份文件)
  2. 用户同意 → 将本 skill 目录下 specs/deployment-common.md 复制为目标工程 specs/deployment-common.md,然后追加端口行。
  3. 用户拒绝 → 跳过,但告知用户:"请在共享主机的端口分配总表(deployment-common.md)中登记这两个端口,防止其他服务端口冲突。"

五、完成提示

生成完成后,向用户输出以下内容:

  1. 已生成文件列表(逐行列出路径)
  2. 下一步操作(根据实际情况生成,不要直接复制粘贴,而是用实际参数值):
## 下一步操作

### 首次部署(dev 环境,本机)

1. 确认 supervisord daemon 正在运行,且端口 <NGINX_PORT>/<APP_PORT> 空闲:
   supervisorctl status
   ss -tln | grep -E ':(<NGINX_PORT>|<APP_PORT>)\b'   # 应为空输出
   nginx -t                                           # 应为通过(先拿到基线状态)

2. 准备数据库(如尚未创建):
   sudo -u postgres createuser -P <DB_NAME>
   sudo -u postgres createdb -O <DB_NAME> <DB_NAME>
   # 并确认 pom.xml 里有 org.flywaydb:flyway-core(+ flyway-database-postgresql):
   # 缺依赖时 spring.flyway.* 被静默忽略,一次迁移都不会跑

3. 填入真实凭证(三份 env 已在生成时写好,键集必须保持齐):
   vim src/backend/<SERVICE_NAME>/.env      # DB_PASSWORD 等
   # 模板只包含生成的代码真正读取的键;业务自己新增的密钥(如 JWT_SECRET、第三方 API Key)
   # 按需追加,但必须三份同步加,否则 deploy.sh 会在部署时告警

4. 执行部署:
   bash scripts/deploy.sh
   # 空库首次启动时 Flyway 建 flyway_schema_history 并跑完 db/migration/ 下的全部脚本,
   # 结构随这次部署一起落到位,不需要单独"建表"步骤

### 此后的结构变更(日常最常走的路径)

1. 写迁移:src/backend/<SERVICE_NAME>/src/main/resources/db/migration/V<n>__<主题>.sql
   (编号接现有最大号往后,不复用不重排;正文写成幂等 SQL)
2. 本地起一次 = dev 库迁移完成,确认落到哪一版(只读):
   echo 'select version,description,success from flyway_schema_history order by installed_rank desc limit 3' \
       | bash scripts/db-sql.sh -e dev -s <SERVICE_NAME>
3. test / prod 没有额外步骤:**部署本身就是迁移**(应用启动时 Flyway 前滚到最新版本)。
   迁移失败 → 应用起不来 → deploy.sh 的 420s 健康检查判本次部署失败并以非零码中止,
   报错看 /data/logs/apps/<SERVICE_NAME>/supervisord.log
4. 提交时迁移脚本与代码同一个 commit:结构要求与代码要求同源,任何一台机器拉这个 commit
   部署都能得到对得上的库
5. 数据订正(改数据不改结构)才走手工入口,且先只读核对影响行数再放开写:
   bash scripts/db-sql.sh -e test -r <user@host> -f fix.sql            # 先看
   bash scripts/db-sql.sh -e test -r <user@host> --apply -f fix.sql    # 再改
   # 回滚代码不等于回滚结构:Flyway 只前滚,要退结构就再提交一个 V 文件把它改回来

### 首次部署(dev 环境,远程服务器)

1. 把 dev 环境变量送到远程机器并填真实值:
   rsync src/backend/<SERVICE_NAME>/.env root@<HOST>:/opt/soft/apps/<SERVICE_NAME>/.env
   ssh root@<HOST> "vim /opt/soft/apps/<SERVICE_NAME>/.env"

2. 执行远程部署(本地构建 → rsync 上传 → 远程重启):
   bash scripts/deploy.sh --remote root@<HOST>

### 首次部署(test/prod 环境)

在对应机器上:
1. 确认域名已解析到这台机器
2. 填入该环境的真实凭证(键集与 dev 对齐,密钥单独生成、不复用 dev 的值):
   vim src/backend/<SERVICE_NAME>/.env.test     # 或 .env.prod
3. 申请 SSL 证书(80 端口需公网可达):
   bash scripts/apply-ssl.sh test               # 或 prod
4. 执行部署(--target ssl 会安装 nginx 主配置 + 该环境的 HTTPS 站点配置):
   bash scripts/deploy.sh --env test --remote root@<HOST>
   bash scripts/deploy.sh --target ssl --env test --remote root@<HOST>
5. 看到"降级使用 dev 配置(HTTP)"告警说明第 3 步的证书没到位,补签后重跑第 4 步
6. 该环境的库结构无需单独处理:第 4 步部署重启应用时 Flyway 已把它前滚到最新版本。
   只读核对一下版本(远端只认显式 -r,脚本不会替你连机器):
   echo 'select version,installed_on,success from flyway_schema_history order by installed_rank desc limit 3' \
       | bash scripts/db-sql.sh -e test -r <user@host> -s <SERVICE_NAME>
   首次接管一个已有表但没有 flyway_schema_history 的库:靠 application.yml 里的
   baseline-on-migrate: true + baseline-version: 0 补基线,别手工建那张表(列不对会让后续校验报错)

### 验证

curl -s http://127.0.0.1:<APP_PORT>/api/<SERVICE_NAME>/health          # 应用内
curl -s http://<本机IP>:<NGINX_PORT>/<SERVICE_NAME>/api/health         # 经 nginx

六、注意事项

  • 不要修改目标工程 specs/deployment-common.md 中的通用规范内容,只更新端口登记表。
  • 生成的文件如果目标路径已存在,先展示差异,询问用户是否覆盖,不要直接覆盖。
  • .env / .env.test / .env.prod 里的密码值保持占位符 changeme,不要填入任何真实凭证; application*.yml 里敏感项一律 ${VAR:} 空默认。
  • supervisord 配置不生成静态文件:deploy.sh 在部署时 inline 生成 /etc/supervisor/conf.d/<SERVICE_NAME>.<后缀>,无需在版本库中维护 supervisor 配置文件。 后缀由脚本现问目标主机 $SUPERVISOR_CONF 的 [include] files= 模式决定(apt 默认 *.conf, 不少在跑的主机改成只 *.ini):写错后缀的文件 supervisord 不加载、也不报错,属于 "改了配置不生效"那类静默故障;发现另一后缀同名文件会告警并给出删除命令。 重启前的存在性检查(require_supervisor_group())判 supervisorctl status 的输出文本,不判退出码 ——退出码是状态码(RUNNING=0/STOPPED|STARTING|BACKOFF=3/未登记=4),拿它判"存不存在"会把 "先 stop 再部署"的正常操作误判成"未登记";同时它也不放任静默:问不到 daemon、restart 与 start 双双失败,都会 fail 出带 [STATUS] 的结论行(见 specs/deployment-common.md 第三节第 5 条 与故障案例 3)。
  • Spring 环境(dev/test/prod)通过 env 文件中的 SPRING_PROFILES_ACTIVE 传递给 JVM, supervisord 命令行不写死 --spring.profiles.active。
  • 健康检查端点由生成的 RootController.java 提供,路径必须是 /api/<SERVICE_NAME>/health (deploy.sh 的服务表、nginx 站点配置、specs/deployment.md 三处都按它对接)。 不要改成 Actuator 默认 /actuator/health,那会让部署脚本探不到就绪状态。
  • 应用端口必须只绑 127.0.0.1(SERVER_ADDRESS 默认值),对外一律经 nginx。
  • 结构变更只有 Flyway 一条路径:迁移脚本放 src/main/resources/db/migration/,应用启动时 前滚,版本记账只看目标库的 flyway_schema_history。不要再引入第二套迁移脚本 —— 同一个库两个 记账来源时,"这条变更到底跑没跑"会有两个互相矛盾的答案。
  • Flyway 依赖必须由工程 pom 显式声明(org.flywaydb:flyway-core,PostgreSQL 还要 flyway-database-postgresql):本 skill 不生成 pom.xml,而 spring-boot-starter-data-jpa 不会带进 Flyway。缺依赖时 spring.flyway.* 整段配置被静默忽略、一次迁移都不会跑, 空库上表现为启动时 ddl-auto: validate 报"表不存在"。生成配置后要在工程 pom.xml 里确认这一项, 并把这条列进交付的人工待办(见第五节)。
  • db-sql.sh 不碰结构:它只做临时查询与数据订正。用它手工 ALTER 生产库会让库跑在 flyway_schema_history 记账之外,下次部署 Flyway 校验直接失败(或更糟:静默认为已是新版)。
  • 迁移失败就是部署失败:Flyway 报错 → 应用起不来 → deploy.sh 的 420s 健康检查超时中止。 这是把结构变更绑到部署这个受控动作上的目的,所以 prod 也自动迁移,不留"人记得去跑一次"的口子。
  • 不在本 skill 里登记目标机地址:连哪台机器永远由使用者显式给 -r <user@host>。 -e test|prod 只挑 env 文件,本机照它连会连到 dev 库(那两份 env 里的 127.0.0.1 指的是 被部署过去的那台机自己)—— 报告与 dev 一字不差,那是假绿。缺 -r 时 db-sql.sh 直接拒。
  • 回滚代码不等于回滚结构:Flyway 只前滚。要退结构就再提交一个 V 文件把它改回来, 所以迁移脚本里不要做不可逆的数据销毁。
  • 本 skill 的 deploy.sh/apply-ssl.sh 与 /new-deploy skill 是同一份副本, 改动本 skill 下这两个文件时必须同步 /new-deploy 那份并用 md5sum 校验一致。 db-sql.sh 只由本 skill 生成(/new-deploy 不含数据库层),不是共享副本。
  • 如果目标工程的 CLAUDE.md 已存在,在其中追加一条说明,指向 specs/deployment.md; 如果不存在,跳过(不自动创建 CLAUDE.md)。

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.