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 eda6edcb0741 [improve](ci) Avoid full builds for landing translation 
metadata (#4520)
eda6edcb0741 is described below

commit eda6edcb07417add48d942e6d6cf5d6627f7727e
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Mon Oct 26 07:47:49 2026 +0000

    [improve](ci) Avoid full builds for landing translation metadata (#4520)
    
    PR #4516 spent 125m54s building the complete five-language site, then
    failed after another 17m11s of static verification. The actual failure
    was an obsolete browser assertion: `/ja/download/` was required to have
    an English body even though the PR publishes a Japanese translation.
    
    Two known inputs, `translations/COMMUNITY_ENTRIES.md` and
    `translations/community-reports.json`, were classified as unknown and
    escalated the PR to full compilation. Treat top-level translation
    maintenance notes as prose and the report registry as shared page
    content, so landing translation PRs use the existing representative
    build. Ordinary pages remain fully rendered; document samples span
    configured languages and active versions. Dependencies, versions,
    unknown inputs and explicit `ci/full` requests retain full validation.
    
    Run sampling, native content and publishing fixtures only for their
    respective subsystems. Skip unchanged translation unit tests for
    document/dictionary/state edits and the full docs governance scan for
    landing-only edits. Keep browser tools available for independently
    selected fixtures. Browser language/fallback expectations now follow the
    native manifest; the content fixture exercises both English fallback and
    translated bodies.
    
    Validation:
    - Replayed all 30 changed paths from #4516: `full` becomes `sample`;
    publishing fixture and docs governance are omitted.
    - 286 i18n tests passed, one existing skip; 22 workflow/security tests
    passed; actionlint and `git diff --check` passed.
    - Reproduced the original `ja !== en` failure against #4516's existing
    local five-language landing artifact. The updated verifier passed
    language navigation, disabled storage/JavaScript and 1440/390 layouts
    across eight themes.
    - Fresh native fixture builds passed in five languages and with Russian
    disabled (nine locale builds, 162 checked pages). The updated CLI
    verifier passed both fallback and translated bodies against the fresh
    artifact. The real #4516 browser reproduction reused its existing local
    landing artifact; no new full-site compilation or production deployment
    is claimed.
    
    Self-review against AGENTS.md:
    - Goal: fixes unnecessary full-build selection and the stale language
    assertion; the exact failing PR's file set now selects representative
    compilation.
    - Scope: six CI, test and maintenance-document files; existing build and
    sampling implementations are reused.
    - Information architecture: document trees, versions, locale content and
    sidebars are unchanged.
    - Links/navigation: no public paths, anchors, slugs or redirects change.
    - Frontend/build conventions: no UI changes; production builds and
    full-validation overrides remain available.
    - Validation/security: read-only PR permissions and credential-free
    checkout remain; checks above cover policy, workflow wiring and browser
    behavior. GitHub CI will validate fresh fixture builds.
    - Other issues: samples retain their publication prohibition and
    explicit coverage limits. No additional issue found.
    
    ---------
    
    Co-authored-by: morningman <[email protected]>
---
 .github/workflows/build-check.yml           |  6 ++---
 scripts/i18n/CI_VALIDATION.md               | 14 +++++++++-
 scripts/i18n/__tests__/ci-sampling.test.cjs | 40 +++++++++++++++++++++++++++++
 scripts/i18n/__tests__/ci-scope.test.cjs    |  5 ++++
 scripts/i18n/ci-plan.cjs                    | 24 ++++++++++++-----
 scripts/i18n/verify-global-language.cjs     | 23 ++++++++++++-----
 6 files changed, 95 insertions(+), 17 deletions(-)

diff --git a/.github/workflows/build-check.yml 
b/.github/workflows/build-check.yml
index 1445da03a333..4fedd9c21bad 100644
--- a/.github/workflows/build-check.yml
+++ b/.github/workflows/build-check.yml
@@ -120,7 +120,7 @@ jobs:
             - name: Translation unit tests
               env:
                   NODE_OPTIONS: --max-old-space-size=8192
-              if: steps.detect.outputs.validation_mode != 'skip'
+              if: steps.detect.outputs.translation_tests == 'true'
               run: node --test scripts/i18n/__tests__/*.test.cjs
 
             - name: Publish artifact optimization tests
@@ -160,7 +160,7 @@ jobs:
                   done
 
             - name: Install browser verification tools
-              if: steps.detect.outputs.validation_mode == 'full' || 
steps.detect.outputs.validation_mode == 'sample' || 
steps.detect.outputs.publish_fixture == 'true'
+              if: steps.detect.outputs.validation_mode == 'full' || 
steps.detect.outputs.validation_mode == 'sample' || 
steps.detect.outputs.publish_fixture == 'true' || 
steps.detect.outputs.content_fixture == 'true' || 
steps.detect.outputs.sampling_fixture == 'true'
               run: |
                   DORIS_BROWSER_TOOLS="$RUNNER_TEMP/doris-browser-tools"
                   npm install --prefix "$DORIS_BROWSER_TOOLS" --no-save 
--no-package-lock [email protected]
@@ -263,7 +263,7 @@ jobs:
                   retention-days: 14
 
             - name: Docs governance report
-              if: always() && steps.install.outcome == 'success' && 
steps.detect.outputs.validation_mode != 'fixtures'
+              if: always() && steps.install.outcome == 'success' && 
steps.detect.outputs.docs_report == 'true'
               continue-on-error: true
               run: |
                   yarn docs:lint:changed --output 
website-quality-governance/generated/docs-governance-report.json
diff --git a/scripts/i18n/CI_VALIDATION.md b/scripts/i18n/CI_VALIDATION.md
index 97671d2bf1f5..d24a937c0eb6 100644
--- a/scripts/i18n/CI_VALIDATION.md
+++ b/scripts/i18n/CI_VALIDATION.md
@@ -9,7 +9,7 @@ committed lockfile after source planning. Source snapshot 
checks remain enabled.
 | Change | Validation |
 | --- | --- |
 | README, planning or review notes only | Configuration, move/link and 
workflow security checks; no dependency installation or site build |
-| CI and tooling | Unit tests, five-language native compilation fixture and 
publishing/Apache/Chromium fixture |
+| CI and tooling | Applicable unit tests and fixtures for the changed 
subsystem; no real-site build |
 | Shared UI, shared UI registry, routing, SEO, localization or sidebars | 
Representative real documents in five languages and every active version; 
native content fixtures for content/locale implementations, publishing fixtures 
for delivery/host implementations |
 | Documentation or assets | Changed documents and import dependants, plus 
representative real documents; English source changes select all five 
languages, localized changes select English and the affected locales |
 | Dependencies, active versions or unclassified inputs | Full five-language 
publication build and verification |
@@ -26,6 +26,18 @@ configured language and active version, along with the 
native content fixture.
 Ordinary pages remain in the real build. This classification applies only to
 that exact registry path; other unknown inputs still require full validation.
 
+Top-level `translations/*.md` files are maintenance notes and do not request a
+site build. `translations/community-reports.json` controls ordinary page 
content
+and selects representative real builds. Combining these known inputs with a
+landing-page translation no longer upgrades it to a full publication build.
+Other unknown JSON inputs still require full validation.
+
+The sampling adapter, native content and publishing/Apache fixtures run only
+when their own implementation changes. Document, dictionary and 
translation-state
+edits retain content admission and real sampled compilation without rerunning
+unchanged translation unit tests. Docs governance runs for document/navigation
+changes and full validation, rather than scanning all docs for a landing UI 
edit.
+
 ## What a sample proves
 
 The CI adapter first loads native Docusaurus metadata for **all documents and
diff --git a/scripts/i18n/__tests__/ci-sampling.test.cjs 
b/scripts/i18n/__tests__/ci-sampling.test.cjs
index 288c875663ec..3d482260982d 100644
--- a/scripts/i18n/__tests__/ci-sampling.test.cjs
+++ b/scripts/i18n/__tests__/ci-sampling.test.cjs
@@ -47,6 +47,46 @@ test('shared UI registry selects real samples and content 
fixtures while unknown
     assert.equal(policy([registry], {forceFull: true}).mode, 'full');
     assert.equal(policy([registry, 'translations/shared/unknown.json'], 
{forceSample: true}).mode, 'full');
 });
+test('community translation metadata does not upgrade a landing-page PR to 
full compilation', () => {
+    const files = ['translations/COMMUNITY_ENTRIES.md', 
'translations/community-reports.json',
+        'translations/pages/registry.json', 'translations/pages/state.json',
+        'i18n/ja/code.json', 
'src/components/community-report-next/CommunityReportNext.tsx'];
+    const selected = policy(files);
+    assert.equal(selected.mode, 'sample');
+    assert.equal(selected.selection, 'representative');
+    assert.equal(selected.publicationAllowed, false);
+    assert.equal(selected.runPublishFixture, false);
+    assert.equal(selected.runDocsReport, false);
+    assert.equal(policy(['translations/community-reports.json']).mode, 
'sample');
+    for (const note of ['COMMUNITY_ENTRIES', 'FIRST_RELEASE', 'SHARED_UI', 
'README'])
+        assert.equal(policy([`translations/${note}.md`]).mode, 'skip');
+    assert.equal(policy([...files, 'translations/unknown.json']).mode, 'full');
+    assert.equal(policy(files, {forceFull: true}).mode, 'full');
+});
+test('CI tooling only runs the fixtures for its own subsystem', () => {
+    const language = policy(['scripts/i18n/verify-global-language.cjs']);
+    assert.equal(language.mode, 'fixtures');
+    assert.equal(language.runContentFixture, true);
+    assert.equal(language.runSamplingFixture, false);
+    assert.equal(language.runPublishFixture, false);
+    const sampler = policy(['scripts/i18n/ci-sample.cjs']);
+    assert.equal(sampler.runSamplingFixture, true);
+    assert.equal(sampler.runPublishFixture, false);
+    const publishing = policy(['scripts/publish/apache.cjs']);
+    assert.equal(publishing.runPublishFixture, true);
+    assert.equal(publishing.runSamplingFixture, false);
+    assert.equal(policy(['.github/workflows/build-check.yml']).runDocsReport, 
false);
+    const docs = policy(['docs/topic.md']);
+    assert.equal(docs.runTranslationTests, false);
+    assert.equal(docs.runDocsReport, true);
+    const dictionary = policy(['i18n/ja/code.json']);
+    assert.equal(dictionary.runTranslationTests, false);
+    assert.equal(dictionary.runDocsReport, false);
+    assert.equal(policy(['src/utils/localizedRoute.ts']).runTranslationTests, 
true);
+    const full = policy([], {forceFull: true});
+    assert.equal(full.runTranslationTests, true);
+    assert.equal(full.runDocsReport, true);
+});
 test('a registry-only commit samples all configured languages and active 
versions without claiming publication coverage', t => {
     const root = directory(t), registry = 'translations/shared/registry.json';
     fs.mkdirSync(path.join(root, 'translations/shared'), {recursive: true});
diff --git a/scripts/i18n/__tests__/ci-scope.test.cjs 
b/scripts/i18n/__tests__/ci-scope.test.cjs
index fc304216a9fe..e2a95efa5433 100644
--- a/scripts/i18n/__tests__/ci-scope.test.cjs
+++ b/scripts/i18n/__tests__/ci-scope.test.cjs
@@ -58,9 +58,14 @@ test('CI fails early on cheap checks, retains required job 
identity and deduplic
     assert(index('Publish artifact optimization tests') < index('Translation 
gate for changed locales'));
     assert(index('Publish artifact optimization tests') < index('Verify native 
content manifest fixture'));
     assert.match(steps[index('Translation unit tests')].run, 
/__tests__\/\*\.test\.cjs/);
+    assert.equal(steps[index('Translation unit tests')].if, 
"steps.detect.outputs.translation_tests == 'true'");
     assert.match(steps[index('Install dependencies')].run, /\byarn 
--frozen-lockfile\b/);
     assert.equal(steps.filter(step => /docs:links:changed/.test(step.run ?? 
'')).length, 0);
     assert.match(steps[index('Docs governance report')].run, 
/docs:lint:changed/);
+    assert.match(steps[index('Docs governance report')].if, 
/steps\.detect\.outputs\.docs_report == 'true'/);
+    const browserTools = steps[index('Install browser verification tools')].if;
+    for (const fixture of ['content_fixture', 'publish_fixture', 
'sampling_fixture'])
+        assert(browserTools.includes(`steps.detect.outputs.${fixture} == 
'true'`), fixture);
     const verify = steps[index('Verify localized static pages')].run;
     assert.match(verify, /\[ "\$LOCALE" = "ru" \]; then 
EXTRA_ARGS\+=\(--source-locales/);
     assert.match(verify, /\[ "\$LOCALE" = "ko" \] \|\| \[ "\$LOCALE" = "ru" 
\]; then EXTRA_ARGS=\(--themes\)/);
diff --git a/scripts/i18n/ci-plan.cjs b/scripts/i18n/ci-plan.cjs
index 941185b418d9..0bf2e69f3a46 100644
--- a/scripts/i18n/ci-plan.cjs
+++ b/scripts/i18n/ci-plan.cjs
@@ -14,10 +14,14 @@ const runtime =
 const content =
     
/^(static\/|docs\/|versioned_docs\/|community\/|course\/|releasenotes\/|blog\/|i18n\/|translations\/(pages|blog|state)\/)/;
 const highRisk = 
/^(package\.json|yarn\.lock|versions\.json|config\/versions-plugin\.js)$/;
+// Maintenance notes are not site content. The report registry, in contrast,
+// changes ordinary pages, which the real sample build always renders.
+const maintenanceNotes = /^translations\/[^/]+\.md$/;
+const reportRegistry = 'translations/community-reports.json';
 function policy(files, {forceFull = false, forceSample = false} = {}) {
     const risk = files.filter(file => highRisk.test(file));
     const changed = files.some(file => content.test(file));
-    const shared = files.some(file => runtime.test(file) || 
mechanism.test(file));
+    const shared = files.some(file => runtime.test(file) || 
mechanism.test(file) || file === reportRegistry);
     const checks = validationScope(files);
     const ci = files.some(file => /^(scripts\/|\.github\/)/.test(file));
     const unknown = files.filter(
@@ -25,7 +29,9 @@ function policy(files, {forceFull = false, forceSample = 
false} = {}) {
             !runtime.test(file) &&
             !mechanism.test(file) &&
             !content.test(file) &&
-            
!/^((scripts|\.github|plan-doc|review-docs|\.agents)\/|translations\/CONTENT_MANIFEST\.md$|README\.md$|AGENTS\.md$|LICENSE$|NOTICE$)/.test(
+            file !== reportRegistry &&
+            !maintenanceNotes.test(file) &&
+            
!/^((scripts|\.github|plan-doc|review-docs|\.agents)\/|README\.md$|AGENTS\.md$|LICENSE$|NOTICE$)/.test(
                 file,
             ),
     );
@@ -50,11 +56,15 @@ function policy(files, {forceFull = false, forceSample = 
false} = {}) {
         runContentFixture:
             (shared && checks.check_content_fixture) ||
             files.some(file =>
-                
/verify-content-fixture|__tests__\/fixtures\/content-manifest/.test(file),
+                
/verify-(content-fixture|global-language)|__tests__\/fixtures\/content-manifest/.test(file),
             ),
-        runSamplingFixture: mode === 'fixtures' || files.some(file =>
-            
/^scripts\/(build-locales?\.cjs$|i18n\/(ci-sample|ci-blog-sample|build-cache)\.cjs$)/.test(file)),
-        runPublishFixture: mode === 'fixtures' || checks.check_publish_fixture 
|| mode === 'full',
+        runSamplingFixture: files.some(file =>
+            
/^scripts\/(build-locales?\.cjs$|i18n\/(ci-sample|ci-blog-sample|build-cache|verify-ci-sampl(?:e|ing-fixture))\.cjs$)/.test(file)),
+        runPublishFixture: checks.check_publish_fixture || mode === 'full',
+        runTranslationTests: mode === 'full' || files.some(file =>
+            runtime.test(file) || mechanism.test(file) || 
/^scripts\/(i18n\/|build-locales?\.cjs$)/.test(file)
+                || file === '.github/workflows/build-check.yml'),
+        runDocsReport: mode === 'full' || (mode !== 'fixtures' && 
checks.check_docs_report),
         publicationAllowed: mode === 'full',
         checkRenderedLinks: !(files.length && files.every(file => 
/\.(css|scss|sass)$/.test(file))),
     };
@@ -164,6 +174,8 @@ if (require.main === module) {
                     content_fixture: result.runContentFixture,
                     publish_fixture: result.runPublishFixture,
                     sampling_fixture: result.runSamplingFixture,
+                    translation_tests: result.runTranslationTests,
+                    docs_report: result.runDocsReport,
                 })
                     .map(([k, v]) => `${k}=${v}\n`)
                     .join(''),
diff --git a/scripts/i18n/verify-global-language.cjs 
b/scripts/i18n/verify-global-language.cjs
index 761c0a06d871..986bce31ba98 100644
--- a/scripts/i18n/verify-global-language.cjs
+++ b/scripts/i18n/verify-global-language.cjs
@@ -48,7 +48,14 @@ function verifyContent(site, directory) {
     }
     return reports;
 }
-async function verifyBrowser(site, {fixture = false, output = 
'.i18n-build/browser-global-language.json'} = {}) {
+async function verifyBrowser(site, {fixture = false, manifests = 
'.i18n-build/content-manifests',
+    ordinary = fixture ? 'sample-2/' : 'download/', output = 
'.i18n-build/browser-global-language.json'} = {}) {
+    const manifest = JSON.parse(fs.readFileSync(path.join(manifests, 
'ja.json')));
+    const ordinaryPage = manifest.pages.find(page => page.kind === 'page' && 
page.route === '/ja/' + ordinary);
+    assert(ordinaryPage, `Missing ordinary-page manifest entry: 
/ja/${ordinary}`);
+    // The fixture deliberately exercises fallback. A real page may acquire a
+    // translation without changing its language-menu/navigation contract.
+    if (fixture && ordinary === 'sample-2/') 
assert.equal(ordinaryPage.fallback, true);
     const {chromium} = require('playwright');
     const server = createServer(site);
     await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
@@ -72,10 +79,9 @@ async function verifyBrowser(site, {fixture = false, output 
= '.i18n-build/brows
         const page = await context.newPage();
         page.on('pageerror', error => {errors.push(error.message);});
         page.on('console', message => {if (/hydration|did not match|server 
HTML/i.test(message.text())) errors.push(message.text());});
-        const ordinary = fixture ? 'sample-2/' : 'download/';
         await page.goto(origin + '/ja/' + ordinary + '?from=qa#usage');
         await page.locator('.language-menu 
a[href*="from=qa"]').first().waitFor({state: 'attached'});
-        assert.equal(await 
page.locator('[data-content-id]').first().getAttribute('lang'), 'en');
+        assert.equal(await 
page.locator('[data-content-id]').first().getAttribute('lang'), 
ordinaryPage.contentLocale);
         const menu = 
page.locator('.language-menu:not(.language-menu--mobile)');
         await menu.locator('summary').click();
         const english = menu.locator('a[lang="en-US"]');
@@ -151,8 +157,8 @@ async function verifyBrowser(site, {fixture = false, output 
= '.i18n-build/brows
         }
         const noJs = await browser.newContext({javaScriptEnabled: false});
         const noJsPage = await noJs.newPage(); await noJsPage.goto(origin + 
'/ja/' + ordinary);
-        assert.equal(await 
noJsPage.locator('[data-content-id]').first().getAttribute('lang'), 'en');
-        assert.equal(await noJsPage.locator('.page-fallback-notice').count(), 
1);
+        assert.equal(await 
noJsPage.locator('[data-content-id]').first().getAttribute('lang'), 
ordinaryPage.contentLocale);
+        assert.equal(await noJsPage.locator('.page-fallback-notice').count(), 
ordinaryPage.fallback ? 1 : 0);
         assert.equal(await noJsPage.locator('.language-menu 
a').first().getAttribute('href'), '/' + ordinary);
         await noJs.close();
         assert.deepEqual(errors, []);
@@ -165,8 +171,11 @@ async function main() {
     const args = process.argv.slice(2); const value = (name, fallback) => 
args.includes(name) ? args[args.indexOf(name) + 1] : fallback;
     const site = value('--site', 'build'), output = value('--output', 
'.i18n-build/browser-global-language.json');
     fs.mkdirSync(path.dirname(output), {recursive: true});
-    const content = verifyContent(site, value('--manifests', 
'.i18n-build/content-manifests'));
-    const browser = await verifyBrowser(site, {fixture: 
args.includes('--fixture'), output});
+    const manifests = value('--manifests', '.i18n-build/content-manifests');
+    const content = verifyContent(site, manifests);
+    const browser = await verifyBrowser(site, {fixture: 
args.includes('--fixture'), manifests, output});
+    if (args.includes('--fixture'))
+        browser.translatedPage = await verifyBrowser(site, {fixture: true, 
manifests, ordinary: 'sample-0/', output});
     fs.writeFileSync(output, JSON.stringify({content, ...browser}, null, 2) + 
'\n');
     console.log(JSON.stringify({content, ...browser}));
 }


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

Reply via email to