heirloomlogic/Tightlip

GitHub: heirloomlogic/Tightlip

Tightlip是一个SwiftPM插件,用于在构建时从环境变量生成类型化的Swift枚举,以保护敏感信息。

Stars: 0 | Forks: 0

Tightlip

# 紧闭 一个 SwiftPM 构建工具插件,在构建时从环境变量生成一个类型化的 Swift `Secrets` 枚举。生成的文件位于插件的工 作目录中,并编译到消费目标中。秘密永远不会进入源代码控制。 [![Swift 6.1](https://img.shields.io/badge/Swift-6.1-orange.svg)](https://swift.org) [![平台](https://img.shields.io/badge/Platform-macOS%20|%20iOS%20|%20tvOS%20|%20watchOS-blue.svg)](https://swift.org) [![许可证](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) [![文档](https://img.shields.io/badge/Documentation-DocC-blue.svg)](https://heirloomlogic.github.io/Tightlip/documentation/tightlipcore/) ## 安装 ### Swift 包管理器 ``` // Package.swift .package(url: "https://github.com/heirloomlogic/Tightlip", from: "1.0.0"), ``` 将插件附加到目标: ``` .target( name: "MyApp", plugins: [.plugin(name: "Lipservice", package: "Tightlip")] ) ``` 将 `Secrets.yml` 放在目标源根目录下(例如 `Sources/MyApp/Secrets.yml`)。 ### Xcode 项目 1. `文件 > 添加包依赖...` → 粘贴 `https://github.com/heirloomlogic/Tightlip` → 将依赖规则设置为从 `1.0.0` 开始的 **Next Major**。 (`添加本地...` 也适用于 vendored 检出。) 2. 在目标的 `构建阶段 > 运行构建工具插件` 中添加 **Lipservice**。 3. 在项目根目录(包含 `.xcodeproj` 的目录)中创建 `/Secrets.yml`。`` 是目标的 *显示名称*;插件在文件系统中解析此路径,而不是通过 Xcode 的组树,因此文件在项目导航器中的位置无关紧要。对于标准应用程序模板,这是项目顶部已存在的 `/` 文件夹。 4. 在目标中引用生成的枚举:`Secrets.revenueCatAPIKey`。 ## 代理技能 如果您使用 AI 编码助手驱动此设置,请安装 `tightlip-ref` 技能。它教会助手如何集成 Tightlip、添加或重命名秘密、配置按环境密钥以及调试 Lipservice 构建失败。 ``` # Claude 代码 gh skill install heirloomlogic/skills tightlip-ref --agent claude-code --force --scope user # Codex gh skill install heirloomlogic/skills tightlip-ref --agent codex --force --scope user ``` `--force` 会覆盖任何现有副本,因此请重新运行相同命令以更新到最新版本。`--scope user` 会在机器上的每个项目中安装一次技能。 ## 使用方法 Tightlip 读取单个配置文件 `Secrets.yml`,有两种格式之一。格式从第一个非注释行自动检测。 ### 平坦配置 ``` # secrets.yml revenueCatAPIKey: REVENUECAT_API_KEY hmacSigningKey: HMAC_KEY ``` 每行一个秘密:`: `。左侧成为 `Secrets` 上的静态属性;右侧命名一个在构建时解析的环境变量。 ### 分节配置(多环境) ``` # secrets.yml staging: revenueCatAPIKey: STAGING_REVENUECAT_API_KEY hmacSigningKey: STAGING_HMAC_KEY production: revenueCatAPIKey: PROD_REVENUECAT_API_KEY hmacSigningKey: PROD_HMAC_KEY ``` 每个顶级标识符后面跟一个冒号(没有值)是一个环境部分。部分内的行缩进正好为 2 个空格。所有部分必须声明相同的属性集。每个构建选择一个部分 - 查看 [环境选择](#environment-selection)。 ### 语法 解析器故意严格: - 属性名称和环境变量名称必须是裸 ASCII 标识符(`[A-Za-z_][A-Za-z0-9_]*`)。不支持引号。 - 行首的 `#` 是注释。值后面的内联注释不受支持。 - 空行是允许的。制表符不行 - 任何地方。 - 平坦模式:映射行上没有前导空白。 - 分节模式:部分标题在第 1 列,内容缩进正好为 2 个空格。 - 重复键、空文件以及任何其他不符合此语法的其他内容都是带有行号的解析错误。 每个声明的秘密在构建时都是必需的。如果环境变量未设置,构建将失败,并显示一条消息,指出缺少的变量。真正可选的值应在运行时从 `ProcessInfo` 读取,而不是在此声明。 ### 命名约定 在同一台机器上开发多个应用程序的开发者会看到像 `REVENUECAT_API_KEY` 这样的裸名称发生冲突。在每个环境变量前加上一个应用程序特定的标签 - `_`,使用尖叫蛇式大小写(例如 `ACME_REVENUECAT_API_KEY`)。插件不会强制执行此约定;该约定只是确保跨项目的配置不会相互冲突。 ## 环境选择 当使用分节配置时,构建工具按以下顺序选择一个部分: 1. **`TIGHTLIP_ENV`** — 如果设置,其值必须与部分名称完全匹配。最高优先级。 2. **自动推断** — 当存在两个部分且其中一个命名为 `prod` 或 `production` 时: - `CONFIGURATION=Release`(Xcode)→ `prod`/`production` 部分。 - 任何其他配置(包括 `Debug` 和未设置)→ 另一个部分。 3. **错误** — 如果没有规则解决(例如,没有 `TIGHTLIP_ENV` 的三个部分),构建将失败,并显示一条消息,列出可用的环境。 平坦配置没有环境概念,并忽略所有这些。 ### 推荐设置 - **本地开发**:将 `export TIGHTLIP_ENV=staging` 添加到 `~/.zshenv`,或者将其保留为未设置,让调试构建自动选择非生产部分。 - **CI 发布通道**:设置 `TIGHTLIP_ENV=production`,或者如果使用 Xcode,则依赖 `CONFIGURATION=Release`。 - **超过两个环境(qa、uat 等)**:始终显式设置 `TIGHTLIP_ENV`。 ## 生成输出 ``` // Auto-generated by Tightlip. Do not edit. // Regenerated on every build from environment variables. // Environment: staging import Foundation nonisolated enum Secrets { static let appAPIKey: String = Self.decode("4qO9...") static let appBaseURL: String = Self.decode("9F2c...") private static let salt: [UInt8] = [0x12, 0x34, /* ...32 bytes... */] private static func decode(_ encoded: String) -> String { /* XOR + base64 */ } } ``` 调用站点看到普通的 `String` (`Secrets.appAPIKey`)。存储的字节与从解析的值中确定性地导出的 32 字节盐进行 XOR 编码,因此相同的输入产生字节相同的输出,这避免了下游不必要的重新编译。明文文字永远不会出现在编译的二进制文件中;对 shipped `.app` 的 `strings` 不会显示它们。 属性按字母顺序发出。枚举始终命名为 `Secrets`。对于分节配置,只有 `// Environment:` 注释出现。 ## 环境变量来源 默认情况下,构建工具在干净的 zsh 子shell 中源 `~/.zshenv`,捕获生成的环境,并将其与 `ProcessInfo.processInfo.environment`(构建自己的环境)合并(构建的环境)。每个键的冲突解决有利于 `ProcessInfo`,因此 CI 运行器和 Xcode Scheme 环境变量覆盖 `.zshenv` 中的任何内容。 无论构建如何启动,这种行为都是相同的 — 从 Finder、Conductor、VS Code 或终端中的 `xcodebuild` 启动。这消除了常见的 env 变量在 shell 中工作但在 Xcode 中看不到的情况。 如果配置的文件不存在(在 CI 中很典型),则工具回退到仅使用 `ProcessInfo`。源失败和超时(默认 5 秒)也回退,并在 stderr 中显示一条单条注释。 ### 覆盖源文件 在任何秘密声明之前添加一个顶级 `envFile:` 指令。路径相对于 `$HOME` 进行展开;相对路径相对于配置的目录解析。 ``` envFile: ~/.bash_profile revenueCatAPIKey: REVENUECAT_API_KEY ``` | Shell | 推荐路径 | 备注 | |---|---|---| | zsh | `~/.zshenv` *(默认 — 指令可以省略)* | 使用 `zsh -f` 清洁源 | | bash | `~/.bash_profile` 或 `~/.bashrc` | 由 zsh 源;shell 兼容的 `export` 语法工作 | | fish | `~/.config/tightlip.env` *(辅助)* | Fish 语法与 zsh 不兼容;保留一个 `export KEY=value` 行的文件 | | nushell / xonsh / 等. | `~/.tightlip.env` *(辅助)* | 与 fish 相同的辅助模式 | CI 运行器通常没有 `.zshenv`;工具回退到仅使用 `ProcessInfo`,并且作业的 `env:` 块工作不变。 指令仅在第一个非空白、非注释行时被识别。任何在部分标题或属性映射之后的内容都被解析为秘密声明。 ### 项目本地环境文件 相对 `envFile:` 路径相对于 `Secrets.yml` 的目录解析,因此 `envFile: secrets.env` 指向目标内部的一个同级文件: ``` # Sources/MyApp/secrets.yml envFile: secrets.env revenueCatAPIKey: ACME_REVENUECAT_API_KEY ``` 该文件是 shell 源的,因此使用 `export KEY=value` 语法,并 **gitignore 它** — 只有 `Secrets.yml` 应该在源代码控制中。注意,项目本地文件必须在每个 git 工作树中重新创建;机器全局的 `~/.zshenv` 在所有工作树中以相同的方式源,这通常是 Conductor 下的期望。 Tightlip 故意没有自动发现的 `.env` 功能:上面的指令已经涵盖了项目本地文件,并且自动发现加上定制的 dotenv 解析器会添加意外的提交和值解析代码路径,而 shell 源避免了这些。 ## 故障排除 **`error: environment variable X must be set to generate Secrets.Y`** — 环境变量在源文件和 `ProcessInfo` 中都未设置。随附的 `note:` 行列出了所有具有相同前缀(例如 `*`)的内容,这通常指向一个错误。确认键存在于您的 `envFile`(默认 `~/.zshenv`)中。 **`note: sourcing /path/to/file ... using process environment only`** — 源您的 `envFile` 的子shell失败或超时。使用 `zsh -f -c 'source '` 重新生成。典型原因:`.zshenv` 中的工具(例如 `mise`、`asdf`)遇到沙盒阻止的内容,或 `.zshenv` 花费的时间超过 5 秒。 **`error: cannot determine environment: ...`** — 使用分节配置,但工具无法决定构建哪个部分。将 `TIGHTLIP_ENV` 设置为列出的名称之一。这发生在有超过两个部分或两个部分中没有一个命名为 `prod`/`production` 的情况下。 **`error: Secrets.yml:N: ...`** — 配置没有解析。将行与 [语法](#grammar) 规则进行比较。常见原因:制表符(从不同编辑器粘贴),引号值,嵌套缩进,值后面的内联注释。 **插件在更改环境变量后不会重新生成** — 确认您正在查看正确的目标。插件在配置文件或环境变量值在构建之间更改时重新生成。如果它看起来卡住了,请运行 `xcodebuild clean` 以强制生成新的。 **生成的枚举在代码中不可见** — 确认插件已附加到目标(在 Xcode 中,`构建阶段 > 运行构建工具插件`),并且 `Secrets.yml` 在预期的路径上。
标签:AI Coding Assistant, Build Time, Build Tool, Code Generation, Development Tools, Documentation, Environment Variables, iOS, MacOS, MIT License, Plugin, Programming, Secrets Management, Security, Software Development, Source Control, Swift, Swift 6.1, Swift Package Manager, SwiftPM, tvOS, Type Safety, watchOS, Xcode, 凭据导出