feat: 添加环境变量管理工具和部署配置改版
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m33s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m33s
主要改动: - 新增交互式环境配置脚本 (scripts/setup-env.sh) - 新增本地启动快捷脚本 (scripts/run-local.sh) - 新增环境变量模板文件 (.env.example) - 部署模式改版:使用嵌入式配置 + 环境变量覆盖 - 添加对象存储功能支持 - 改进 IoT 卡片导入任务 - 优化 OpenAPI 文档生成 - 删除旧的配置文件,改用嵌入式默认配置
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# bootstrap-init 规范
|
||||
|
||||
应用启动时的集中化初始化机制,确保所有必需目录在组件初始化前创建完成。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 集中化目录初始化
|
||||
|
||||
系统 SHALL 在应用启动时通过 `bootstrap.EnsureDirectories()` 函数统一创建所有必需的运行时目录。
|
||||
|
||||
目录列表:
|
||||
- 临时文件目录(从 `config.Storage.TempDir` 读取)
|
||||
- 应用日志目录(从 `config.Logging.AppLog.Filename` 提取目录部分)
|
||||
- 访问日志目录(从 `config.Logging.AccessLog.Filename` 提取目录部分)
|
||||
|
||||
#### Scenario: 成功创建所有目录
|
||||
|
||||
- **WHEN** 应用启动且所有目录路径可写
|
||||
- **THEN** 系统创建所有必需目录,权限为 0755
|
||||
- **AND** 函数返回 nil
|
||||
|
||||
#### Scenario: 目录已存在
|
||||
|
||||
- **WHEN** 应用启动且目录已存在
|
||||
- **THEN** 系统跳过创建,不报错
|
||||
- **AND** 函数返回 nil
|
||||
|
||||
#### Scenario: 配置路径为空
|
||||
|
||||
- **WHEN** 某个目录配置为空字符串
|
||||
- **THEN** 系统跳过该目录的创建
|
||||
- **AND** 不影响其他目录的创建
|
||||
|
||||
### Requirement: 权限降级策略
|
||||
|
||||
系统 SHALL 在目录创建权限不足时自动降级到系统临时目录。
|
||||
|
||||
#### Scenario: 权限不足时降级
|
||||
|
||||
- **WHEN** 创建目录因权限不足失败(os.IsPermission 为 true)
|
||||
- **THEN** 系统使用 `os.TempDir()/junhong/<原目录名>` 作为降级路径
|
||||
- **AND** 记录 WARN 级别日志,包含原路径和降级路径
|
||||
- **AND** 函数返回降级后的路径
|
||||
|
||||
#### Scenario: 非权限错误
|
||||
|
||||
- **WHEN** 创建目录失败且不是权限问题
|
||||
- **THEN** 系统返回错误,应用启动失败
|
||||
- **AND** 错误信息包含目录路径和原始错误
|
||||
|
||||
### Requirement: 初始化顺序
|
||||
|
||||
系统 SHALL 确保目录初始化在所有组件初始化之前完成。
|
||||
|
||||
#### Scenario: 正确的初始化顺序
|
||||
|
||||
- **WHEN** 应用启动
|
||||
- **THEN** 执行顺序为:
|
||||
1. config.Load() 加载配置
|
||||
2. bootstrap.EnsureDirectories() 创建目录
|
||||
3. logger.Init() 初始化日志
|
||||
4. 其他组件初始化
|
||||
|
||||
#### Scenario: 目录初始化失败
|
||||
|
||||
- **WHEN** `bootstrap.EnsureDirectories()` 返回错误
|
||||
- **THEN** 应用立即退出,不继续初始化其他组件
|
||||
- **AND** 错误信息输出到 stderr
|
||||
|
||||
### Requirement: 移除分散的目录创建逻辑
|
||||
|
||||
系统 SHALL 移除各组件中分散的目录创建代码。
|
||||
|
||||
#### Scenario: S3Provider 不再创建目录
|
||||
|
||||
- **WHEN** 初始化 S3Provider
|
||||
- **THEN** 不再调用 `os.MkdirAll` 创建临时目录
|
||||
- **AND** 假设目录已由 bootstrap 创建
|
||||
@@ -0,0 +1,133 @@
|
||||
# embedded-config 规范
|
||||
|
||||
配置嵌入机制,使用 go:embed 将默认配置嵌入二进制文件,通过环境变量覆盖。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 配置嵌入
|
||||
|
||||
系统 SHALL 使用 Go 的 `go:embed` 指令将默认配置文件嵌入二进制文件。
|
||||
|
||||
嵌入文件位置:`pkg/config/defaults/config.yaml`
|
||||
|
||||
#### Scenario: 加载嵌入配置
|
||||
|
||||
- **WHEN** 调用 `config.Load()`
|
||||
- **THEN** 系统从嵌入的 `defaults/config.yaml` 读取默认配置
|
||||
- **AND** 无需外部配置文件即可启动
|
||||
|
||||
#### Scenario: 嵌入配置包含完整结构
|
||||
|
||||
- **WHEN** 读取嵌入配置
|
||||
- **THEN** 配置包含所有配置节:server、database、redis、storage、logging、queue、jwt、middleware
|
||||
|
||||
### Requirement: 环境变量覆盖
|
||||
|
||||
系统 SHALL 支持通过环境变量覆盖嵌入的默认配置值。
|
||||
|
||||
环境变量格式:`JUNHONG_{SECTION}_{KEY}`
|
||||
|
||||
#### Scenario: 环境变量覆盖配置
|
||||
|
||||
- **WHEN** 设置环境变量 `JUNHONG_DATABASE_HOST=myhost`
|
||||
- **THEN** `config.Database.Host` 的值为 "myhost"
|
||||
- **AND** 覆盖嵌入配置中的默认值
|
||||
|
||||
#### Scenario: 嵌套配置覆盖
|
||||
|
||||
- **WHEN** 设置环境变量 `JUNHONG_LOGGING_LEVEL=debug`
|
||||
- **THEN** `config.Logging.Level` 的值为 "debug"
|
||||
|
||||
#### Scenario: 未设置环境变量
|
||||
|
||||
- **WHEN** 未设置某个配置的环境变量
|
||||
- **THEN** 使用嵌入配置中的默认值
|
||||
|
||||
### Requirement: 配置优先级
|
||||
|
||||
系统 SHALL 按以下优先级应用配置(高到低):
|
||||
|
||||
1. 环境变量 (JUNHONG_*)
|
||||
2. 嵌入默认值 (go:embed)
|
||||
|
||||
#### Scenario: 优先级验证
|
||||
|
||||
- **WHEN** 嵌入配置中 `server.address` 为 ":3000"
|
||||
- **AND** 设置环境变量 `JUNHONG_SERVER_ADDRESS=:8080`
|
||||
- **THEN** 最终 `config.Server.Address` 为 ":8080"
|
||||
|
||||
### Requirement: 必填配置验证
|
||||
|
||||
系统 SHALL 在加载配置后验证必填配置项是否已设置。
|
||||
|
||||
必填配置项:
|
||||
- `database.host`
|
||||
- `database.user`
|
||||
- `database.password`
|
||||
- `database.dbname`
|
||||
- `redis.address`
|
||||
- `jwt.secret_key`
|
||||
|
||||
#### Scenario: 必填配置缺失
|
||||
|
||||
- **WHEN** 必填配置项为空且未通过环境变量设置
|
||||
- **THEN** `config.Load()` 返回错误
|
||||
- **AND** 错误信息明确指出缺失的配置项和对应的环境变量名
|
||||
|
||||
#### Scenario: 必填配置通过环境变量提供
|
||||
|
||||
- **WHEN** 所有必填配置通过环境变量设置
|
||||
- **THEN** `config.Load()` 成功返回配置
|
||||
|
||||
### Requirement: 删除外部配置文件支持
|
||||
|
||||
系统 SHALL 移除对外部配置文件的支持。
|
||||
|
||||
#### Scenario: 不读取 configs 目录
|
||||
|
||||
- **WHEN** 应用启动
|
||||
- **THEN** 不读取 `configs/*.yaml` 文件
|
||||
- **AND** 不依赖 `CONFIG_PATH` 或 `CONFIG_ENV` 环境变量
|
||||
|
||||
### Requirement: 删除配置热重载
|
||||
|
||||
系统 SHALL 移除配置热重载功能。
|
||||
|
||||
#### Scenario: 不监听配置文件变化
|
||||
|
||||
- **WHEN** 应用运行中
|
||||
- **THEN** 不使用 fsnotify 监听文件变化
|
||||
- **AND** 删除 `pkg/config/watcher.go`
|
||||
|
||||
#### Scenario: 配置变更需重启
|
||||
|
||||
- **WHEN** 需要更改配置
|
||||
- **THEN** 必须重启应用使新配置生效
|
||||
|
||||
### Requirement: 环境变量前缀
|
||||
|
||||
系统 SHALL 使用 `JUNHONG_` 作为环境变量前缀。
|
||||
|
||||
#### Scenario: 前缀隔离
|
||||
|
||||
- **WHEN** 存在环境变量 `DATABASE_HOST=other`
|
||||
- **AND** 存在环境变量 `JUNHONG_DATABASE_HOST=correct`
|
||||
- **THEN** `config.Database.Host` 为 "correct"
|
||||
- **AND** 忽略无前缀的 `DATABASE_HOST`
|
||||
|
||||
### Requirement: 敏感配置处理
|
||||
|
||||
系统 SHALL 确保敏感配置不嵌入二进制文件。
|
||||
|
||||
敏感配置项(嵌入值为空):
|
||||
- `database.password`
|
||||
- `redis.password`
|
||||
- `jwt.secret_key`
|
||||
- `storage.s3.access_key_id`
|
||||
- `storage.s3.secret_access_key`
|
||||
|
||||
#### Scenario: 敏感配置默认为空
|
||||
|
||||
- **WHEN** 读取嵌入配置
|
||||
- **THEN** 敏感配置项的值为空字符串
|
||||
- **AND** 必须通过环境变量提供实际值
|
||||
Reference in New Issue
Block a user