lingora-cli 0.1.0rc1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,280 @@
1
+ # Lingora CLI 命令手册
2
+
3
+ 需要 Python 3.12+。以下示例假设已安装指定版本 `lingora-cli`;示例 UUID、域名和分支均为占位值,
4
+ 不包含真实凭据。所有 HTTP 操作都使用 Lingora 公开 API。
5
+
6
+ ## 1. 命令总览
7
+
8
+ | 命令 | 职责 | 副作用 |
9
+ | --- | --- | --- |
10
+ | `help [command ...]`、`--help` | 查看用法,例如 `help extract print` | 无 |
11
+ | `--version` | 查看安装包版本 | 无 |
12
+ | `init` | 创建无凭据的 `.lingorarc`,拒绝覆盖 | 新建本地文件 |
13
+ | `push` | 上传 Android 基础资源 | 写入服务端 |
14
+ | `pull` | 下载目标语言,校验后更新生成资源 | 写入本地生成目录 |
15
+ | `validate` | 校验已有生成资源 | 本地只读 |
16
+ | `extract print` | 从 Android XML 提取资源键及基础值 | 本地只读 |
17
+ | `extract check` | 校验 XML 结构、重复键等,返回键数量 | 本地只读 |
18
+ | `compare` | 对比本地与远端的键集合 | 远端只读 |
19
+ | `sync` | 补齐缺失键,可选移除未使用键 | 远端写入,需要 `--yes`;`--dry-run` 只读 |
20
+ | `tag` | 在明确范围内增加或移除 Key 标签 | 远端写入,需要 `--yes`;`--dry-run` 只读 |
21
+ | `branch` | 列出、创建或删除分支 | 创建会写入;删除需要 `--yes` |
22
+ | `merge` | 创建合并预览,或应用无冲突的合并 | 预览也会建立服务端记录;应用需要 `--apply --yes` |
23
+ | `delivery check` | 检查公开 CDN 清单、资源类型和缓存头 | 公开 HTTP GET |
24
+
25
+ 不提供项目级 `mock-ci` 命令。消费项目自行编排 `lingora push && lingora pull`。
26
+ 正常 Android Gradle 构建不执行这些命令。
27
+
28
+ 当前边界:资源格式仅支持 `ANDROID_XML`,同次处理一个基础语言和一个 namespace,可有多个 XML 文件。
29
+ `extract` 读取 XML 资源,不扫描 Kotlin、React、Vue、Svelte 或 Angular 源码;不提供自定义执行器、
30
+ 持续 watch、浏览器登录或本机凭据存储。`sync` 是 Android 资源键同步,不是上传后再下载。
31
+ 这些未实现能力不会注册为空壳命令。
32
+
33
+ ## 2. 通用参数与优先级
34
+
35
+ 通用参数可放在命令前后,嵌套命令同样适用,例如 `lingora --json branch` 和
36
+ `lingora delivery check --alias current --json`。
37
+
38
+ | 参数 | 用途 |
39
+ | --- | --- |
40
+ | `--config PATH` | 读取指定 JSON;明确指定但不存在时失败 |
41
+ | `--no-config` | 完全不读取本地配置,与 `--config` 互斥 |
42
+ | `--api-url URL` | 服务根地址,不带 `/api/v1`;禁止内嵌凭据、query 或 fragment |
43
+ | `--project-id UUID` | Lingora 项目 UUID |
44
+ | `--api-key KEY` | 临时凭据参数;推荐环境注入以避免暴露于进程参数 |
45
+ | `--branch NAME`、`-b NAME` | Lingora 分支名称,不是 Git 分支或 UUID |
46
+ | `--timeout SECONDS` | 单次 HTTP 超时,默认 30,必须为有限正数 |
47
+ | `--json` | stdout 仅输出一个 JSON 结果,便于脚本或 MCP 消费 |
48
+
49
+ 默认读取当前目录的 `.lingorarc`;没有该文件时,可仅通过完整参数执行。
50
+ 显式配置缺失、内容非法或含 `apiKey` 时必须失败,不静默忽略。
51
+
52
+ 配置优先级:命令参数 > `LINGORA_API_URL` / `LINGORA_PROJECT_ID` / `LINGORA_BRANCH` > JSON。
53
+ 凭据优先级:`--api-key` > `LINGORA_API_KEY`。凭据不写入配置、安装包或本机凭据文件。
54
+ 文件中资源相对路径以配置目录为准;命令行 `--files`、`--path` 相对路径以当前目录为准。
55
+
56
+ ```bash
57
+ lingora init
58
+ # 或者在首次创建时填入连接信息;已有文件时不可再次 init。
59
+ lingora init --api-url https://lingora.example.com \
60
+ --project-id 11111111-1111-1111-1111-111111111111 --branch develop
61
+ lingora push --config ci/lingora.json --branch develop
62
+ lingora pull --config ci/lingora.json --branch develop --json
63
+ ```
64
+
65
+ ## 3. push:完整参数上传
66
+
67
+ 所有必要配置都可以直接跟在 `push` 后面,无须 `.lingorarc`。先通过安全的个人环境或 CI Secret
68
+ 注入 `LINGORA_API_KEY`,不要将真实密钥直接写在示例命令或版本化文件里。
69
+
70
+ ```bash
71
+ lingora push \
72
+ --no-config \
73
+ --api-url https://lingora.example.com \
74
+ --project-id 11111111-1111-1111-1111-111111111111 \
75
+ --branch develop \
76
+ --files app/src/main/res/values/strings.xml app/src/main/res/values/arrays.xml \
77
+ --base-language en-US \
78
+ --namespace app \
79
+ --format ANDROID_XML \
80
+ --force-mode NO_FORCE \
81
+ --override-mode RECOMMENDED \
82
+ --error-on-unresolved-conflict auto \
83
+ --no-override-key-descriptions \
84
+ --no-convert-placeholders-to-icu \
85
+ --no-remove-other-keys \
86
+ --json
87
+ ```
88
+
89
+ `push` 仅要求连接、分支和源文件信息,不要求配置下载目标语言。
90
+
91
+ | 参数 | 用途 / 默认值 |
92
+ | --- | --- |
93
+ | `--files PATH ...` | 一个或多个基础 XML,可重复使用;同次文件名和资源键不得重复 |
94
+ | `--base-language TAG` | 所有源文件的基础语言;使用配置时可覆盖其 language |
95
+ | `--namespace NAME` | 所有源文件的 namespace;空字符串表示默认空 namespace |
96
+ | `--format ANDROID_XML` | 资源格式,默认 `ANDROID_XML` |
97
+ | `--force-mode KEEP\|OVERRIDE\|NO_FORCE` | 冲突策略,默认 `NO_FORCE`,不会交互询问 |
98
+ | `--override-mode RECOMMENDED\|ALL` | 默认 `RECOMMENDED`,遵守服务端审核值保护策略 |
99
+ | `--error-on-unresolved-conflict yes\|no\|auto` | 未解决冲突时的失败策略,默认 `auto` |
100
+ | `--override-key-descriptions` / `--no-override-key-descriptions` | 覆盖描述开关;未配置时为 true,`init` 模板为 false |
101
+ | `--convert-placeholders-to-icu` / `--no-convert-placeholders-to-icu` | 占位符转换;未配置时为 true,`init` 模板为 false |
102
+ | `--tag-new-keys TAG ...` | 给新增键添加标签 |
103
+ | `--remove-other-keys` / `--no-remove-other-keys` | 默认 false;true 会删除本次导入范围内未出现的服务端键,谨慎使用 |
104
+
105
+ `push.files` 的多个文件通过一次 multipart import 提交,而不是分别上传后删除彼此的资源。
106
+ 服务端保护、权限不足或冲突仍会使操作失败,CLI 不绕过限制。
107
+
108
+ ## 4. pull 与 validate:下载和本地校验
109
+
110
+ ```bash
111
+ lingora pull \
112
+ --no-config \
113
+ --api-url https://lingora.example.com \
114
+ --project-id 11111111-1111-1111-1111-111111111111 \
115
+ --branch develop \
116
+ --files app/src/main/res/values/strings.xml app/src/main/res/values/arrays.xml \
117
+ --base-language en-US \
118
+ --namespace app \
119
+ --languages ja zh-CN \
120
+ --path generated/lingora/res \
121
+ --states TRANSLATED REVIEWED \
122
+ --file-structure-template 'values-{androidLanguageTag}/strings.xml' \
123
+ --empty-dir \
124
+ --json
125
+
126
+ lingora validate --config .lingorarc --json
127
+ ```
128
+
129
+ 基础文件用于类型、占位符和数组长度校验,不会随下载覆盖。
130
+ `pull` / `validate` 可使用 `--files`、`--base-language`、`--namespace`、`--format`,以及:
131
+
132
+ | 参数 | 用途 |
133
+ | --- | --- |
134
+ | `--languages TAG ...`、`-l` | 必填目标语言,不包含基础语言 |
135
+ | `--path DIR` | 专用生成目录,默认 `generated/lingora/res` |
136
+ | `--namespaces NAME ...`、`-n` | 必须与源文件的单一 namespace 一致 |
137
+ | `--states STATE ...`、`-s` | 默认 `TRANSLATED REVIEWED`;可含 `UNTRANSLATED` |
138
+ | `--file-structure-template TEMPLATE` | 默认 `values-{androidLanguageTag}/strings.xml` |
139
+ | `--empty-dir` / `--no-empty-dir` | 是否校验后替换完整生成目录;未配置为 false,`init` 模板为 true |
140
+ | `--tags TAG ...` | 仅下载命中任一标签的键 |
141
+ | `--exclude-tags TAG ...` | 排除这些标签 |
142
+ | `--support-arrays` / `--no-support-arrays` | 将索引键按数组导出,默认 false |
143
+ | `--include-empty-translations` / `--no-include-empty-translations` | 默认 false |
144
+
145
+ `validate` 不联网、不写文件;下载相关筛选参数仅影响 `pull` 的请求。
146
+ 下载、解包或校验失败不替换旧目录;不完整目标数组在下载暂存目录中整组移除,以使用 APK fallback。
147
+ 不允许输出目录包含配置或源资源,也不允许 ZIP 路径穿越、额外文件或覆盖基础文件。
148
+ 没有目标翻译时可成功但显示 `exported=none`;本地 `validate` 的 `languages=[]` 不代表翻译已下载。
149
+
150
+ ## 5. extract、compare、sync:资源键工作流
151
+
152
+ ```bash
153
+ # 本地读取资源键;数组转换为 days[0]、days[1] 等键。
154
+ lingora extract print --config .lingorarc --json
155
+ lingora extract check --config .lingorarc --json
156
+
157
+ # 仅比较键名集合,不比较所有翻译值;--check 在有差异时返回 1。
158
+ lingora compare --config .lingorarc --branch develop --json
159
+ lingora compare --config .lingorarc --check --json
160
+
161
+ # 先预览,再明确批准补齐缺失键。不会覆盖已有值或描述,不下载资源。
162
+ lingora sync --config .lingorarc --dry-run --json
163
+ lingora sync --config .lingorarc --yes --tag-new-keys added-by-cli --json
164
+
165
+ # 可选删除当前 namespace 内未出现在本地输入中的键。
166
+ lingora sync --config .lingorarc --yes --remove-unused --json
167
+ ```
168
+
169
+ 以上命令支持源资源参数;`extract` 不要求服务地址、项目 ID 或密钥。
170
+ `compare` 和 `sync` 需要项目、分支和凭据,不要求下载配置。
171
+ `sync` 固定使用 `KEEP`,只有差异需要写入时才要求 `--yes`;`--remove-unused` 不从配置自动开启。
172
+ `unused` 仅表示“不在这组输入文件里”,并不证明项目没有使用这些键;不要未审查就自动删除。
173
+ 服务器键分页不完整、重复或总数变化时会失败,不拿部分列表继续写操作。
174
+
175
+ ## 6. tag:管理 Key 标签
176
+
177
+ ```bash
178
+ # 必须指定 namespace,以及 --keys 或 --all 范围。
179
+ lingora tag --config .lingorarc --namespace app \
180
+ --keys hello welcome --tag android release --dry-run --json
181
+
182
+ lingora tag --config .lingorarc --namespace app \
183
+ --keys hello welcome --tag android release --yes --json
184
+
185
+ lingora tag --config .lingorarc --namespace app --all \
186
+ --filter-tag 'old-*' --filter-no-tag protected \
187
+ --untag 'old-*' --tag current --yes --json
188
+ ```
189
+
190
+ `--tag` 增加标签,`--untag` 移除标签;移除和筛选支持 shell 风格通配符,使用引号避免 shell 展开。
191
+ `--filter-tag` 匹配任一已有标签,`--filter-no-tag` 排除命中标签的键。
192
+ 该命令不要求 Android 文件。每个 Key 单独请求,**不是跨 Key 事务**;失败时 JSON 错误的
193
+ `details.updatedKeyIds` 给出已经写入的 Key,`partial` 表示是否部分完成。
194
+ 不要并发运行多个标签修改器,当前接口没有跨请求的标签并发合并机制。
195
+
196
+ ## 7. branch 与 merge:项目分支
197
+
198
+ ```bash
199
+ lingora branch --config .lingorarc --json
200
+ lingora branch feature/demo --config .lingorarc --origin develop --json
201
+ lingora branch --create feature/demo --config .lingorarc --origin develop --json
202
+ lingora branch --delete feature/demo --config .lingorarc --yes --json
203
+
204
+ # 创建服务端预览,但不合并文案。
205
+ lingora merge feature/demo --config .lingorarc --json
206
+
207
+ # 再创建并检查最新预览,无未解决冲突且未过时才应用。
208
+ lingora merge feature/demo --config .lingorarc --apply --yes --json
209
+ ```
210
+
211
+ 这些命令不要求本地 Android 文件,支持仅用 `--api-url`、`--project-id` 和环境密钥执行。
212
+ 创建分支的来源必须来自 `--origin` 或配置的 `branch`,不会猜测 Git 分支。
213
+ 合并目标由服务端的来源分支关系确定;不允许 CLI 自动解决冲突或跳过过时预览检查。
214
+ 只有显式传 `--delete-source-branch` 才在合并后请求删除来源分支,未完成任务的额外保护仍由服务端执行。
215
+ 查看分支需要 `translations.view`,分支写入和合并需要 `branch.management`。
216
+
217
+ ## 8. delivery check:公开 CDN 检查
218
+
219
+ ```bash
220
+ lingora delivery check --config .lingorarc --alias current --json
221
+
222
+ # 完全独立于项目配置和 API Key;languages 包含基础语言。
223
+ lingora delivery check --no-config \
224
+ --url https://cdn.example.com/your-prefix \
225
+ --namespace app --languages en-US ja zh-CN --json
226
+ ```
227
+
228
+ 读取 `manifest.json` 和 `<namespace>/<locale>.json`,检查语言集合与翻译值类型。
229
+ 缺少缓存头会作为 warnings 返回,不等于发布失败。
230
+ 检查可读不能证明它来自最新项目或最近发布;该命令不创建或发布 Delivery。
231
+ App 仍自行配置运行时 Delivery URL,不从 CLI 配置或凭据读取。
232
+
233
+ ## 9. JSON 协议、退出码与 MCP 调用
234
+
235
+ `--json` 的 stdout 只输出一个 JSON 文档,无进度文本;stderr 保留脱敏警告。
236
+
237
+ ```json
238
+ {
239
+ "ok": true,
240
+ "command": "push",
241
+ "data": {"affected": 3, "deleted": 0},
242
+ "error": null,
243
+ "exitCode": 0
244
+ }
245
+ ```
246
+
247
+ 失败时 `ok=false`、`error.message` 给出原因;部分操作可带 `error.details`。
248
+ 退出码:0 成功;1 执行、校验、网络或凭据失败;2 命令参数错误。
249
+ `compare --check` 检测到键差异也返回 1,但会保留 `data` 中的差异结果。
250
+ `--help` / `--version` 是终端展示选项,不返回这个 JSON 协议;需要 JSON 帮助时使用 `help ... --json`。
251
+
252
+ MCP 层应构造参数数组调用固定版本命令,不拼接 shell,也不要从文本日志猜结果:
253
+
254
+ ```python
255
+ import json
256
+ import os
257
+ import subprocess
258
+
259
+ result = subprocess.run(
260
+ [
261
+ "lingora", "push", "--no-config",
262
+ "--api-url", "https://lingora.example.com",
263
+ "--project-id", "11111111-1111-1111-1111-111111111111",
264
+ "--branch", "develop",
265
+ "--files", "app/src/main/res/values/strings.xml",
266
+ "--base-language", "en-US", "--namespace", "app",
267
+ "--force-mode", "NO_FORCE", "--json",
268
+ ],
269
+ env=os.environ.copy(), # MCP 服务预先通过 Secret 注入 LINGORA_API_KEY。
270
+ capture_output=True,
271
+ text=True,
272
+ timeout=120,
273
+ check=False,
274
+ )
275
+ payload = json.loads(result.stdout)
276
+ ```
277
+
278
+ MCP 还应对服务地址、可读写目录、项目和允许执行的写命令建立白名单,并在调用端获取写入授权。
279
+ stdout 与 stderr 不得混流;超时后不要盲目重试写操作,应先核对远端是否已经提交。
280
+ 此处提供的是 CLI 调用契约,尚未实现 MCP Server。
@@ -0,0 +1,6 @@
1
+ include README.md COMMANDS.md pyproject.toml
2
+ recursive-include src/lingora_cli *.py *.json
3
+ prune tests
4
+ prune scripts
5
+ exclude uv.lock .lingorarc .env Makefile .gitignore
6
+ global-exclude *.pyc __pycache__ .env .env.* .lingorarc
@@ -0,0 +1,98 @@
1
+ Metadata-Version: 2.4
2
+ Name: lingora-cli
3
+ Version: 0.1.0rc1
4
+ Summary: Lingora translation synchronization CLI
5
+ Project-URL: Repository, https://github.com/brain/lingora
6
+ Requires-Python: >=3.12
7
+ Description-Content-Type: text/markdown
8
+
9
+ # Lingora CLI
10
+
11
+ 独立安装的翻译同步工具;只调用 Lingora 公开 HTTP API,不依赖后端源码或 Android 仓库中的脚本。
12
+ 需要 Python 3.12+。CLI 与后端、Maven SDK 的版本互不绑定。
13
+
14
+ ## 安装与使用
15
+
16
+ 首次 PyPI 发布完成后使用固定版本安装(当前代码不代表包已发布):
17
+
18
+ ```bash
19
+ uv tool install --python 3.12 "lingora-cli==0.1.0"
20
+ lingora --version
21
+ lingora init
22
+ ```
23
+
24
+ 编辑生成的 `.lingorarc`:设置 `apiUrl`、`projectId`、`branch`、源文件、目标语言和 Delivery URL。
25
+ `init` 不联网、不创建服务端项目、不覆盖已有配置;不要把 API Key 写进配置。
26
+ 所有资源相对路径以配置文件所在目录为基准。多文件通过 `push.files` 配置,必须使用相同语言和 namespace、不同文件名,不允许重复资源键。
27
+ 默认模板 namespace 为 `app`,与 Demo 的 `app/{languageTag}.json` 一致。
28
+
29
+ ```bash
30
+ # 通过个人环境或 CI Secret 提供 LINGORA_API_KEY;不要将真实值写入文档。
31
+ lingora push
32
+ lingora pull
33
+ lingora validate
34
+ lingora delivery check --alias current
35
+
36
+ # 公共参数可放在子命令前后。
37
+ lingora pull --config ci/lingora.json --branch your-branch
38
+ ```
39
+
40
+ CLI 只提供独立操作,不提供 `mock-ci`。上传、下载和构建的编排由消费项目的脚本或 CI 负责:
41
+
42
+ ```bash
43
+ lingora push && lingora pull
44
+ ```
45
+
46
+ `push` 会写入服务端;`pull` 只下载并校验资源,失败时不会触发上传。
47
+ 完整参数、分支与 Key 管理、无配置调用、JSON 协议及 MCP 接入示例见独立的
48
+ [命令手册](COMMANDS.md)。也可执行 `lingora help push`、`lingora pull --help`。
49
+
50
+ 分支优先级:`--branch` > `LINGORA_BRANCH` > 配置;连接参数支持 `LINGORA_API_URL`、
51
+ `LINGORA_PROJECT_ID`,同样由命令参数优先覆盖。凭据优先级:`--api-key` > `LINGORA_API_KEY`。
52
+ 建议使用环境变量,避免凭据出现在进程参数中。错误返回 1,参数用法错误返回 2。
53
+ `pull.emptyDir=true` 只应用于专门的生成目录;下载或校验失败不替换原目录。
54
+ 不完整数组被移除,让 Android 回退基础资源;类型、占位符、未知键和额外文件仍然报错。
55
+
56
+ CLI 的 Delivery 检查 URL 与 App 的运行时 URL 分别配置,使用者需要确保二者指向同一资源且 namespace 一致。
57
+ Android 构建不执行 CLI,不上传翻译,也不读取 CLI 实现。
58
+
59
+ ## 开发与验证
60
+
61
+ ```bash
62
+ cd cli
63
+ make sync
64
+ make check
65
+ make test
66
+ make build
67
+ ```
68
+
69
+ 运行时无第三方依赖。构建使用 setuptools/setuptools-scm;开发依赖用于自动版本分析、lint 和安装包校验。
70
+ 尚未正式发布前可在隔离环境安装本地 wheel 验证,但消费项目不应依赖本仓库的源码路径。
71
+
72
+ ## 自动发布
73
+
74
+ 专用工作流 `.github/workflows/cli.yml`:PR 只检查;`develop` 自动发布 rc 预发布,`main` 自动发布正式版。
75
+ 只分析 CLI 相关 Conventional Commits:`fix` 升 patch,`feat` 升 minor,`!` 或 `BREAKING CHANGE` 升 major;`docs`、`test`、`chore` 不发版。
76
+ 首次正式版本为 0.1.0。示例:
77
+
78
+ | 分支 | Git Tag | PyPI 版本 |
79
+ | --- | --- | --- |
80
+ | develop | lingora-cli-0.1.0-rc.1 | 0.1.0rc1 |
81
+ | main | lingora-cli-0.1.0 | 0.1.0 |
82
+
83
+ Tag 是版本来源,命令从安装元数据读取版本;不回写受保护分支,不移动 Tag,不覆盖 PyPI 包。
84
+ 发版产物包含 SHA、版本、Tag 和 SHA256 清单;重跑同一 Actions run 时复用已上传的原产物。
85
+ 失败后用 Actions 的 Re-run failed jobs 恢复,不新建同版本或手工移动 Tag。
86
+
87
+ ### 所有者的一次性配置
88
+
89
+ 1. 在 PyPI 创建 `lingora-cli` 的 pending Trusted Publisher:owner `brain`、repository `lingora`、workflow `cli.yml`、environment `lingora-cli-pypi`。
90
+ 2. 在 GitHub 创建该 Environment,只允许 `main` 和 `develop`,不设置人工审批,以保留自动发版。
91
+ 3. 确保 Actions 可以创建 `lingora-cli-*` Tag;限制其他身份修改、删除版本 Tag,不给予受保护分支绕过权限。
92
+ 4. PyPI 配置或权限缺失会导致发布失败,不能当作发布成功。无需长期 PyPI Token。
93
+
94
+ 首版成功发布且干净环境安装验收后,再删除主仓库及 Android 仓库的旧翻译脚本。
95
+ Android 的 `sdk_release.py` 等 Maven 发布脚本不属于待删除对象。
96
+
97
+ 自动发版使用 [Python Semantic Release](https://python-semantic-release.readthedocs.io/en/latest/configuration/configuration-guides/monorepos.html)
98
+ 与 [PyPI Trusted Publisher](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/)。
@@ -0,0 +1,90 @@
1
+ # Lingora CLI
2
+
3
+ 独立安装的翻译同步工具;只调用 Lingora 公开 HTTP API,不依赖后端源码或 Android 仓库中的脚本。
4
+ 需要 Python 3.12+。CLI 与后端、Maven SDK 的版本互不绑定。
5
+
6
+ ## 安装与使用
7
+
8
+ 首次 PyPI 发布完成后使用固定版本安装(当前代码不代表包已发布):
9
+
10
+ ```bash
11
+ uv tool install --python 3.12 "lingora-cli==0.1.0"
12
+ lingora --version
13
+ lingora init
14
+ ```
15
+
16
+ 编辑生成的 `.lingorarc`:设置 `apiUrl`、`projectId`、`branch`、源文件、目标语言和 Delivery URL。
17
+ `init` 不联网、不创建服务端项目、不覆盖已有配置;不要把 API Key 写进配置。
18
+ 所有资源相对路径以配置文件所在目录为基准。多文件通过 `push.files` 配置,必须使用相同语言和 namespace、不同文件名,不允许重复资源键。
19
+ 默认模板 namespace 为 `app`,与 Demo 的 `app/{languageTag}.json` 一致。
20
+
21
+ ```bash
22
+ # 通过个人环境或 CI Secret 提供 LINGORA_API_KEY;不要将真实值写入文档。
23
+ lingora push
24
+ lingora pull
25
+ lingora validate
26
+ lingora delivery check --alias current
27
+
28
+ # 公共参数可放在子命令前后。
29
+ lingora pull --config ci/lingora.json --branch your-branch
30
+ ```
31
+
32
+ CLI 只提供独立操作,不提供 `mock-ci`。上传、下载和构建的编排由消费项目的脚本或 CI 负责:
33
+
34
+ ```bash
35
+ lingora push && lingora pull
36
+ ```
37
+
38
+ `push` 会写入服务端;`pull` 只下载并校验资源,失败时不会触发上传。
39
+ 完整参数、分支与 Key 管理、无配置调用、JSON 协议及 MCP 接入示例见独立的
40
+ [命令手册](COMMANDS.md)。也可执行 `lingora help push`、`lingora pull --help`。
41
+
42
+ 分支优先级:`--branch` > `LINGORA_BRANCH` > 配置;连接参数支持 `LINGORA_API_URL`、
43
+ `LINGORA_PROJECT_ID`,同样由命令参数优先覆盖。凭据优先级:`--api-key` > `LINGORA_API_KEY`。
44
+ 建议使用环境变量,避免凭据出现在进程参数中。错误返回 1,参数用法错误返回 2。
45
+ `pull.emptyDir=true` 只应用于专门的生成目录;下载或校验失败不替换原目录。
46
+ 不完整数组被移除,让 Android 回退基础资源;类型、占位符、未知键和额外文件仍然报错。
47
+
48
+ CLI 的 Delivery 检查 URL 与 App 的运行时 URL 分别配置,使用者需要确保二者指向同一资源且 namespace 一致。
49
+ Android 构建不执行 CLI,不上传翻译,也不读取 CLI 实现。
50
+
51
+ ## 开发与验证
52
+
53
+ ```bash
54
+ cd cli
55
+ make sync
56
+ make check
57
+ make test
58
+ make build
59
+ ```
60
+
61
+ 运行时无第三方依赖。构建使用 setuptools/setuptools-scm;开发依赖用于自动版本分析、lint 和安装包校验。
62
+ 尚未正式发布前可在隔离环境安装本地 wheel 验证,但消费项目不应依赖本仓库的源码路径。
63
+
64
+ ## 自动发布
65
+
66
+ 专用工作流 `.github/workflows/cli.yml`:PR 只检查;`develop` 自动发布 rc 预发布,`main` 自动发布正式版。
67
+ 只分析 CLI 相关 Conventional Commits:`fix` 升 patch,`feat` 升 minor,`!` 或 `BREAKING CHANGE` 升 major;`docs`、`test`、`chore` 不发版。
68
+ 首次正式版本为 0.1.0。示例:
69
+
70
+ | 分支 | Git Tag | PyPI 版本 |
71
+ | --- | --- | --- |
72
+ | develop | lingora-cli-0.1.0-rc.1 | 0.1.0rc1 |
73
+ | main | lingora-cli-0.1.0 | 0.1.0 |
74
+
75
+ Tag 是版本来源,命令从安装元数据读取版本;不回写受保护分支,不移动 Tag,不覆盖 PyPI 包。
76
+ 发版产物包含 SHA、版本、Tag 和 SHA256 清单;重跑同一 Actions run 时复用已上传的原产物。
77
+ 失败后用 Actions 的 Re-run failed jobs 恢复,不新建同版本或手工移动 Tag。
78
+
79
+ ### 所有者的一次性配置
80
+
81
+ 1. 在 PyPI 创建 `lingora-cli` 的 pending Trusted Publisher:owner `brain`、repository `lingora`、workflow `cli.yml`、environment `lingora-cli-pypi`。
82
+ 2. 在 GitHub 创建该 Environment,只允许 `main` 和 `develop`,不设置人工审批,以保留自动发版。
83
+ 3. 确保 Actions 可以创建 `lingora-cli-*` Tag;限制其他身份修改、删除版本 Tag,不给予受保护分支绕过权限。
84
+ 4. PyPI 配置或权限缺失会导致发布失败,不能当作发布成功。无需长期 PyPI Token。
85
+
86
+ 首版成功发布且干净环境安装验收后,再删除主仓库及 Android 仓库的旧翻译脚本。
87
+ Android 的 `sdk_release.py` 等 Maven 发布脚本不属于待删除对象。
88
+
89
+ 自动发版使用 [Python Semantic Release](https://python-semantic-release.readthedocs.io/en/latest/configuration/configuration-guides/monorepos.html)
90
+ 与 [PyPI Trusted Publisher](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/)。
@@ -0,0 +1,61 @@
1
+ [build-system]
2
+ requires = ["setuptools>=80", "setuptools-scm>=9.2"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "lingora-cli"
7
+ dynamic = ["version"]
8
+ description = "Lingora translation synchronization CLI"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ dependencies = []
12
+
13
+ [project.scripts]
14
+ lingora = "lingora_cli.cli:main"
15
+
16
+ [project.urls]
17
+ Repository = "https://github.com/brain/lingora"
18
+
19
+ [dependency-groups]
20
+ dev = ["python-semantic-release==10.7.0", "ruff>=0.14,<0.15", "twine>=6,<7"]
21
+
22
+ [tool.setuptools.packages.find]
23
+ where = ["src"]
24
+
25
+ [tool.setuptools.package-data]
26
+ lingora_cli = ["config.example.json"]
27
+
28
+ [tool.setuptools_scm]
29
+ root = ".."
30
+ fallback_version = "0.0.0.dev0"
31
+ local_scheme = "no-local-version"
32
+
33
+ [tool.setuptools_scm.tag]
34
+ prefix = "lingora-cli-"
35
+ strict = true
36
+
37
+ [tool.semantic_release]
38
+ tag_format = "lingora-cli-{version}"
39
+ allow_zero_version = true
40
+ major_on_zero = true
41
+ commit_parser = "conventional-monorepo"
42
+ version_toml = []
43
+ version_variables = []
44
+
45
+ [tool.semantic_release.commit_parser_options]
46
+ path_filters = [".", "../.github/workflows/cli.yml"]
47
+
48
+ [tool.semantic_release.branches.main]
49
+ match = "^main$"
50
+ prerelease = false
51
+
52
+ [tool.semantic_release.branches.develop]
53
+ match = "^develop$"
54
+ prerelease = true
55
+ prerelease_token = "rc"
56
+
57
+ [tool.ruff]
58
+ target-version = "py312"
59
+
60
+ [tool.ruff.lint]
61
+ select = ["E4", "E7", "E9", "F", "I"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ """Lingora 翻译同步命令行包。"""
@@ -0,0 +1,5 @@
1
+ """通过 python -m lingora_cli 执行命令。"""
2
+
3
+ from .cli import main
4
+
5
+ raise SystemExit(main())