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 37358394612a [fix](i18n) Label automated translation PRs ai-translate 
(#4549)
37358394612a is described below

commit 37358394612a1d4e47132ddfe96e1b165a648b08
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Thu Oct 29 00:02:32 2026 +0000

    [fix](i18n) Label automated translation PRs ai-translate (#4549)
    
    Automated Japanese, Korean and Russian translation PRs now receive
    `ai-translate` alongside their existing `translate-<locale>` label. The
    shared workflow verifies that both labels exist before calling the
    translation model.
    
    Validation: 49 focused translation workflow/publication tests and 27
    workflow security tests passed; `git diff --check` passed. The local
    end-to-end publication simulator passed and recorded both `translate-ja`
    and `ai-translate` in the actual PR command. All three PR checks passed
    on final head `33ef82c501a4e43d7ed0886fa426d27e2a06bd7e`: Build Check
    (389 tests passed, one conditional skip), Markdown Links and File Sizes.
    The repository already has all four required labels.
    
    Self-review against AGENTS.md:
    
    - Goal: the shared publisher covers all three language workflows and
    adds both labels when creating a PR.
    - Scope: limited to the publisher, shared workflow preflight, existing
    regression test and local publication simulator.
    - Information architecture: no documentation, version, locale or
    navigation content changes.
    - Links and routes: no paths, anchors, slugs or sidebar entries change.
    - Frontend/build conventions: no rendering or build behavior changes;
    existing workflow triggers, permissions, checkout settings and
    credentials are preserved.
    - Validation: tests cover successful label checks and failure for either
    missing label in every language; simulated publication and all normal PR
    checks passed.
    - Other issues: none found.
    
    ---------
    
    Co-authored-by: morningman <[email protected]>
---
 .github/workflows/translation-run.yml              |  7 +++--
 .../i18n/__tests__/translation-workflows.test.cjs  | 32 ++++++++++++++--------
 scripts/i18n/simulate-workflow.cjs                 |  5 ++--
 scripts/i18n/workflow-publish.cjs                  |  2 +-
 4 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/.github/workflows/translation-run.yml 
b/.github/workflows/translation-run.yml
index f46a84a5d292..a43ae4b18811 100644
--- a/.github/workflows/translation-run.yml
+++ b/.github/workflows/translation-run.yml
@@ -157,11 +157,14 @@ jobs:
         id: fresh
         run: node scripts/i18n/workflow-scan.cjs 
"$RUNNER_TEMP/$TRANSLATION_WORKSPACE"
       # The publisher labels the PR only after the model calls and the branch 
push.
-      - name: Verify the PR label before model calls
+      - name: Verify the PR labels before model calls
         if: steps.fresh.outputs.can_generate == 'true'
         env:
           GH_TOKEN: ${{ github.token }}
-        run: test "$(gh api 
"repos/apache/doris-website/labels/translate-$I18N_LOCALE" --jq .name)" = 
"translate-$I18N_LOCALE"
+        run: |
+          for label in "translate-$I18N_LOCALE" ai-translate; do
+            test "$(gh api "repos/apache/doris-website/labels/$label" --jq 
.name)" = "$label"
+          done
       - name: Install ossutil for the pinned Codex package and auth pool
         if: steps.fresh.outputs.can_generate == 'true'
         run: |
diff --git a/scripts/i18n/__tests__/translation-workflows.test.cjs 
b/scripts/i18n/__tests__/translation-workflows.test.cjs
index 6ed0f7ee0629..8c02f972ca0d 100644
--- a/scripts/i18n/__tests__/translation-workflows.test.cjs
+++ b/scripts/i18n/__tests__/translation-workflows.test.cjs
@@ -139,28 +139,38 @@ test('discovery and generation exchange artifacts in the 
caller workspace', () =
     assert(scripts.every(step => step.run.endsWith(' 
"$RUNNER_TEMP/$TRANSLATION_WORKSPACE"')));
     const index = name => generate.findIndex(step => step.name === name);
     assert(index('Reconcile again immediately before model calls') < 
index('Replan exact source and open PRs'));
-    assert(index('Replan exact source and open PRs') < index('Verify the PR 
label before model calls'));
+    assert(index('Replan exact source and open PRs') < index('Verify the PR 
labels before model calls'));
     assert.equal(generate.find(step => step.id === 
'translation').env.I18N_CODEX_MODEL,
         "${{ vars.I18N_CODEX_MODEL || 'gpt-6-sol' }}");
 });
 
-test('the label check runs before auth loading and fails for a missing locale 
label', t => {
+test('the label check runs before auth loading and requires both PR labels for 
every language', t => {
     const steps = shared.jobs.generate.steps;
-    const label = steps.findIndex(step => step.name === 'Verify the PR label 
before model calls');
+    const label = steps.findIndex(step => step.name === 'Verify the PR labels 
before model calls');
     const auth = steps.findIndex(step => step.id === 'auth');
     assert(label >= 0 && label < auth);
     assert.equal(steps[label].if, "steps.fresh.outputs.can_generate == 
'true'");
     assert.equal(steps[label].env.GH_TOKEN, '${{ github.token }}');
     const bin = fs.mkdtempSync(path.join(os.tmpdir(), 'translation-label-'));
     t.after(() => fs.rmSync(bin, {recursive: true, force: true}));
-    // The mock answers like the labels API: the name, or nothing when missing.
-    fs.writeFileSync(path.join(bin, 'gh'), '#!/bin/sh\ncase "$2" in 
*/labels/translate-ja) echo translate-ja;; */labels/translate-ru) echo 
translate-ru;; esac\n',
-        {mode: 0o755});
-    const run = locale => spawnSync('bash', ['-e', '-c', steps[label].run],
-        {env: {...process.env, PATH: `${bin}:${process.env.PATH}`, 
I18N_LOCALE: locale}}).status;
-    assert.equal(run('ja'), 0);
-    assert.equal(run('ru'), 0);
-    assert.notEqual(run('ko'), 0);
+    // The mock answers like the labels API, including a failed request for a 
missing label.
+    fs.writeFileSync(path.join(bin, 'gh'), `#!/bin/sh
+case "$2" in
+  
*/labels/translate-ja|*/labels/translate-ko|*/labels/translate-ru|*/labels/ai-translate)
+    label="\${2##*/}";;
+  *) exit 1;;
+esac
+test "$label" != "$TEST_MISSING_LABEL" || exit 1
+echo "$label"
+`, {mode: 0o755});
+    const run = (locale, missing = '') => spawnSync('bash', ['-e', '-c', 
steps[label].run],
+        {env: {...process.env, PATH: `${bin}:${process.env.PATH}`, 
I18N_LOCALE: locale,
+            TEST_MISSING_LABEL: missing}}).status;
+    for (const locale of Object.keys(callers)) {
+        assert.equal(run(locale), 0, locale);
+        assert.notEqual(run(locale, `translate-${locale}`), 0, `${locale}: 
missing language label`);
+        assert.notEqual(run(locale, 'ai-translate'), 0, `${locale}: missing 
shared label`);
+    }
 });
 
 test('publication preserves the verified runtime and refresh path', () => {
diff --git a/scripts/i18n/simulate-workflow.cjs 
b/scripts/i18n/simulate-workflow.cjs
index 9870775dab81..5a45731bc5fa 100644
--- a/scripts/i18n/simulate-workflow.cjs
+++ b/scripts/i18n/simulate-workflow.cjs
@@ -82,10 +82,11 @@ if (args[0] === 'api' && args.some(arg => 
arg.startsWith('repos/apache/doris-web
     assert(!args.includes('--draft'));
     assert.equal(value('--base'), 'master');
     assert.equal(value('--repo'), 'apache/doris-website');
-    assert.equal(value('--label'), 'translate-ja');
+    const labels = args.flatMap((arg, index) => arg === '--label' ? 
[args[index + 1]] : []);
+    assert.deepEqual(labels, ['translate-ja', 'ai-translate']);
     const body = fs.readFileSync(value('--body-file'), 'utf8');
     assert.match(body, /japanese-translation-automation:v1 
bot-head=[a-f0-9]{40}/);
-    const record = {draft: false, base: value('--base'), head: 
value('--head'), labels: [value('--label')], body,
+    const record = {draft: false, base: value('--base'), head: 
value('--head'), labels, body,
         url: 'https://example.invalid/apache/doris-website/pull/1'};
     fs.writeFileSync(process.env.I18N_SIM_PR_RECORD, JSON.stringify(record, 
null, 2) + '\\n');
     process.stdout.write(record.url + '\\n');
diff --git a/scripts/i18n/workflow-publish.cjs 
b/scripts/i18n/workflow-publish.cjs
index 8eaa73bf1896..49d1ba8f8647 100644
--- a/scripts/i18n/workflow-publish.cjs
+++ b/scripts/i18n/workflow-publish.cjs
@@ -98,7 +98,7 @@ async function main() {
     execFileSync('gh', ['auth', 'setup-git'], {cwd: root, stdio: 'inherit'});
     git('push', 'origin', `HEAD:refs/heads/${branch}`);
     const url = execFileSync('gh', ['pr', 'create', '--repo', 
'apache/doris-website', '--base', 'master',
-        '--head', branch, '--label', `translate-${locale}`,
+        '--head', branch, '--label', `translate-${locale}`, '--label', 
'ai-translate',
         '--title', `docs(i18n): machine ${language} translation candidates`,
         '--body-file', bodyFile], {cwd: root, encoding: 'utf8'}).trim();
     if (process.env.GITHUB_STEP_SUMMARY) 
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY,


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to