This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-website.git
The following commit(s) were added to refs/heads/master by this push:
new b2216b0b48a [ci](i18n) Schedule Korean translation runs twice daily
(#4409)
b2216b0b48a is described below
commit b2216b0b48add56314a2d6d2880efdc7b2b9e65d
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Tue Oct 20 07:13:32 2026 +0100
[ci](i18n) Schedule Korean translation runs twice daily (#4409)
## Why
Korean generation has been manual-only (`workflow_dispatch`) since the
workflow was added in #4358. All 3336 Korean target documents (Dev 1670,
4.x 1615, community 51) are now translated, merged and served in
production. From here the workflow needs to keep them current as the
English sources change, the same way the Japanese workflow does.
## What changed
### 1. Schedule
`korean-translation.yml` now also runs at 04:43 and 16:43 UTC, twenty
minutes after the Japanese schedule (04:23 and 16:23). Both workflows
already share the `japanese-translation-ja` concurrency group with
`cancel-in-progress: false` and `queue: max`, so a Korean run waits
while a Japanese run is still working and never uses the Codex auth pool
at the same time.
### 2. Scheduled runs generate
Scheduled runs have no inputs, so the mode fell back to `dry-run`. But
`workflow-scan.cjs` treats every scheduled run as eligible, while
`workflow-generate.cjs` only runs in `generate` mode. With the old
fallback, a scheduled run with a pending document would have reached the
generation step and failed after loading Codex auth. The fallback is now
`generate`, matching the Japanese workflow. Manual dispatch still
defaults to `dry-run`.
### 3. Tests
`korean-workflow.test.cjs` now asserts the schedule (and that it stays
offset from the Japanese one) and the new fallback. A new test passes
the mode a scheduled run resolves to into `allowGeneration`. With the
old `dry-run` fallback, both tests fail.
### 4. Docs
`translations/README.md` still described the workflow as manual-only and
production as Japanese-only. Both statements are updated.
Unchanged: the planner limit (20) and batch size (10), open-PR
reconciliation and deduplication, protection of human-owned targets, the
`translate-ko` label check before auth loading, and the PR admission
checks. Maintainers review and merge generated PRs as before; nothing is
merged automatically.
## Verification
- `node --test` on Node 20.20.2: `korean-workflow` 5/5, `workflow`
29/29, `multi-locale` 6/6.
- Mutation check: restoring the `dry-run` fallback fails the two updated
assertions.
- Scheduled discovery replayed locally on this branch
(`GITHUB_EVENT_NAME=schedule`, `I18N_MODE=generate`, `I18N_LOCALE=ko`):
38 open PRs, 0 relevant; required 3336, merged current 3336, pending 0;
selected 0, `can_generate=false`. On today's state, a scheduled run
scans and skips generation without calling the model.
## After merge
The first scheduled run will be checked for its trigger time, lock and
queue behaviour, discovery summary, and that generation is skipped while
nothing is pending.
Rollback: remove the `schedule` block. Manual dispatch keeps working
either way.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: morningman <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
.github/workflows/korean-translation.yml | 14 +++++++++----
scripts/i18n/__tests__/korean-workflow.test.cjs | 20 ++++++++++++++++---
translations/README.md | 26 +++++++++++++++----------
3 files changed, 43 insertions(+), 17 deletions(-)
diff --git a/.github/workflows/korean-translation.yml
b/.github/workflows/korean-translation.yml
index 133a4ea2fcb..11487252b54 100644
--- a/.github/workflows/korean-translation.yml
+++ b/.github/workflows/korean-translation.yml
@@ -21,6 +21,12 @@ on:
paths:
description: Optional JSON array of exact English .md/.mdx files
(exclusive with path)
type: string
+ # Twenty minutes after the Japanese schedule; the shared lock below holds
+ # this run while a Japanese run is still in progress. Scheduled runs have no
+ # inputs, so the mode falls back to generate; manual runs keep the dry-run
+ # default.
+ schedule:
+ - cron: '43 4,16 * * *'
permissions:
contents: read
@@ -62,7 +68,7 @@ jobs:
- name: Plan all current and historical pending documents
id: scan
env:
- I18N_MODE: ${{ inputs.mode || 'dry-run' }}
+ I18N_MODE: ${{ inputs.mode || 'generate' }}
I18N_PLUGIN: ${{ inputs.plugin }}
I18N_VERSION: ${{ inputs.version }}
I18N_PATH: ${{ inputs.path }}
@@ -110,7 +116,7 @@ jobs:
APP_ID: ${{ secrets.I18N_GITHUB_APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.I18N_GITHUB_APP_PRIVATE_KEY }}
I18N_GITHUB_PAT: ${{ secrets.I18N_GITHUB_PAT }}
- I18N_MODE: ${{ inputs.mode || 'dry-run' }}
+ I18N_MODE: ${{ inputs.mode || 'generate' }}
run: |
if [[ -n "$APP_ID" && -n "$APP_PRIVATE_KEY" ]]; then
echo 'auth_kind=app' >> "$GITHUB_OUTPUT"
@@ -140,7 +146,7 @@ jobs:
if: steps.config.outputs.ready == 'true'
id: fresh
env:
- I18N_MODE: ${{ inputs.mode || 'dry-run' }}
+ I18N_MODE: ${{ inputs.mode || 'generate' }}
I18N_PLUGIN: ${{ inputs.plugin }}
I18N_VERSION: ${{ inputs.version }}
I18N_PATH: ${{ inputs.path }}
@@ -192,7 +198,7 @@ jobs:
id: translation
env:
I18N_CODEX_MODEL: ${{ vars.I18N_CODEX_MODEL || 'gpt-6-sol' }}
- I18N_MODE: ${{ inputs.mode || 'dry-run' }}
+ I18N_MODE: ${{ inputs.mode || 'generate' }}
I18N_PATH: ${{ inputs.path }}
I18N_MAX_PAGES: ${{ inputs.mode == 'validate' && '1' || '10' }}
GITHUB_REPOSITORY: ${{ github.repository }}
diff --git a/scripts/i18n/__tests__/korean-workflow.test.cjs
b/scripts/i18n/__tests__/korean-workflow.test.cjs
index b193deb02e9..6b83352a05e 100644
--- a/scripts/i18n/__tests__/korean-workflow.test.cjs
+++ b/scripts/i18n/__tests__/korean-workflow.test.cjs
@@ -5,6 +5,7 @@ const path = require('node:path');
const yaml = require('js-yaml');
const os = require('node:os');
const {spawnSync} = require('node:child_process');
+const {allowGeneration} = require('../workflow-generate.cjs');
const workflows = path.resolve(__dirname, '../../../.github/workflows');
const load = name => yaml.load(fs.readFileSync(path.join(workflows, name),
'utf8'));
const korean = load('korean-translation.yml');
@@ -22,8 +23,11 @@ test('one-page validation rejects an exact batch list before
creating scan artif
assert(!fs.existsSync(output));
});
-test('Korean generation requires a manual choice and shares the existing
whole-run lock', () => {
- assert.deepEqual(Object.keys(korean.on), ['workflow_dispatch']);
+test('Korean generation runs on its schedule or a manual choice and shares the
existing whole-run lock', () => {
+ assert.deepEqual(Object.keys(korean.on), ['workflow_dispatch',
'schedule']);
+ // Twice daily, offset from the Japanese runs that hold the same lock.
+ assert.deepEqual(korean.on.schedule, [{cron: '43 4,16 * * *'}]);
+ assert.deepEqual(japanese.on.schedule, [{cron: '23 4,16 * * *'}]);
assert.equal(korean.on.workflow_dispatch.inputs.mode.default, 'dry-run');
assert.deepEqual(korean.on.workflow_dispatch.inputs.mode.options,
['dry-run', 'validate', 'generate']);
assert.equal(korean.on.workflow_dispatch.inputs.locale, undefined);
@@ -37,7 +41,7 @@ test('Korean generation requires a manual choice and shares
the existing whole-r
for (const job of Object.values(korean.jobs)) {
assert.equal(job.env.I18N_LOCALE, 'ko');
for (const step of job.steps) {
- if (step.env?.I18N_MODE) assert.equal(step.env.I18N_MODE, "${{
inputs.mode || 'dry-run' }}");
+ if (step.env?.I18N_MODE) assert.equal(step.env.I18N_MODE, "${{
inputs.mode || 'generate' }}");
if (step.uses?.startsWith('actions/checkout@')) {
assert.equal(step.with['fetch-depth'], 0);
assert.equal(step.with['persist-credentials'], false);
@@ -46,6 +50,16 @@ test('Korean generation requires a manual choice and shares
the existing whole-r
}
});
+test('a scheduled run resolves to a mode that the generation gate accepts', ()
=> {
+ // Discovery marks every scheduled run eligible, so the input-less fallback
+ // must be a mode that workflow-generate.cjs will run, not a dry run.
+ const step = korean.jobs.generate.steps.find(item => item.id ===
'translation');
+ const fallback = /\|\| '([a-z-]+)' \}\}$/.exec(step.env.I18N_MODE)[1];
+ assert.equal(allowGeneration({I18N_MODE: fallback, GITHUB_EVENT_NAME:
'schedule', I18N_MAX_PAGES: '10'}), true);
+ assert.equal(allowGeneration({I18N_MODE:
korean.on.workflow_dispatch.inputs.mode.default,
+ GITHUB_EVENT_NAME: 'workflow_dispatch', I18N_MAX_PAGES: '10'}), false);
+});
+
test('Korean discovery and generation exchange Korean artifacts at the exact
scanned commit', () => {
const discover = korean.jobs.discover.steps;
const generate = korean.jobs.generate.steps;
diff --git a/translations/README.md b/translations/README.md
index d940cc7cb31..75806b97e50 100644
--- a/translations/README.md
+++ b/translations/README.md
@@ -32,7 +32,7 @@ build; the scoped website build and static-page verification
run afterward.
Korean is registered as an optional documentation locale. Enable it locally
with
`DORIS_ENABLE_KO=true`, or enable both managed languages with
-`DORIS_ENABLE_LOCALES=ja,ko`. Production deployment still enables Japanese
only.
+`DORIS_ENABLE_LOCALES=ja,ko`. Production deployment enables both Japanese and
Korean.
Korean ordinary pages use the existing English fallback.
The initial Korean native inventory is pinned to the merged source commit in
@@ -103,10 +103,17 @@ existing source/technical hash algorithms unchanged.
Offline Korean fixtures
exercise these boundaries without real credentials or model calls; actual
single-page generation is a separate validation step.
-The `Korean translation discovery` workflow is manual-only and defaults to a
-read-only dry run. It fixes the locale to `ko`, uses separate Korean artifacts,
-and shares the existing `japanese-translation-ja` lock with Japanese runs.
-After the workflow is merged, run discovery on master:
+The `Korean translation discovery` workflow runs at 04:43 and 16:43 UTC daily
+and on manual dispatch. It fixes the locale to `ko`, uses separate Korean
+artifacts, and shares the existing `japanese-translation-ja` lock with Japanese
+runs. The schedule starts twenty minutes after the Japanese one, so a Korean
run
+waits while a Japanese run is still in progress. A scheduled run generates a
+batch when eligible Korean documents are pending, with the same limits as a
+manual `generate` run: the planner considers at most 20 targets and a batch has
+at most 10 pages. It opens a normal labeled PR that maintainers review and
merge
+like any other; nothing is merged automatically. With nothing pending, a
+scheduled run only scans and makes no model call. Manual dispatch defaults to a
+read-only dry run. To run discovery on master:
```bash
gh workflow run korean-translation.yml --repo apache/doris-website --ref
master -f mode=dry-run
@@ -120,8 +127,7 @@ to one and rechecks active PRs before calling Codex.
Generation checks the
runtime and auth pool. A write credential enables a normal labeled PR after
shared admission checks; otherwise a successful one-page validation remains
an artifact and does not establish the PR publication path. Inspect generation
-warnings and normal PR checks before merging. There is no Korean generation
-schedule in this workflow.
+warnings and normal PR checks before merging.
## Search, navigation and four-language validation
@@ -605,7 +611,7 @@ No real model call or GitHub write occurs.
### Selecting an exact Korean manual batch
-The Korean workflow remains manual-only and defaults to `dry-run`. Its optional
+Manual Korean dispatch defaults to `dry-run`. Its optional
`paths` input accepts a JSON array of 1–20 exact English source files, so a
bounded
batch can select documents from different folders without including every page
under a shared prefix:
@@ -622,8 +628,8 @@ fresh scan immediately before generation. `validate` still
requires one exact
targets and generation still produces at most 10. Exact selection preserves
full coverage counts, current-page and active-PR exclusion, human ownership,
scope/version checks, and exact-master protection. Unknown, duplicate,
-malformed or out-of-scope source paths fail before model calls. This input does
-not enable scheduled generation.
+malformed or out-of-scope source paths fail before model calls. Scheduled runs
+do not use this input; they plan from the full scope.
The planner CLI exposes the same filter as `--paths '<JSON array>'`.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]