Files
secrets/AGENTS.md
voson 3a5ec92bf0
Some checks failed
Secrets CLI - Build & Release / 版本 & Release (push) Successful in 3s
Secrets CLI - Build & Release / 质量检查 (fmt / clippy / test) (push) Successful in 2m36s
Secrets CLI - Build & Release / Build (macOS aarch64 + x86_64) (push) Successful in 1m3s
Secrets CLI - Build & Release / Build (x86_64-unknown-linux-musl) (push) Successful in 1m15s
Secrets CLI - Build & Release / 发布草稿 Release (push) Has been cancelled
Secrets CLI - Build & Release / Build (x86_64-pc-windows-msvc) (push) Has been cancelled
fix: inject/run 仅注入 secrets 字段,不含 metadata
- build_injected_env_map 不再合并 metadata
- 删除 build_metadata_env_map 及其测试
- 更新 README、AGENTS.md 文档
- bump 版本至 0.9.5

Made-with: Cursor
2026-03-19 17:03:01 +08:00

26 KiB
Raw Permalink Blame History

Secrets CLI — AGENTS.md

提交 / 发版硬规则(优先于下文其他说明)

  1. 涉及 src/**Cargo.tomlCargo.lock、CLI 行为变更的提交,默认视为需要发版,除非用户明确说明“本次不发版”。
  2. 发版前必须先检查 Cargo.toml 中的 version,再检查是否已存在对应 taggit tag -l 'secrets-*'
  3. 若当前版本对应 tag 已存在,必须先 bump Cargo.tomlversion,再执行 cargo build 同步 Cargo.lock,然后才能提交。
  4. 提交前优先运行 ./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.tomldatabase_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/DELETECAS 并发保护
      rollback.rs        # rollback 命令:按 entry_version 恢复 entry + secrets
      history.rs         # history 命令:查看 entry 变更历史列表
      run.rs             # inject / run 命令:仅 secrets 逐字段解密 + key_ref 引用解析(不含 metadata
      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          # CIfmt + 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"
  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,
  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.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 — 输出临时环境变量

仅注入 secrets 表中的加密字段(解密后),不含 metadata。敏感值仅打印到 stdout不持久化、不写入当前 shell。

# 参数说明
#   -n / --namespace   refining | ricnsmart
#   --kind             server | service
#   --name             记录名
#   --tag              按 tag 过滤(可重复)
#   --prefix           变量名前缀(留空则以记录 name 作前缀)
#   -o / --output      text默认 KEY=VALUE| json | json-compact

# 打印单条记录的 secrets 变量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 表中的加密字段(解密后),不含 metadata。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 buildCI 已自动注入。或运行时:写在 .envexport 后执行。

# 检查是否有新版本(不下载)
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,数据库操作 sqlx async
  • 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

# 查看是否已存在该版本对应的 tagCI 使用格式 secrets-<version>
git tag -l 'secrets-*'

若当前版本已被 tag例如已有 secrets-0.3.0Cargo.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 Actionsrunners: debian / darwin-arm64 / windows
  • 触发:src/**Cargo.tomlCargo.lock 变更推送到 main
  • 构建目标:x86_64-unknown-linux-muslaarch64-apple-darwinx86_64-apple-darwin(由 ARM mac runner 交叉编译)、x86_64-pc-windows-msvc
  • 新版本自动打 Tag格式 secrets-<version>)并上传二进制与对应 .sha256 摘要到 Gitea Release
  • Release 仅在 Linux/macOS/Windows 构建全部成功后才会从 draft 发布
  • 通知:飞书 Webhookvars.WEBHOOK_URL
  • 所需 secrets/varsRELEASE_TOKENRelease 上传Gitea PATvars.WEBHOOK_URL(通知,可选)
  • 注意Gitea Actions 的 Secret/Variable 创建时,data/value 字段需传入原始值,不要使用 base64 编码

环境变量

变量 说明
RUST_LOG 日志级别,如 secrets=debugsecrets=trace(默认 warn
USER 审计日志 actor 字段来源Shell 自动设置,通常无需手动配置
SECRETS_UPGRADE_URL upgrade 的 Release API 地址。构建时cargo build或运行时.env/export

数据库连接通过 secrets config set-db 持久化到 ~/.config/secrets/config.toml,不支持环境变量。