省略 --name 时按 namespace(+ 可选 --kind)批量删除所有匹配记录; 支持 --dry-run 预览;删除前自动快照历史并写入审计日志。 移除独立的 delete-ns 子命令,合并为统一的 delete 入口。 更新 AGENTS.md 文档,版本 bump 至 0.9.3。 Made-with: Cursor
26 KiB
Secrets CLI — AGENTS.md
提交 / 发版硬规则(优先于下文其他说明)
- 涉及
src/**、Cargo.toml、Cargo.lock、CLI 行为变更的提交,默认视为需要发版,除非用户明确说明“本次不发版”。 - 发版前必须先检查
Cargo.toml中的version,再检查是否已存在对应 tag:git tag -l 'secrets-*'。 - 若当前版本对应 tag 已存在,必须先 bump
Cargo.toml的version,再执行cargo build同步Cargo.lock,然后才能提交。 - 提交前优先运行
./scripts/release-check.sh;该脚本会检查重复版本并执行cargo fmt -- --check && cargo clippy --locked -- -D warnings && cargo test --locked。
跨设备密钥与配置管理 CLI 工具,将服务器信息、服务凭据等存储到 PostgreSQL 18,供 AI 工具读取上下文。每个加密字段单独行存储(secrets 子表),字段名、类型、长度以明文保存,主密钥由 Argon2id 从主密码派生并存入平台安全存储(macOS Keychain / Windows Credential Manager / Linux keyutils)。
项目结构
secrets/
src/
main.rs # CLI 入口,clap 命令定义,auto-migrate,--verbose 全局参数
output.rs # OutputMode 枚举 + TTY 检测(TTY→text,非 TTY→json-compact)
config.rs # 配置读写:~/.config/secrets/config.toml(database_url)
db.rs # PgPool 创建 + 建表/索引(DROP+CREATE,含所有表)
crypto.rs # AES-256-GCM 加解密、Argon2id 派生、OS 钥匙串
models.rs # Entry + SecretField 结构体(sqlx::FromRow + serde)
audit.rs # 审计写入:log_tx(事务内)
commands/
init.rs # init 命令:主密钥初始化(每台设备一次)
add.rs # add 命令:upsert entries + 逐字段写入 secrets,含历史快照
config.rs # config 命令:set-db / show / path(持久化 database_url)
search.rs # search 命令:多条件查询,展示 secrets 字段 schema(无需 master_key)
delete.rs # delete 命令:事务化,CASCADE 删除 secrets,含历史快照
update.rs # update 命令:增量更新,secrets 行级 UPSERT/DELETE,CAS 并发保护
rollback.rs # rollback 命令:按 entry_version 恢复 entry + secrets
history.rs # history 命令:查看 entry 变更历史列表
run.rs # inject / run 命令:逐字段解密 + key_ref 引用解析
upgrade.rs # upgrade 命令:检查、校验摘要并下载最新版本,自动替换二进制
export_cmd.rs # export 命令:批量导出记录,支持 JSON/TOML/YAML,含解密明文
import_cmd.rs # import 命令:批量导入记录,冲突检测,dry-run,重新加密写入
scripts/
release-check.sh # 发版前检查版本号/tag 是否重复,并执行 fmt/clippy/test
setup-gitea-actions.sh # 配置 Gitea Actions 变量与 Secrets
.gitea/workflows/
secrets.yml # CI:fmt + clippy + musl 构建 + Release 上传 + 飞书通知
.vscode/tasks.json # 本地测试任务(build / config / search / add+delete / update / audit 等)
数据库
- Host:
<host>:<port> - Database:
secrets - 连接串:
postgres://postgres:<password>@<host>:<port>/secrets - 表:
entries(主表)+secrets(加密字段子表)+entries_history+secrets_history+audit_log+kv_config,首次连接自动建表(auto-migrate)
表结构
entries (
id UUID PRIMARY KEY DEFAULT uuidv7(), -- PG18 时间有序 UUID
namespace VARCHAR(64) NOT NULL, -- 一级隔离: "refining" | "ricnsmart"
kind VARCHAR(64) NOT NULL, -- 类型: "server" | "service" | "key"(可扩展)
name VARCHAR(256) NOT NULL, -- 人类可读标识
tags TEXT[] NOT NULL DEFAULT '{}', -- 灵活标签: ["aliyun","hongkong"]
metadata JSONB NOT NULL DEFAULT '{}', -- 明文描述: ip, desc, domains, location...
version BIGINT NOT NULL DEFAULT 1, -- 乐观锁版本号,每次写操作自增
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(namespace, kind, name)
)
secrets (
id UUID PRIMARY KEY DEFAULT uuidv7(),
entry_id UUID NOT NULL REFERENCES entries(id) ON DELETE CASCADE,
field_name VARCHAR(256) NOT NULL, -- 明文字段名: "username", "token", "ssh_key"
field_type VARCHAR(32) NOT NULL DEFAULT 'string', -- 明文类型: "string"|"number"|"boolean"|"json"
value_len INT NOT NULL DEFAULT 0, -- 明文原始值字符数(PEM≈4096,token≈40)
encrypted BYTEA NOT NULL DEFAULT '\x', -- 仅加密值本身:nonce(12B)||ciphertext+tag
version BIGINT NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(entry_id, field_name)
)
kv_config (
key TEXT PRIMARY KEY, -- 如 'argon2_salt'
value BYTEA NOT NULL -- Argon2id salt,首台设备 init 时生成
)
audit_log 表结构
audit_log (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
action VARCHAR(32) NOT NULL, -- 'add' | 'update' | 'delete' | 'rollback'
namespace VARCHAR(64) NOT NULL,
kind VARCHAR(64) NOT NULL,
name VARCHAR(256) NOT NULL,
detail JSONB NOT NULL DEFAULT '{}', -- 变更摘要(tags/meta keys/secret keys,不含 value)
actor VARCHAR(128) NOT NULL DEFAULT '', -- 操作者($USER 环境变量)
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
entries_history 表结构
entries_history (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
entry_id UUID NOT NULL,
namespace VARCHAR(64) NOT NULL,
kind VARCHAR(64) NOT NULL,
name VARCHAR(256) NOT NULL,
version BIGINT NOT NULL, -- 被快照时的版本号
action VARCHAR(16) NOT NULL, -- 'add' | 'update' | 'delete' | 'rollback'
tags TEXT[] NOT NULL DEFAULT '{}',
metadata JSONB NOT NULL DEFAULT '{}',
actor VARCHAR(128) NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
secrets_history 表结构
secrets_history (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
entry_id UUID NOT NULL,
secret_id UUID NOT NULL, -- 对应 secrets.id
entry_version BIGINT NOT NULL, -- 关联 entries_history 的版本号
field_name VARCHAR(256) NOT NULL,
field_type VARCHAR(32) NOT NULL DEFAULT 'string',
value_len INT NOT NULL DEFAULT 0,
encrypted BYTEA NOT NULL DEFAULT '\x',
action VARCHAR(16) NOT NULL, -- 'add' | 'update' | 'delete' | 'rollback'
actor VARCHAR(128) NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
字段职责划分
| 字段 | 存什么 | 示例 |
|---|---|---|
namespace |
项目/团队隔离 | refining, ricnsmart |
kind |
记录类型 | server, service, key |
name |
唯一标识名 | i-example0abcd1234efgh, gitea |
tags |
多维分类标签 | ["aliyun","hongkong","ricn"] |
metadata |
明文非敏感信息 | {"ip":"192.0.2.1","desc":"Grafana","key_ref":"my-shared-key"} |
secrets.field_name |
加密字段名(明文) | "username", "token", "ssh_key" |
secrets.field_type |
值类型(明文) | "string", "number", "boolean", "json" |
secrets.value_len |
原始值字符数(明文) | 4(root),40(token),4096(PEM) |
secrets.encrypted |
仅加密值本身 | AES-256-GCM 密文 |
PEM 共享机制(key_ref)
同一 PEM 被多台服务器共享时,将 PEM 存为独立的 kind=key 记录,服务器通过 metadata.key_ref 引用:
# 1. 存共享 PEM
secrets add -n refining --kind key --name my-shared-key \
--tag aliyun --tag hongkong \
-s content=@./keys/my-shared-key.pem
# 2. 服务器通过 metadata.key_ref 引用(inject/run 时自动合并 key 的 secrets)
secrets add -n refining --kind server --name i-example0xyz789 \
-m ip=192.0.2.1 -m key_ref=my-shared-key \
-s username=ecs-user
# 3. 轮换只需更新 key 记录,所有引用服务器自动生效
secrets update -n refining --kind key --name my-shared-key \
-s content=@./keys/new-key.pem
数据库配置
首次使用需显式配置数据库连接,设置一次后在该设备上持久生效:
secrets config set-db "postgres://postgres:<password>@<host>:<port>/secrets"
secrets config show # 查看当前配置(密码脱敏)
secrets config path # 打印配置文件路径
set-db 会先验证连接可用,成功后才写入配置文件;连接失败时提示 "Database connection failed" 且不修改配置。
配置文件:~/.config/secrets/config.toml,权限 0600。--db-url 参数可一次性覆盖。
主密钥与加密
首次使用(每台设备各执行一次):
secrets config set-db "postgres://postgres:<password>@<host>:<port>/secrets"
secrets init # 提示输入主密码,Argon2id 派生主密钥后存入 OS 钥匙串
主密码不存储;salt 存于 kv_config,首台设备生成后共享,确保同一主密码在所有设备派生出相同主密钥。
主密钥存储后端:macOS Keychain、Windows Credential Manager、Linux keyutils(会话级,重启后需再次 secrets init)。
从旧版(明文 JSONB)升级:升级后执行 secrets init 即可(明文记录需手动重新 add 或通过 update 更新)。
CLI 命令
AI 使用主路径
读取一律用 search,写入用 add / update,避免反复查帮助。
输出格式规则:
- TTY(终端直接运行)→ 默认
text - 非 TTY(管道/重定向/AI 调用)→ 自动
json-compact - 显式
-o json→ 美化 JSON
init — 主密钥初始化(每台设备一次)
# 首次设备:生成 Argon2id salt 并存库,派生主密钥后存 OS 钥匙串
secrets init
# 后续设备:复用已有 salt,派生主密钥后存钥匙串(主密码需与首台相同)
secrets init
search — 发现与读取
# 参数说明(带典型值)
# -n / --namespace refining | ricnsmart
# --kind server | service
# --name gitea | i-example0abcd1234efgh | mqtt
# --tag aliyun | hongkong | production
# -q / --query mqtt | grafana | gitea (模糊匹配 name/namespace/kind/tags/metadata)
# secrets schema search 默认展示 secrets 字段名、类型与长度(无需 master_key)
# -f / --field metadata.ip | metadata.url | metadata.default_org
# --summary 不带值的 flag,仅返回摘要(name/tags/desc/updated_at)
# --limit 20 | 50(默认 50)
# --offset 0 | 10 | 20(分页偏移)
# --sort name(默认)| updated | created
# -o / --output text | json | json-compact
# 发现概览(起步推荐)
secrets search --summary --limit 20
secrets search -n refining --summary --limit 20
secrets search --sort updated --limit 10 --summary
# 精确定位单条记录
secrets search -n refining --kind service --name gitea
secrets search -n refining --kind server --name i-example0abcd1234efgh
# 精确定位并获取完整内容(secrets 保持加密占位)
secrets search -n refining --kind service --name gitea -o json
# 直接提取 metadata 字段值(最短路径)
secrets search -n refining --kind service --name gitea -f metadata.url
secrets search -n refining --kind service --name gitea \
-f metadata.url -f metadata.default_org
# 需要 secrets 时,改用 inject / run
secrets inject -n refining --kind service --name gitea
secrets run -n refining --kind service --name gitea -- printenv
# 模糊关键词搜索
secrets search -q mqtt
secrets search -q grafana
secrets search -q 192.0.2
# 按条件过滤
secrets search -n refining --kind service
secrets search -n ricnsmart --kind server
secrets search --tag hongkong
secrets search --tag aliyun --summary
# 分页
secrets search -n refining --summary --limit 10 --offset 0
secrets search -n refining --summary --limit 10 --offset 10
# 管道 / AI 调用(非 TTY 自动 json-compact)
secrets search -n refining --kind service | jq '.[].name'
add — 新增或全量覆盖(upsert)
# 参数说明(带典型值)
# -n / --namespace refining | ricnsmart
# --kind server | service
# --name gitea | i-example0abcd1234efgh
# --tag aliyun | hongkong(可重复)
# -m / --meta ip=10.0.0.1 | desc="ECS" | url=https://... | tls:cert@./cert.pem(可重复)
# -s / --secret token=<value> | ssh_key=@./key.pem | password=secret123 | credentials:content@./key.pem(可重复)
# 添加服务器
secrets add -n refining --kind server --name i-example0abcd1234efgh \
--tag aliyun --tag shanghai \
-m ip=10.0.0.1 -m desc="Aliyun Shanghai ECS" \
-s username=root -s ssh_key=@./keys/deploy-key.pem
# 添加服务凭据
secrets add -n refining --kind service --name gitea \
--tag gitea \
-m url=https://code.example.com -m default_org=refining -m username=voson \
-s token=<token> -s runner_token=<runner_token>
# 从文件读取 token
secrets add -n ricnsmart --kind service --name mqtt \
-m host=mqtt.example.com -m port=1883 \
-s password=@./mqtt_password.txt
# 多行文件直接写入嵌套 secret 字段
secrets add -n refining --kind server --name i-example0abcd1234efgh \
-s credentials:content@./keys/deploy-key.pem
# 使用类型化值(key:=<json>)存储非字符串类型
secrets add -n refining --kind service --name prometheus \
-m scrape_interval:=15 \
-m enabled:=true \
-m labels:='["prod","metrics"]' \
-s api_key=abc123
update — 增量更新(记录必须已存在)
只有传入的字段才会变动,其余全部保留。
# 参数说明(带典型值)
# -n / --namespace refining | ricnsmart
# --kind server | service
# --name gitea | i-example0abcd1234efgh
# --add-tag production | backup(不影响已有 tag,可重复)
# --remove-tag staging | deprecated(可重复)
# -m / --meta ip=10.0.0.1 | desc="新描述" | credentials:username=root(新增或覆盖,可重复)
# --remove-meta old_port | legacy_key | credentials:content(删除 metadata 字段,可重复)
# -s / --secret token=<new> | ssh_key=@./new.pem | credentials:content@./new.pem(新增或覆盖,可重复)
# --remove-secret old_password | deprecated_key | credentials:content(删除 secret 字段,可重复)
# 更新单个 metadata 字段
secrets update -n refining --kind server --name i-example0abcd1234efgh \
-m ip=10.0.0.1
# 轮换 token
secrets update -n refining --kind service --name gitea \
-s token=<new-token>
# 新增 tag 并轮换 token
secrets update -n refining --kind service --name gitea \
--add-tag production \
-s token=<new-token>
# 移除废弃字段
secrets update -n refining --kind service --name mqtt \
--remove-meta old_port --remove-secret old_password
# 从文件更新嵌套 secret 字段
secrets update -n refining --kind server --name i-example0abcd1234efgh \
-s credentials:content@./keys/deploy-key.pem
# 删除嵌套字段
secrets update -n refining --kind server --name i-example0abcd1234efgh \
--remove-secret credentials:content
# 移除 tag
secrets update -n refining --kind service --name gitea --remove-tag staging
delete — 删除记录(支持单条精确删除与批量删除)
删除时会自动将 entry 与所有关联 secret 字段快照到历史表,并写入审计日志,可通过 rollback 命令恢复。
# 参数说明(带典型值)
# -n / --namespace refining | ricnsmart(必填)
# --kind server | service(指定 --name 时必填;批量时可选)
# --name gitea | i-example0abcd1234efgh(精确匹配;省略则批量删除)
# --dry-run 预览将删除的记录,不实际写入(仅批量模式有效)
# -o / --output text | json | json-compact
# 精确删除单条记录(--kind 必填)
secrets delete -n refining --kind service --name legacy-mqtt
secrets delete -n ricnsmart --kind server --name i-old-server-id
# 预览批量删除(不写入数据库)
secrets delete -n refining --dry-run
secrets delete -n ricnsmart --kind server --dry-run
# 批量删除整个 namespace 的所有记录
secrets delete -n ricnsmart
# 批量删除 namespace 下指定 kind 的所有记录
secrets delete -n ricnsmart --kind server
# JSON 输出
secrets delete -n refining --kind service -o json
history — 查看变更历史
# 参数说明
# -n / --namespace refining | ricnsmart
# --kind server | service
# --name 记录名
# --limit 返回条数(默认 20)
# 查看某条记录的历史版本列表
secrets history -n refining --kind service --name gitea
# 查最近 5 条
secrets history -n refining --kind service --name gitea --limit 5
# JSON 输出
secrets history -n refining --kind service --name gitea -o json
rollback — 回滚到指定版本
# 参数说明
# -n / --namespace refining | ricnsmart
# --kind server | service
# --name 记录名
# --to-version <N> 目标版本号(省略则恢复最近一次快照)
# 撤销上次修改(回滚到最近一次快照)
secrets rollback -n refining --kind service --name gitea
# 回滚到版本 3
secrets rollback -n refining --kind service --name gitea --to-version 3
inject — 输出临时环境变量
敏感值仅打印到 stdout,不持久化、不写入当前 shell。
# 参数说明
# -n / --namespace refining | ricnsmart
# --kind server | service
# --name 记录名
# --tag 按 tag 过滤(可重复)
# --prefix 变量名前缀(留空则以记录 name 作前缀)
# -o / --output text(默认 KEY=VALUE)| json | json-compact
# 打印单条记录的所有变量(KEY=VALUE 格式)
secrets inject -n refining --kind service --name gitea
# 自定义前缀
secrets inject -n refining --kind service --name gitea --prefix GITEA
# JSON 格式(适合管道或脚本解析)
secrets inject -n refining --kind service --name gitea -o json
# eval 注入当前 shell(谨慎使用)
eval $(secrets inject -n refining --kind service --name gitea)
run — 向子进程注入 secrets 并执行命令
secrets 仅作用于子进程环境,不修改当前 shell,进程退出码透传。
# 参数说明
# -n / --namespace refining | ricnsmart
# --kind server | service
# --name 记录名
# --tag 按 tag 过滤(可重复)
# --prefix 变量名前缀
# -- <command> 执行的命令及参数
# 向脚本注入单条记录的 secrets
secrets run -n refining --kind service --name gitea -- ./deploy.sh
# 按 tag 批量注入(多条记录合并)
secrets run --tag production -- env | grep -i token
# 验证注入了哪些变量
secrets run -n refining --kind service --name gitea -- printenv
upgrade — 自动更新 CLI 二进制
从 Release 服务器下载最新版本,校验对应 .sha256 摘要后替换当前二进制,无需数据库连接或主密钥。
配置方式:SECRETS_UPGRADE_URL 必填。优先用构建时:SECRETS_UPGRADE_URL=https://... cargo build,CI 已自动注入。或运行时:写在 .env 或 export 后执行。
# 检查是否有新版本(不下载)
secrets upgrade --check
# 下载、校验 SHA-256 并安装最新版本
secrets upgrade
export — 批量导出记录
将匹配的记录(含解密后的明文 secrets)导出到文件或 stdout。支持 JSON、TOML、YAML 三种格式,文件格式由扩展名自动推断。使用 --no-secrets 时无需主密钥。
# 参数说明
# -n / --namespace refining | ricnsmart
# --kind server | service
# --name gitea | i-example0abcd1234efgh
# --tag aliyun | production(可重复)
# -q / --query 模糊关键词
# --file <path> 输出文件路径,格式由扩展名推断(.json / .toml / .yaml / .yml)
# --format json | toml | yaml 显式指定格式(输出到 stdout 时必须指定)
# --no-secrets 不导出 secrets,无需主密钥
# 全量导出到 JSON 文件
secrets export --file backup.json
# 按 namespace 导出为 TOML
secrets export -n refining --file refining.toml
# 按 kind 导出为 YAML
secrets export -n refining --kind service --file services.yaml
# 按 tag 过滤导出
secrets export --tag production --file prod.json
# 模糊关键词导出
secrets export -q mqtt --file mqtt.json
# 仅导出 schema(不含 secrets,无需主密钥)
secrets export --no-secrets --file schema.json
# 输出到 stdout(必须指定 --format)
secrets export -n refining --format yaml
secrets export --format json | jq '.'
import — 批量导入记录
从导出文件读取记录并写入数据库,自动重新加密 secrets。支持 JSON、TOML、YAML 三种格式,文件格式由扩展名自动推断。
# 参数说明
# <file> 必选,输入文件路径(格式由扩展名推断)
# --force 冲突时覆盖已有记录(默认:报错并停止)
# --dry-run 预览将执行的操作,不写入数据库
# -o / --output text | json | json-compact
# 导入 JSON 文件(遇到已存在记录报错)
secrets import backup.json
# 导入 TOML 文件,冲突时覆盖
secrets import --force refining.toml
# 导入 YAML 文件,冲突时覆盖
secrets import --force services.yaml
# 预览将执行的操作(不写入)
secrets import --dry-run backup.json
# JSON 格式输出导入摘要
secrets import backup.json -o json
config — 配置管理(无需主密钥)
# 设置数据库连接(每台设备执行一次,之后永久生效;先验证连接可用再写入)
secrets config set-db "postgres://postgres:<password>@<host>:<port>/secrets"
# 查看当前配置(密码脱敏)
secrets config show
# 打印配置文件路径
secrets config path
# 输出: /Users/<user>/.config/secrets/config.toml
全局参数
# debug 日志(位于子命令之前)
secrets --verbose search -q mqtt
secrets -v add -n refining --kind service --name gitea -m url=xxx -s token=yyy
# 或通过环境变量精细控制
RUST_LOG=secrets=trace secrets search
# 一次性覆盖数据库连接
secrets --db-url "postgres://..." search -n refining
代码规范
- 错误处理:统一使用
anyhow::Result,不用unwrap() - 异步:全程
tokio,数据库操作sqlxasync - SQL:使用
sqlx::query/sqlx::query_as绑定参数,禁止字符串拼接(搜索的动态 WHERE 子句除外,需使用参数绑定$1/$2) - 新增
kind类型时:只需在add调用时传入,无需改代码 - 字段命名:CLI 短标志
-n=namespace,-m=meta,-s=secret,-q=query,-v=verbose,-f=field,-o=output - 日志:用户可见输出用
println!;调试/运维信息用tracing::debug!/info!/warn!/error! - 审计:
add/update/delete成功后调用audit::log_tx,写入audit_log表;失败只 warn 不中断 - 加密:
encrypted列存储 AES-256-GCM 密文;add/update/search/delete需主密钥(secrets init后从 OS 钥匙串加载) - 输出:读命令通过
OutputMode支持 text/json/json-compact/env;写命令add同样支持-o json
提交前检查(必须全部通过)
每次提交代码前,请在本地依次执行以下检查,全部通过后再 push:
优先使用:
./scripts/release-check.sh
它等价于先检查版本号 / tag,再执行下面的格式、Lint、测试。
1. 版本号(按需)
若本次改动需要发版,请先确认 Cargo.toml 中的 version 已提升,避免 CI 打出的 Tag 与已有版本重复。升级版本后需同时更新 Cargo.lock(运行 cargo build 即可自动同步),否则 CI 中 cargo clippy --locked 会因 lock 与 manifest 不一致而失败。可通过 git tag 判断:
# 查看当前 Cargo.toml 版本
grep '^version' Cargo.toml
# 查看是否已存在该版本对应的 tag(CI 使用格式 secrets-<version>)
git tag -l 'secrets-*'
若当前版本已被 tag(例如已有 secrets-0.3.0 且 Cargo.toml 仍为 0.3.0),则应在 Cargo.toml 中 bump 版本号,再执行 cargo build 同步 Cargo.lock,最后一并提交,以便 CI 自动打新 Tag 并发布 Release。
2. 格式、Lint、测试
cargo fmt -- --check # 格式检查(不通过则运行 cargo fmt 修复)
cargo clippy -- -D warnings # Lint 检查(消除所有 warning)
cargo test # 单元/集成测试
或一次性执行:
cargo fmt -- --check && cargo clippy -- -D warnings && cargo test
CI/CD
- Gitea Actions(runners: debian / darwin-arm64 / windows)
- 触发:
src/**、Cargo.toml、Cargo.lock变更推送到 main - 构建目标:
x86_64-unknown-linux-musl、aarch64-apple-darwin、x86_64-apple-darwin(由 ARM mac runner 交叉编译)、x86_64-pc-windows-msvc - 新版本自动打 Tag(格式
secrets-<version>)并上传二进制与对应.sha256摘要到 Gitea Release - Release 仅在 Linux/macOS/Windows 构建全部成功后才会从 draft 发布
- 通知:飞书 Webhook(
vars.WEBHOOK_URL) - 所需 secrets/vars:
RELEASE_TOKEN(Release 上传,Gitea PAT)、vars.WEBHOOK_URL(通知,可选) - 注意:Gitea Actions 的 Secret/Variable 创建时,
data/value字段需传入原始值,不要使用 base64 编码
环境变量
| 变量 | 说明 |
|---|---|
RUST_LOG |
日志级别,如 secrets=debug、secrets=trace(默认 warn) |
USER |
审计日志 actor 字段来源,Shell 自动设置,通常无需手动配置 |
SECRETS_UPGRADE_URL |
upgrade 的 Release API 地址。构建时(cargo build)或运行时(.env/export) |
数据库连接通过 secrets config set-db 持久化到 ~/.config/secrets/config.toml,不支持环境变量。