From 23b6ab8f12531558895c1c94ca6d1acb4a805096 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Tue, 24 Jun 2025 14:53:07 -0700 Subject: [PATCH 1/7] Remove redundant purge-old-workflow-runs workflow (#56206) --- .github/workflows/purge-old-workflow-runs.yml | 35 --- package.json | 1 - src/workflows/purge-old-workflow-runs.ts | 241 ------------------ 3 files changed, 277 deletions(-) delete mode 100644 .github/workflows/purge-old-workflow-runs.yml delete mode 100755 src/workflows/purge-old-workflow-runs.ts diff --git a/.github/workflows/purge-old-workflow-runs.yml b/.github/workflows/purge-old-workflow-runs.yml deleted file mode 100644 index 487ac78b9011..000000000000 --- a/.github/workflows/purge-old-workflow-runs.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Purge old workflow runs - -# **What it does**: Deletes really old workflow runs. -# **Why we have it**: To keep things neat and tidy. -# **Who does it impact**: Docs engineering. - -on: - workflow_dispatch: - schedule: - - cron: '20 * * * *' # Run every hour at 20 minutes past the hour - -permissions: - contents: write - -jobs: - purge-old-workflow-runs: - if: ${{ github.repository == 'github/docs-internal' || github.repository == 'github/docs' }} - runs-on: ubuntu-latest - steps: - - name: Checkout out repo - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - uses: ./.github/actions/node-npm-setup - - - name: Run purge script - env: - # Necessary to be able to delete deployment environments - GITHUB_TOKEN: ${{ secrets.DOCS_BOT_PAT_BASE }} - run: npm run purge-old-workflow-runs - - - uses: ./.github/actions/slack-alert - if: ${{ failure() && github.event_name != 'workflow_dispatch' }} - with: - slack_channel_id: ${{ secrets.DOCS_ALERTS_SLACK_CHANNEL_ID }} - slack_token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }} diff --git a/package.json b/package.json index e25f27544dcc..8d36f98a9677 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "prevent-pushes-to-main": "tsx src/workflows/prevent-pushes-to-main.ts", "purge-fastly-edge-cache": "tsx src/workflows/purge-fastly-edge-cache.ts", "purge-fastly-edge-cache-per-language": "tsx src/languages/scripts/purge-fastly-edge-cache-per-language.js", - "purge-old-workflow-runs": "tsx src/workflows/purge-old-workflow-runs.js", "ready-for-docs-review": "tsx src/workflows/ready-for-docs-review.ts", "release-banner": "tsx src/ghes-releases/scripts/release-banner.ts", "repo-sync": "./src/workflows/local-repo-sync.sh", diff --git a/src/workflows/purge-old-workflow-runs.ts b/src/workflows/purge-old-workflow-runs.ts deleted file mode 100755 index 4594a130be96..000000000000 --- a/src/workflows/purge-old-workflow-runs.ts +++ /dev/null @@ -1,241 +0,0 @@ -/** - * - * The only mandatory environment variables for this scrips are: - * - * - GITHUB_TOKEN - * - GITHUB_REPOSITORY (e.g. "github/docs") - * - * To delete old workflows, it first downloads all the workflows. - * The list of workflows is sorted by: A) does the `workflow.path` - * exist in the repo any more, B) each workflow's `updated_at` date. - * - * Then, one workflow at a time, it searches that workflow for runs. - * The search for runs uses a `created` filter that depends on the - * `MIN_AGE_DAYS` environment variable. The default is 90 days. - * - * For every run found, it deletes its logs and its run. - * - * The total number of deletions is limited by the `MAX_DELETIONS` - * environment variable. The default is 500. - * */ - -import fs from 'fs' -import assert from 'node:assert/strict' - -import { getOctokit } from '@actions/github' - -main() -async function main() { - const DRY_RUN = Boolean(JSON.parse(process.env.DRY_RUN || 'false')) - const MAX_DELETIONS = parseInt(JSON.parse(process.env.MAX_DELETIONS || '500')) - const MIN_AGE_DAYS = parseInt(process.env.MIN_AGE_DAYS || '90', 10) - - const [owner, repo] = process.env.GITHUB_REPOSITORY?.split('/') || [] - if (!owner || !repo) { - throw new Error('GITHUB_REPOSITORY environment variable not set') - } - const token = process.env.GITHUB_TOKEN - if (!token) { - throw new Error(`GITHUB_TOKEN environment variable not set`) - } - const github = getOctokit(token) - - // The sort order is not explicitly listed for this API endpoint. - // In practice it appears to list those that are oldest first. - // But to guarantee that it reaches the oldest, we paginate over - // all of them. - let allWorkflows = [] - - try { - allWorkflows = await github.paginate('GET /repos/{owner}/{repo}/actions/workflows', { - owner, - repo, - }) - } catch (error: any) { - console.log('Error happened when getting workflows') - console.warn('Status: %O', error.status) - console.warn('Message: %O', error.message) - - // Generally, if it fails, it's because of a network error or - // because busy servers. It's not our fault, but considering that - // this script is supposed to run on frequent schedule, we don't - // need to fret. We'll just try again next time. - if (isOperationalError(error.status, error.message)) { - return - } else { - throw error - } - } - - const validWorkflows = allWorkflows.filter((w) => !w.path.startsWith('dynamic/')) - - const sortByDate = (a: Record, b: Record) => - a.updated_at.localeCompare(b.updated_at) - const workflows = [ - ...validWorkflows.filter((w) => !fs.existsSync(w.path)).sort(sortByDate), - ...validWorkflows.filter((w) => fs.existsSync(w.path)).sort(sortByDate), - ] - - let deletions = 0 - for (const workflow of workflows) { - console.log('WORKFLOW', workflow) - console.log( - fs.existsSync(workflow.path) - ? `${workflow.path} still exists on disk` - : `${workflow.path} no longer exists on disk`, - ) - try { - deletions += await deleteWorkflowRuns(github, owner, repo, workflow, { - dryRun: DRY_RUN, - minAgeDays: MIN_AGE_DAYS, - maxDeletions: MAX_DELETIONS - deletions, - }) - } catch (error: any) { - console.log("Error happened when calling 'deleteWorkflowRuns'") - console.warn('Status: %O', error.status) - console.warn('Message: %O', error.message) - - // Generally, if it fails, it's because of a network error or - // because busy servers. It's not our fault, but considering that - // this script is supposed to run on frequent schedule, we don't - // need to fret. We'll just try again next time. - if (isOperationalError(error.status, error.message)) { - break - } else { - throw error - } - } - - if (deletions >= MAX_DELETIONS) { - console.log(`Reached max number of deletions: ${MAX_DELETIONS}`) - break - } - } - console.log(`Deleted ${deletions} runs in total`) -} - -function isOperationalError(status: number, message: string) { - if (status && status >= 500) { - return true - } - if (/Unable to delete logs while the workflow is running/.test(message)) { - return true - } - if (status === 403 && /API rate limit exceeded/.test(message)) { - return true - } - - return false -} - -async function deleteWorkflowRuns( - github: ReturnType, - owner: string, - repo: string, - workflow: Record, - { dryRun = false, minAgeDays = 90, maxDeletions = 500 }, -) { - // https://docs.github.com/en/search-github/getting-started-with-searching-on-github/understanding-the-search-syntax#query-for-dates - const minCreated = new Date(Date.now() - minAgeDays * 24 * 60 * 60 * 1000) - const minCreatedSearch = `<${minCreated.toISOString().split('T')[0]}` - // Delete is 10, but max is 100. But if we're only going to delete, - // 30, use 30. And if we're only going to delete 5, use the default - // per_page value of 10. - const perPage = Math.max(100, Math.max(10, maxDeletions)) - // We could use github.paginate(...) but given that we can use a - // filter on `created` and we can set a decent `per_page`, there's no - // reason to request data that we're not going to use. - const { data } = await github.request( - 'GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs', - { - owner, - repo, - workflow_id: workflow.id, - per_page: perPage, - created: minCreatedSearch, - }, - ) - const runs = data.workflow_runs || [] - console.log( - `Total runs in workflow "${ - workflow.name - }" (${minCreatedSearch}): ${(data.total_count || 0).toLocaleString()}`, - ) - - let deletions = 0 - let notDeletions = 0 - for (const run of runs) { - const created = new Date(run.created_at) - if (created < minCreated) { - const ageDays = Math.round((Date.now() - created.getTime()) / (24 * 60 * 60 * 1000)) - console.log( - 'DELETE', - { - id: run.id, - created_at: run.created_at, - name: run.name, - display_title: run.display_title, - }, - `${ageDays} days old`, - ) - - if (!dryRun) { - try { - const { status } = await github.request( - 'DELETE /repos/{owner}/{repo}/actions/runs/{run_id}/logs', - { - owner, - repo, - run_id: run.id, - }, - ) - assert(status === 204, `Unexpected status deleting logs for run ${run.id}: ${status}`) - } catch (error: any) { - console.warn('ERROR trying to delete the logs for run', run.id, error.message) - if (error.message && error.message.includes('API rate limit exceeded')) { - // This can not be recovered by continuing on to the next run. - break - } - } - } - - if (!dryRun) { - try { - const { status } = await github.request( - 'DELETE /repos/{owner}/{repo}/actions/runs/{run_id}', - { - owner, - repo, - run_id: run.id, - }, - ) - assert(status === 204, `Unexpected status deleting logs for run ${run.id}: ${status}`) - } catch (error: any) { - console.warn('ERROR trying to delete run', run.id, error.message) - if (error.message && error.message.includes('API rate limit exceeded')) { - // This can not be recovered by continuing on to the next run. - break - } - } - } - - deletions++ - if (maxDeletions && deletions >= maxDeletions) { - console.log( - `Reached max number of deletions (${maxDeletions}) for one workflow: ${workflow.name}`, - ) - break - } else { - console.log(`Deleted ${deletions} of ${maxDeletions} runs for workflow: ${workflow.name}`) - } - } else { - notDeletions++ - } - } - console.log(`Deleted ${deletions} runs in total for workflow: ${workflow.name}`) - if (notDeletions) { - console.log(`Skipped ${notDeletions} runs for workflow: ${workflow.name}`) - } - - return deletions -} From 42a9f5f74112c8ab793c2e5535308407fd8e2437 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Tue, 24 Jun 2025 14:53:34 -0700 Subject: [PATCH 2/7] Add missing GHD linter rules to configuration (#56279) --- .../contributing/content-linter-rules.md | 6 ++++- src/content-linter/style/github-docs.js | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/data/reusables/contributing/content-linter-rules.md b/data/reusables/contributing/content-linter-rules.md index 9f74e0121553..5362e5701388 100644 --- a/data/reusables/contributing/content-linter-rules.md +++ b/data/reusables/contributing/content-linter-rules.md @@ -31,6 +31,7 @@ | [search-replace](https://github.com/OnkarRuikar/markdownlint-rule-search-replace) | deprecated liquid syntax: octicon- | The octicon liquid syntax used is deprecated. Use this format instead `octicon "" aria-label=""` | error | | | [GH001](https://github.com/github/markdownlint-github/blob/main/docs/rules/GH001-no-default-alt-text.md) | no-default-alt-text | Images should have meaningful alternative text (alt text) | error | accessibility, images | | [GH002](https://github.com/github/markdownlint-github/blob/main/docs/rules/GH002-no-generic-link-text.md) | no-generic-link-text | Avoid using generic link text like `Learn more` or `Click here` | error | accessibility, links | +| GHD030 | code-fence-line-length | Code fence lines should not exceed a maximum length | warning | code, accessibility | | GHD032 | image-alt-text-end-punctuation | Alternate text for images should end with punctuation | error | accessibility, images | | GHD004 | image-file-kebab-case | Image file names must use kebab-case | error | images | | GHD033 | incorrect-alt-text-length | Images alternate text should be between 40-150 characters | warning | accessibility, images | @@ -65,4 +66,7 @@ | GHD041 | third-party-action-pinning | Code examples that use third-party actions must always pin to a full length commit SHA | error | feature, actions | | GHD042 | liquid-tag-whitespace | Liquid tags should start and end with one whitespace. Liquid tag arguments should be separated by only one whitespace. | error | liquid, format | | GHD043 | link-quotation | Internal link titles must not be surrounded by quotations | error | links, url | -| GHD044 | octicon-aria-labels | Octicons should always have an aria-label attribute even if aria-hidden. | warning | accessibility, octicons | \ No newline at end of file +| GHD044 | octicon-aria-labels | Octicons should always have an aria-label attribute even if aria-hidden. | warning | accessibility, octicons | +| GHD048 | british-english-quotes | Periods and commas should be placed inside quotation marks (American English style) | warning | punctuation, quotes, style, consistency | +| GHD050 | multiple-emphasis-patterns | Do not use more than one emphasis/strong, italics, or uppercase for a string | warning | formatting, emphasis, style | +| GHD049 | note-warning-formatting | Note and warning tags should be formatted according to style guide | warning | formatting, callouts, notes, warnings, style | \ No newline at end of file diff --git a/src/content-linter/style/github-docs.js b/src/content-linter/style/github-docs.js index 9cf4cf591419..4cc8cd2f7ae2 100644 --- a/src/content-linter/style/github-docs.js +++ b/src/content-linter/style/github-docs.js @@ -101,6 +101,12 @@ const githubDocsConfig = { 'partial-markdown-files': true, 'yml-files': true, }, + 'code-fence-line-length': { + // GHD030 + severity: 'warning', + 'partial-markdown-files': true, + 'yml-files': true, + }, 'image-alt-text-exclude-words': { // GHD031 severity: 'error', @@ -174,6 +180,24 @@ const githubDocsConfig = { 'partial-markdown-files': true, 'yml-files': true, }, + 'british-english-quotes': { + // GHD048 + severity: 'warning', + 'partial-markdown-files': true, + 'yml-files': true, + }, + 'note-warning-formatting': { + // GHD049 + severity: 'warning', + 'partial-markdown-files': true, + 'yml-files': true, + }, + 'multiple-emphasis-patterns': { + // GHD050 + severity: 'warning', + 'partial-markdown-files': true, + 'yml-files': true, + }, } export const githubDocsFrontmatterConfig = { From 070de2440200c7e622e56a59bdf35b7efc9fc7d6 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Tue, 24 Jun 2025 14:54:41 -0700 Subject: [PATCH 3/7] Configure faster teardown timeout for Vitest tests (#56282) --- vitest.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/vitest.config.ts b/vitest.config.ts index 332e0c3611ce..d9c2a9e37e4c 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -12,5 +12,6 @@ export default { }, globalSetup: './src/tests/vitest.setup.ts', + teardownTimeout: 500, }, } From eed0a10b00c49e00c963308eedb3815c122c2565 Mon Sep 17 00:00:00 2001 From: "T. Greg Doucette" <58960990+LawDevNull@users.noreply.github.com> Date: Tue, 24 Jun 2025 18:45:59 -0400 Subject: [PATCH 4/7] Tweak Pre-Release Terms to address common pain points (#56274) Co-authored-by: Alex Nguyen <150945400+nguyenalex836@users.noreply.github.com> --- .../github-pre-release-license-terms.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/content/site-policy/github-terms/github-pre-release-license-terms.md b/content/site-policy/github-terms/github-pre-release-license-terms.md index d404379bb1a3..7ad3c61267a0 100644 --- a/content/site-policy/github-terms/github-pre-release-license-terms.md +++ b/content/site-policy/github-terms/github-pre-release-license-terms.md @@ -18,7 +18,7 @@ These terms apply to the pre-release software made available to you by GitHub. T ## 1. Pre-Release Software. - The software provided is a pre-release version. “Pre-release” means software, online services, and additional products and features that are provided for preview, evaluation, demonstration, or trial purposes, as well as prerelease versions of those such as alpha, beta, or early access versions. + The software provided is a pre-release version. “Pre-release” means software, online services, and additional products and features that are not yet generally available, such as alpha, beta, early access, technical preview, or similar versions. Pre-release software may not operate correctly. It may delete your data, corrupt your data, or have other bugs. It also may not work the way a final commercial version of the software will. @@ -54,9 +54,17 @@ These terms apply to the pre-release software made available to you by GitHub. T ## 4. Data Collection and Usage. - a. **Consent to Data Collection.** The pre-release software may collect information about you and your use of the software, and send that information to GitHub. GitHub may use this information to provide services, to improve our products and services, or for any other purpose permitted under the GitHub Data Protection Agreement or GitHub Privacy Statement. Your use of the pre-release software operates as your consent to these practices. - - b. **Use of Collected Data.** GitHub will use collected data for analytics and measurement to understand how our pre-release software and related products are used. The software will collect data and usage information about events generated when interacting with it. These events help us analyze and measure performance and features used. This usage information is used by GitHub and may be shared with affiliates and other third parties to help deliver, develop, evaluate, and improve the software and related products. We analyze data to ensure the pre-release software is working as intended, to evaluate the safety, reliability, and user experience of the software, and to investigate and detect potential abuse. We may combine the information we collect from the pre-release software with other data. + a. **Consent to Data Collection.** The pre-release software may collect telemetry information about you and your use of the software, and send that information to GitHub. Subject to the limitations in Section 4(b) below, GitHub may use this information to provide services, to improve our products and services, or for any other purpose permitted under the GitHub Data Protection Agreement or GitHub Privacy Statement. Your use of the pre-release software operates as your consent to these practices. + + b. **Use of Collected Data.** + * GitHub will use collected data for analytics and measurement to understand how our pre-release software and related products are used. + * The software will collect data and usage information about events generated when interacting with it. These events help us analyze and measure performance and features used. This usage information is used by GitHub and may be shared with affiliates and other third parties to help deliver, develop, evaluate, and improve the software and related products. + * We analyze data to ensure the pre-release software is working as intended, to evaluate the safety, reliability, and user experience of the software, and to investigate and detect potential abuse. + * We may combine the information we collect from the pre-release software with other data. + * For pre-release software that uses AI: + * You retain ownership of the code that you input to the software. + * GitHub does not own the output sent to you by the software. + * GitHub will not use your inputs or the outputs generated to train AI language models, unless you have instructed us in writing to do so. c. **Processing of Personal Data.** GitHub is the data controller in relation to the Personal Data processed in connection with the pre-release software. From aff9acc75b0b2717c5bb5e2309c26cc1e0edb12a Mon Sep 17 00:00:00 2001 From: Evan Bonsignori Date: Tue, 24 Jun 2025 16:42:06 -0700 Subject: [PATCH 5/7] add loading to results & adjust references display (#56289) --- src/search/components/input/AskAIResults.tsx | 3 +-- src/search/components/input/SearchOverlay.tsx | 22 ++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/search/components/input/AskAIResults.tsx b/src/search/components/input/AskAIResults.tsx index 4b1d6afc84fe..41351a8ba694 100644 --- a/src/search/components/input/AskAIResults.tsx +++ b/src/search/components/input/AskAIResults.tsx @@ -481,9 +481,8 @@ export function AskAIResults({ > ) : null} - {!aiCouldNotAnswer && references && references.length > 0 ? ( + {!aiCouldNotAnswer && !responseLoading && references && references.length > 0 ? ( <> -