@chesterbrains/translify-cli 0.3.0 → 0.4.0
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.
- package/README.md +3 -2
- package/dist/commands/publish.js +26 -4
- package/dist/http.js +3 -0
- package/dist/index.js +5 -1
- package/dist/render.js +12 -7
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -133,7 +133,7 @@ Options:
|
|
|
133
133
|
|
|
134
134
|
- `push`: `--dry-run`, `--overwrite-targets`, `--prune`, `-y/--yes`, `--namespace <ns>`, `--status <draft|needs-review|approved>`
|
|
135
135
|
- `pull` and `status`: `--from <working|env:slug>`, `--locale <locale>` (as named on disk), `--namespace <ns>`, `--only-approved`
|
|
136
|
-
- `publish`: `--fail-on-withheld`
|
|
136
|
+
- `publish`: `--fail-on-withheld`, `--allow-errors`
|
|
137
137
|
- `lint`: `--namespace <ns>`, `--locale <locale>` (as named on disk), `--severity <error|warning>` (only list cells whose worst issue is this severity; counts and the exit code are unaffected), `--max-warnings <n>`
|
|
138
138
|
- `--json` on `push`, `pull`, `status`, `publish` and `lint`: machine-readable output on stdout
|
|
139
139
|
- `--debug` (global): print error details and stack traces
|
|
@@ -156,6 +156,7 @@ When the project has **Require review** turned on in Translify:
|
|
|
156
156
|
- `push` lands target-locale translations it writes as **needs review**. `--status draft` or `--status needs-review` picks the status explicitly, and `--status approved` approves them as they land. The source file is always approved. Each locale row shows the status its cells landed in, and the output counts approved translations that went back to review because this push changed their source text.
|
|
157
157
|
- Without review, `push` approves everything it writes, and `--status draft` / `--status needs-review` are refused (`REVIEW_DISABLED`).
|
|
158
158
|
- `publish` to an environment with **Publish approved text only** ships the last approved text of each translation. The output adds how many translations were **carried over** (the approved text went out instead of a newer, unapproved edit) and how many were **withheld** (never approved, so left out). Publishing still succeeds; `--fail-on-withheld` exits 5 when anything was withheld, for pipelines that must ship everything reviewed.
|
|
159
|
+
- `publish` refuses when a translation that would go live has a placeholder error (a missing or extra placeholder, invalid ICU, a printf type mismatch). Nothing is published, the refused translations are listed like `lint` lists them, and the command exits 6. Warnings never block. On an environment with **Publish approved text only**, only approved translations count: one carried over from its last approved text never blocks. `--allow-errors` publishes anyway and reports how many went out with errors; the publish is recorded in the activity log with the override.
|
|
159
160
|
- `pull --only-approved` writes the approved text from the working copy and leaves out translations that were never approved, for building a release bundle without publishing. `status --only-approved` compares against the same. It works with `--from working` only: environments already apply the gate.
|
|
160
161
|
|
|
161
162
|
## CI (GitHub Actions)
|
|
@@ -234,7 +235,7 @@ Pull and publish on deploy:
|
|
|
234
235
|
| 3 | Plan quota exceeded |
|
|
235
236
|
| 4 | Network failure, server error, push too large, CLI too old, transaction conflict, or rate limited after 3 retries |
|
|
236
237
|
| 5 | `publish --fail-on-withheld`: the publish went through, but the review gate withheld some translations |
|
|
237
|
-
| 6 | `lint` found
|
|
238
|
+
| 6 | Placeholder errors: `lint` found them (or more warnings than `--max-warnings`), or `publish` refused because translations with errors would go live |
|
|
238
239
|
|
|
239
240
|
The server's error `code` decides the exit code first; the HTTP status is only used for codes the CLI does not know.
|
|
240
241
|
|
package/dist/commands/publish.js
CHANGED
|
@@ -1,15 +1,37 @@
|
|
|
1
|
-
import { EXIT } from '../errors.js';
|
|
1
|
+
import { CliError, EXIT } from '../errors.js';
|
|
2
|
+
import { renderIssueCells } from '../render.js';
|
|
3
|
+
const isInvalidCells = (details) => typeof details === 'object' && details !== null && details.code === 'INVALID_CELLS';
|
|
4
|
+
/** The server's one-line refusal, the cells grouped like `lint`, and the way out. */
|
|
5
|
+
const refusal = (body, config) => {
|
|
6
|
+
const cells = Array.isArray(body.cells) ? body.cells : [];
|
|
7
|
+
const lines = [
|
|
8
|
+
`Publish refused: ${body.total} translation(s) with placeholder errors would go live. Nothing was published.`,
|
|
9
|
+
...renderIssueCells(cells, config),
|
|
10
|
+
];
|
|
11
|
+
if (body.total > cells.length)
|
|
12
|
+
lines.push(`Showing first ${cells.length} of ${body.total}.`);
|
|
13
|
+
lines.push('Fix them (translify lint lists every issue) or re-run with --allow-errors.');
|
|
14
|
+
return new CliError(EXIT.issues, lines.join('\n'), body);
|
|
15
|
+
};
|
|
2
16
|
export async function runPublish(environment, deps) {
|
|
3
|
-
|
|
17
|
+
// Only send the flag when it is set: a server older than the gate rejects unknown fields.
|
|
18
|
+
const request = deps.allowErrors === true ? { environment, allowErrors: true } : { environment };
|
|
19
|
+
const res = await deps.api.post('/cli/v1/publish', request).catch((error) => {
|
|
20
|
+
if (error instanceof CliError && isInvalidCells(error.details))
|
|
21
|
+
throw refusal(error.details, deps.config);
|
|
22
|
+
throw error;
|
|
23
|
+
});
|
|
4
24
|
const carriedOverCount = res.carriedOverCount ?? 0;
|
|
5
25
|
const withheldCount = res.withheldCount ?? 0;
|
|
26
|
+
const errorCount = res.errorCount ?? 0;
|
|
6
27
|
// An ungated environment reports 0 / 0; keep its output as it was before review state.
|
|
7
28
|
const gate = carriedOverCount > 0 || withheldCount > 0 ? ` ${carriedOverCount} carried over, ${withheldCount} withheld.` : '';
|
|
29
|
+
const errors = errorCount > 0 ? ` ${errorCount} with placeholder errors.` : '';
|
|
8
30
|
deps.out(deps.json === true
|
|
9
31
|
? JSON.stringify({
|
|
10
32
|
response: res,
|
|
11
|
-
summary: { environment, publishedCount: res.publishedCount, carriedOverCount, withheldCount },
|
|
33
|
+
summary: { environment, publishedCount: res.publishedCount, carriedOverCount, withheldCount, errorCount },
|
|
12
34
|
})
|
|
13
|
-
: `Published ${res.publishedCount} translation(s) to ${environment}.${gate}`);
|
|
35
|
+
: `Published ${res.publishedCount} translation(s) to ${environment}.${gate}${errors}`);
|
|
14
36
|
return deps.failOnWithheld === true && withheldCount > 0 ? EXIT.withheld : EXIT.ok;
|
|
15
37
|
}
|
package/dist/http.js
CHANGED
|
@@ -32,6 +32,7 @@ const CODE_EXIT = {
|
|
|
32
32
|
RATE_LIMITED: EXIT.network,
|
|
33
33
|
REVIEW_DISABLED: EXIT.failed,
|
|
34
34
|
APPROVED_ONLY_REQUIRES_WORKING: EXIT.failed,
|
|
35
|
+
INVALID_CELLS: EXIT.issues,
|
|
35
36
|
};
|
|
36
37
|
const exitFor = (status, code) => {
|
|
37
38
|
const byCode = code === undefined ? undefined : CODE_EXIT[code];
|
|
@@ -88,6 +89,8 @@ const describeBody = (status, body) => {
|
|
|
88
89
|
return 'Review is off for this project, so --status draft or needs-review cannot be used. Turn on "Require review" in the project settings, or drop --status.';
|
|
89
90
|
case 'APPROVED_ONLY_REQUIRES_WORKING':
|
|
90
91
|
return ONLY_APPROVED_NEEDS_WORKING;
|
|
92
|
+
case 'INVALID_CELLS':
|
|
93
|
+
return `Publish refused: ${String(body.total)} translation(s) with placeholder errors would go live. Nothing was published.`;
|
|
91
94
|
case 'TRANSACTION_CONFLICT':
|
|
92
95
|
return 'The server hit a transaction conflict. Try again.';
|
|
93
96
|
case 'RATE_LIMITED':
|
package/dist/index.js
CHANGED
|
@@ -106,13 +106,17 @@ program
|
|
|
106
106
|
.command('publish <environment>')
|
|
107
107
|
.description('Publish an environment')
|
|
108
108
|
.option('--fail-on-withheld', 'exit 5 when the review gate withheld any translation (the publish still happens)')
|
|
109
|
+
.option('--allow-errors', 'publish even when translations with placeholder errors would go live (otherwise refused, exit 6)')
|
|
109
110
|
.option('--json', 'machine-readable output')
|
|
110
111
|
.action(async (environment, opts) => {
|
|
112
|
+
const { api, config } = await withApi();
|
|
111
113
|
process.exitCode = await runPublish(environment, {
|
|
112
|
-
api
|
|
114
|
+
api,
|
|
115
|
+
config,
|
|
113
116
|
out,
|
|
114
117
|
json: opts.json,
|
|
115
118
|
failOnWithheld: opts.failOnWithheld,
|
|
119
|
+
allowErrors: opts.allowErrors,
|
|
116
120
|
});
|
|
117
121
|
});
|
|
118
122
|
program
|
package/dist/render.js
CHANGED
|
@@ -109,14 +109,11 @@ const cut = (value) => (value.length > MAX_VALUE ? `${value.slice(0, MAX_VALUE -
|
|
|
109
109
|
* ESLint-style: namespace, then key, then one line per issue with the locale as named on disk.
|
|
110
110
|
* Rows arrive sorted by the server; only the grouping headers are added here.
|
|
111
111
|
*/
|
|
112
|
-
export function
|
|
113
|
-
const { errors, warnings } = res.summary;
|
|
114
|
-
if (errors + warnings === 0)
|
|
115
|
-
return pc.green(`✔ No issues in ${res.summary.locales.length} locale(s).`);
|
|
112
|
+
export function renderIssueCells(cells, config) {
|
|
116
113
|
const lines = [];
|
|
117
114
|
let namespace;
|
|
118
115
|
let key;
|
|
119
|
-
for (const cell of
|
|
116
|
+
for (const cell of cells) {
|
|
120
117
|
if (cell.namespace !== namespace) {
|
|
121
118
|
namespace = cell.namespace;
|
|
122
119
|
key = undefined;
|
|
@@ -126,14 +123,22 @@ export function renderLint(res, config, view = {}) {
|
|
|
126
123
|
key = cell.key;
|
|
127
124
|
lines.push(` ${key}`);
|
|
128
125
|
}
|
|
129
|
-
const locale = toFsLocale(cell.locale, config).padEnd(8);
|
|
126
|
+
const locale = (config === undefined ? cell.locale : toFsLocale(cell.locale, config)).padEnd(8);
|
|
130
127
|
for (const issue of cell.issues) {
|
|
131
128
|
const label = issue.severity === 'ERROR' ? pc.red('error'.padEnd(8)) : pc.yellow('warning'.padEnd(8));
|
|
132
129
|
lines.push(` ${locale} ${label} ${issueText(issue)}`);
|
|
133
|
-
if (issue.code === 'ICU_INVALID')
|
|
130
|
+
if (issue.code === 'ICU_INVALID' && cell.value !== undefined) {
|
|
134
131
|
lines.push(`${VALUE_INDENT}${pc.dim(JSON.stringify(cut(cell.value)))}`);
|
|
132
|
+
}
|
|
135
133
|
}
|
|
136
134
|
}
|
|
135
|
+
return lines;
|
|
136
|
+
}
|
|
137
|
+
export function renderLint(res, config, view = {}) {
|
|
138
|
+
const { errors, warnings } = res.summary;
|
|
139
|
+
if (errors + warnings === 0)
|
|
140
|
+
return pc.green(`✔ No issues in ${res.summary.locales.length} locale(s).`);
|
|
141
|
+
const lines = renderIssueCells(res.cells, config);
|
|
137
142
|
if (res.truncated) {
|
|
138
143
|
const matched = view.severity === 'error' ? errors : view.severity === 'warning' ? warnings : errors + warnings;
|
|
139
144
|
lines.push(pc.yellow(`Showing first ${res.cells.length} of ${matched} cells. Narrow with --namespace / --locale, or use --json.`));
|
package/dist/version.js
CHANGED