diff --git a/.github/workflows/content-pipelines.yml b/.github/workflows/content-pipelines.yml index 1fe634a0186a..d950d3bd0bdd 100644 --- a/.github/workflows/content-pipelines.yml +++ b/.github/workflows/content-pipelines.yml @@ -91,7 +91,6 @@ jobs: fi - name: Run content pipeline update script - id: update env: GH_TOKEN: ${{ secrets.DOCS_BOT_PAT_BASE }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -128,32 +127,31 @@ jobs: git push origin "$UPDATE_BRANCH" fi + - name: Read source repo info from config + id: source-info + env: + PIPELINE_ID: ${{ matrix.id }} + run: | + SOURCE_REPO=$(yq -r ".[\"${PIPELINE_ID}\"].\"source-repo\"" src/content-pipelines/config.yml) + SOURCE_PATH=$(yq -r ".[\"${PIPELINE_ID}\"].\"source-path\"" src/content-pipelines/config.yml) + echo "source_repo=$SOURCE_REPO" >> "$GITHUB_OUTPUT" + echo "source_path=$SOURCE_PATH" >> "$GITHUB_OUTPUT" + - name: Create or update PR if: steps.commit.outputs.has_changes == 'true' env: GH_TOKEN: ${{ secrets.DOCS_BOT_PAT_BASE }} UPDATE_BRANCH: ${{ steps.branch.outputs.update_branch }} PIPELINE_ID: ${{ matrix.id }} - COMPARE_URL: ${{ steps.update.outputs.compare_url }} - SHORT_RANGE: ${{ steps.update.outputs.short_range }} + SOURCE_REPO: ${{ steps.source-info.outputs.source_repo }} + SOURCE_PATH: ${{ steps.source-info.outputs.source_path }} run: | PR_NUMBER="${{ steps.check-pr.outputs.pr_number }}" PR_TITLE="docs: update ${PIPELINE_ID} content from source docs" - NEW_ITEM="* [\`${SHORT_RANGE}\`](${COMPARE_URL})" + SOURCE_LINK="See the [upstream repo](https://github.com/${SOURCE_REPO}/tree/main/${SOURCE_PATH}) for changes that triggered this update." if [ -n "$PR_NUMBER" ]; then - echo "PR #$PR_NUMBER already exists — appending source changes to body" - - EXISTING_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body') - - # Append a new bullet before the SOURCE_CHANGES end marker - # Uses bash parameter expansion for safe literal string replacement - MARKER="" - INSERTION="${NEW_ITEM}"$'\n' - INSERTION+="${MARKER}" - UPDATED_BODY="${EXISTING_BODY/$MARKER/$INSERTION}" - - gh pr edit "$PR_NUMBER" --body "$UPDATED_BODY" + echo "PR #$PR_NUMBER already exists" echo "Ensuring PR #$PR_NUMBER is marked ready for review" gh pr ready "$PR_NUMBER" || echo "Unable to mark PR #$PR_NUMBER as ready (it may already be ready)" @@ -166,10 +164,7 @@ jobs: PR_BODY+="## What this does"$'\n\n' PR_BODY+="Runs the \`content-pipeline-update\` agent (${PIPELINE_ID}) against the latest source docs and updates official articles under \`content/\` that have fallen out of sync."$'\n\n' PR_BODY+="## Source changes"$'\n\n' - PR_BODY+="Changes in the source repo that triggered this update:"$'\n\n' - PR_BODY+=""$'\n' - PR_BODY+="${NEW_ITEM}"$'\n' - PR_BODY+=""$'\n\n' + PR_BODY+="${SOURCE_LINK}"$'\n\n' PR_BODY+="## Review"$'\n\n' PR_BODY+="* Review each commit for accuracy — the agent uses AI, so spot-check important changes"$'\n' PR_BODY+="* To adjust agent behavior, see [Modifying results](${{ github.server_url }}/${{ github.repository }}/blob/main/src/content-pipelines/README.md#modifying-results)"$'\n' diff --git a/.github/workflows/validate-openapi-check.yml b/.github/workflows/validate-openapi-check.yml index 96f59c7b8ad0..fbfb0d5a0f05 100644 --- a/.github/workflows/validate-openapi-check.yml +++ b/.github/workflows/validate-openapi-check.yml @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build Docker image run: | diff --git a/content/site-policy/privacy-policies/github-subprocessors.md b/content/site-policy/privacy-policies/github-subprocessors.md index cfaaf0816014..dabceb1b33b5 100644 --- a/content/site-policy/privacy-policies/github-subprocessors.md +++ b/content/site-policy/privacy-policies/github-subprocessors.md @@ -43,6 +43,7 @@ If you have questions about this list, please contact us at | OpenAI | AI Inference and AI Services | United States | United States | | Oracle America, Inc. | Cloud Hosted Infrastructure | United States | United States | | Pusher | Building and managing real-time infrastructure for web and mobile applications | United States | United States | +| Tines Automation Inc. | Security management | United States | United States | | Twilio (SendGrid) | SMS notification provider for 2 Factor Authentication | United States | United States | | xAI | AI Inference and AI Services | United States | United States | | Zendesk | Customer support ticketing system | United States | United States | diff --git a/data/ui.yml b/data/ui.yml index 1f6257f545fe..abaf051be9e7 100644 --- a/data/ui.yml +++ b/data/ui.yml @@ -9,6 +9,7 @@ header: ' is currently available as a release candidate.' early_access: 📣 Please do not share this URL publicly. This page contains content about a private preview feature. release_notes_use_latest: Please use the latest release for the latest security, performance, and bug fixes. + machine_translation: Some of this page may have been machine-translated or translated using AI. # GHES release notes ghes_release_notes_upgrade_patch_only: 📣 This is not the latest patch release of Enterprise Server. ghes_release_notes_upgrade_release_only: 📣 This is not the latest release of Enterprise Server. diff --git a/package-lock.json b/package-lock.json index 975565ddfe4b..784119de90b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,7 @@ "escape-string-regexp": "5.0.0", "express": "^5.2.1", "fastest-levenshtein": "1.0.16", - "file-type": "21.0.0", + "file-type": "21.3.1", "flat": "^6.0.1", "github-slugger": "^2.0.0", "glob": "13.0.2", @@ -773,6 +773,16 @@ "node": ">=6.9.0" } }, + "node_modules/@borewit/text-codec": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.1.tgz", + "integrity": "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/@elastic/elasticsearch": { "version": "8.19.1", "resolved": "https://registry.npmjs.org/@elastic/elasticsearch/-/elasticsearch-8.19.1.tgz", @@ -3848,13 +3858,13 @@ } }, "node_modules/@tokenizer/inflate": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", - "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "license": "MIT", "dependencies": { - "debug": "^4.4.0", - "fflate": "^0.8.2", - "token-types": "^6.0.0" + "debug": "^4.4.3", + "token-types": "^6.1.1" }, "engines": { "node": ">=18" @@ -8459,11 +8469,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -8478,13 +8483,14 @@ } }, "node_modules/file-type": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.0.0.tgz", - "integrity": "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==", + "version": "21.3.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.1.tgz", + "integrity": "sha512-SrzXX46I/zsRDjTb82eucsGg0ODq2NpGDp4HcsFKApPy8P8vACjpJRDoGGMfEzhFC0ry61ajd7f72J3603anBA==", + "license": "MIT", "dependencies": { - "@tokenizer/inflate": "^0.2.7", - "strtok3": "^10.2.2", - "token-types": "^6.0.0", + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" }, "engines": { @@ -9629,7 +9635,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/ignore": { "version": "5.3.1", @@ -13313,19 +13320,6 @@ "through": "~2.3" } }, - "node_modules/peek-readable": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-7.0.0.tgz", - "integrity": "sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -15183,13 +15177,12 @@ } }, "node_modules/strtok3": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.2.2.tgz", - "integrity": "sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==", + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", + "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", "license": "MIT", "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^7.0.0" + "@tokenizer/token": "^0.3.0" }, "engines": { "node": ">=18" @@ -15522,10 +15515,12 @@ } }, "node_modules/token-types": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz", - "integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "license": "MIT", "dependencies": { + "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" }, diff --git a/package.json b/package.json index d277d04aa9f5..df0c55aca570 100644 --- a/package.json +++ b/package.json @@ -192,7 +192,7 @@ "escape-string-regexp": "5.0.0", "express": "^5.2.1", "fastest-levenshtein": "1.0.16", - "file-type": "21.0.0", + "file-type": "21.3.1", "flat": "^6.0.1", "github-slugger": "^2.0.0", "glob": "13.0.2", diff --git a/src/content-pipelines/scripts/update.ts b/src/content-pipelines/scripts/update.ts index 573f5853c2cc..8e3ea796c9cf 100644 --- a/src/content-pipelines/scripts/update.ts +++ b/src/content-pipelines/scripts/update.ts @@ -325,23 +325,6 @@ async function main(): Promise { console.log(`\nUpdated ${SHA_FILE} to ${currentSha}`) } - // ---- Write source change metadata to $GITHUB_OUTPUT ---- - // The workflow uses these values to populate the PR body with reviewer breadcrumbs. - const ghOutput = process.env.GITHUB_OUTPUT - if (ghOutput) { - const compareUrl = storedSha - ? `https://github.com/${SOURCE_REPO}/compare/${storedSha}...${currentSha}` - : `https://github.com/${SOURCE_REPO}/commit/${currentSha}` - const shortRange = storedSha - ? `${storedSha.slice(0, 7)}...${currentSha.slice(0, 7)}` - : currentSha.slice(0, 7) - - const outputLines = [`compare_url=${compareUrl}`, `short_range=${shortRange}`] - - fs.appendFileSync(ghOutput, `${outputLines.join('\n')}\n`) - console.log('Wrote source change metadata to $GITHUB_OUTPUT') - } - console.log('Done.') } diff --git a/src/fixtures/fixtures/data/ui.yml b/src/fixtures/fixtures/data/ui.yml index 1f6257f545fe..abaf051be9e7 100644 --- a/src/fixtures/fixtures/data/ui.yml +++ b/src/fixtures/fixtures/data/ui.yml @@ -9,6 +9,7 @@ header: ' is currently available as a release candidate.' early_access: 📣 Please do not share this URL publicly. This page contains content about a private preview feature. release_notes_use_latest: Please use the latest release for the latest security, performance, and bug fixes. + machine_translation: Some of this page may have been machine-translated or translated using AI. # GHES release notes ghes_release_notes_upgrade_patch_only: 📣 This is not the latest patch release of Enterprise Server. ghes_release_notes_upgrade_release_only: 📣 This is not the latest release of Enterprise Server. diff --git a/src/frame/components/page-header/HeaderNotifications.tsx b/src/frame/components/page-header/HeaderNotifications.tsx index 027851bd6674..e6305e31ed0f 100644 --- a/src/frame/components/page-header/HeaderNotifications.tsx +++ b/src/frame/components/page-header/HeaderNotifications.tsx @@ -1,4 +1,4 @@ -import { useEffect } from 'react' +import { useEffect, useState } from 'react' import { useRouter } from 'next/router' import cx from 'classnames' import { XIcon } from '@primer/octicons-react' @@ -11,11 +11,14 @@ import { useVersion } from '@/versions/components/useVersion' import { useUserLanguage } from '@/languages/components/useUserLanguage' import styles from './HeaderNotifications.module.scss' import { useSharedUIContext } from '@/frame/components/context/SharedUIContext' +import Cookies from '@/frame/components/lib/cookies' +import { MACHINE_TRANSLATION_BANNER_COOKIE_NAME } from '@/frame/lib/constants' enum NotificationType { RELEASE = 'RELEASE', TRANSLATION = 'TRANSLATION', EARLY_ACCESS = 'EARLY_ACCESS', + MACHINE_TRANSLATION = 'MACHINE_TRANSLATION', } type Notif = { @@ -36,6 +39,11 @@ export const HeaderNotifications = () => { const { t } = useTranslation('header') + const [machineTranslationDismissed, setMachineTranslationDismissed] = useState(true) + useEffect(() => { + setMachineTranslationDismissed(Cookies.get(MACHINE_TRANSLATION_BANNER_COOKIE_NAME) === 'true') + }, []) + const translationNotices: Array = [] if (router.locale === 'en') { if (userLanguage && userLanguage !== 'en' && languages[userLanguage]) { @@ -61,6 +69,22 @@ export const HeaderNotifications = () => { }) } } + + if (router.locale !== 'en' && !machineTranslationDismissed) { + translationNotices.push({ + type: NotificationType.MACHINE_TRANSLATION, + content: t('notices.machine_translation'), + onClose: () => { + setMachineTranslationDismissed(true) + try { + Cookies.set(MACHINE_TRANSLATION_BANNER_COOKIE_NAME, 'true') + } catch (err) { + console.warn('Unable to set cookie', err) + } + }, + }) + } + const releaseNotices: Array = [] if (currentVersion === data.variables.release_candidate.version) { releaseNotices.push({ @@ -100,6 +124,7 @@ export const HeaderNotifications = () => { styles.container, 'text-center f5 color-fg-default py-4 px-6 z-1', type === NotificationType.TRANSLATION && 'color-bg-accent', + type === NotificationType.MACHINE_TRANSLATION && 'color-bg-accent', type === NotificationType.RELEASE && 'color-bg-accent', type === NotificationType.EARLY_ACCESS && 'color-bg-danger', !isLast && 'border-bottom color-border-default', diff --git a/src/frame/lib/constants.ts b/src/frame/lib/constants.ts index 626fbf5f1239..7b0fae90fedf 100644 --- a/src/frame/lib/constants.ts +++ b/src/frame/lib/constants.ts @@ -12,6 +12,7 @@ const DEFAULT_MAX_REQUEST_TIMEOUT = isDev ? 15_000 : 10_000 export const ROOT = process.env.ROOT || '.' export const USER_LANGUAGE_COOKIE_NAME = 'user_language' +export const MACHINE_TRANSLATION_BANNER_COOKIE_NAME = 'machine_translation_banner_seen' export const USER_VERSION_COOKIE_NAME = 'user_version' export const TRANSLATIONS_ROOT = process.env.TRANSLATIONS_ROOT || 'translations' export const MAX_REQUEST_TIMEOUT = process.env.REQUEST_TIMEOUT