From 74526f579afba894c3b98b69003c751aa40be771 Mon Sep 17 00:00:00 2001 From: subatoi <32935794+subatoi@users.noreply.github.com> Date: Mon, 5 Oct 2026 14:45:25 +0000 Subject: [PATCH 01/19] Close OS PRs not targeting main on a schedule (#63679) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .../close-prs-not-targeting-main.yml | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 .github/workflows/close-prs-not-targeting-main.yml diff --git a/.github/workflows/close-prs-not-targeting-main.yml b/.github/workflows/close-prs-not-targeting-main.yml new file mode 100644 index 000000000000..d615382985d3 --- /dev/null +++ b/.github/workflows/close-prs-not-targeting-main.yml @@ -0,0 +1,76 @@ +name: Reject PRs targeting non-default branch (scheduled) + +# Backup check for check-for-spammy-prs.yml: closes PRs that don't target main + +on: + schedule: + - cron: '20 16 * * 1-5' + workflow_dispatch: + +permissions: + contents: read + pull-requests: write + +jobs: + reject-non-main-prs: + name: Close PRs that don't target main + if: github.repository == 'github/docs' + runs-on: ubuntu-latest + steps: + - name: Checkout repository code + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 + with: + github-token: ${{ secrets.DOCS_BOT_PAT_BASE }} + script: | + const owner = 'github' + const repo = 'docs' + + const openPulls = await github.paginate(github.rest.pulls.list, { + owner, + repo, + state: 'open', + per_page: 100, + }) + + for (const pr of openPulls) { + if (pr.base.ref === 'main') continue + if (pr.user.login === 'docs-bot') continue + + const labels = pr.labels.map(l => l.name) + if (!labels.includes('invalid')) { + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: pr.number, + labels: ['invalid'], + }) + + await github.rest.issues.createComment({ + owner, + repo, + issue_number: pr.number, + body: 'Pull requests must target the `main` branch. This pull request has been closed as invalid. If you think this is incorrect, please mention this in a corresponding issue.', + }) + } + + await github.rest.pulls.update({ + owner, + repo, + pull_number: pr.number, + state: 'closed', + }) + } + + - uses: ./.github/actions/create-workflow-failure-issue + id: create-failure-issue + if: ${{ failure() && github.event_name != 'workflow_dispatch' }} + with: + token: ${{ secrets.DOCS_BOT_PAT_BASE }} + + - uses: ./.github/actions/slack-alert + if: ${{ failure() && github.event_name != 'workflow_dispatch' }} + with: + slack_token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }} + issue_url: ${{ steps.create-failure-issue.outputs.issue_url }} From 53aea1e9d1f3c52647faaa36dd7ca0432bf8fbca Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 14:53:31 +0000 Subject: [PATCH 02/19] Update the GHES deprecation runbook from the 3.17 run (#63587) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 85e3c6f1-af4b-4bb9-abc4-a5fef82d83ef --- src/ghes-releases/lib/deprecation-steps.md | 89 +++++++++++++--------- 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/src/ghes-releases/lib/deprecation-steps.md b/src/ghes-releases/lib/deprecation-steps.md index f8b894131201..5e56316b8274 100644 --- a/src/ghes-releases/lib/deprecation-steps.md +++ b/src/ghes-releases/lib/deprecation-steps.md @@ -21,30 +21,24 @@ Throughout, `{{ release-number }}` stands for the version being deprecated. When - A callout appears in the steps: 🛑 **HUMAN**. This means stop and get explicit human sign-off before continuing. These mark points where a mistake is expensive or hard to undo, or actions only a human can perform. Text on these lines are meant for the human to do; any text on a line that does not have the flag is for the agent. - When a step is ambiguous or a tool misbehaves, look at the two most recent deprecation pull requests in `github/docs-internal` for how it was handled. Search for pull requests titled "Deprecate GitHub Enterprise Server", and read the content team's review comments so you can fix the same problems before requesting review. - Clone `github/docs-internal` and `github/github` before you begin. +- This issue copies the runbook when the issue is created. Before you start, compare it with [the runbook on `main`](https://github.com/github/docs-internal/blob/main/src/ghes-releases/lib/deprecation-steps.md). If they differ, follow `main`. +- If other people or agents share your `github/docs-internal` checkout, work in a `git worktree` instead of switching branches. ## Step 1: Confirm the deprecation date The release controller's Slack chatops are the authoritative source for both the deprecation date and the DRI (directly responsible individual): * `.rc get-cycle {{ release-number }}` returns the release cycle for `{{ release-number }}`, including the deprecation date. -* `.rc release-dates` returns the current DRI for each release. Run it in either `#ghes-releases` or `#ghes-release-ops`. +* `.rc release-dates` returns the current DRI for each release. Run it in either `#ghes-releases` or `#ghes-release-ops`. It may stop listing a release once its deprecation completes, so run it a few days before the deprecation date if you can. Draft both chatops and a follow-up confirmation message for the human, who runs the chatops in Slack and posts the confirmation. Cross-check the date returned by `.rc get-cycle` against the one in this issue's title and in `src/ghes-releases/lib/enterprise-dates.json`. 🛑 **HUMAN**: Run `.rc get-cycle {{ release-number }}` and `.rc release-dates` in `#ghes-releases` (or `#ghes-release-ops`). Post a short message in `#ghes-releases` `@`-mentioning the DRI returned by `.rc release-dates` asking them to confirm the deprecation date, and add a comment on this issue linking to that Slack message for posterity. If `.rc release-dates` doesn't return a DRI for `{{ release-number }}`, ask in `#docs-content-enterprise` or `#ghes-releases` instead. * If the date is being pushed out, ask the DRI to update the [release date list](https://github.com/github/enterprise-releases/blob/master/releases.json), update this issue's target date, and pause until the new date arrives. -* The release date list is often not updated. If it isn't, our copy in `src/ghes-releases/lib/enterprise-dates.json` may also be wrong. You fix that in Step 6. +* The release date list is often not updated. If it isn't, our copy in `src/ghes-releases/lib/enterprise-dates.json` may also be wrong. You fix that in Step 5. -## Step 2: Remove the version from the github/docs-content release tracker - -In the `github/docs-content` repository, remove `{{ release-number }}` from the `options` list in [`release-tracking.yml`](https://github.com/github/docs-content/blob/main/.github/ISSUE_TEMPLATE/release-tracking.yml), ensure the list of versions matches the available versions, and open a pull request. - -🛑 **HUMAN**: Review the `github/docs-content` pull request. Acknowledge this will need to be merged in the next few days. - -You can continue once the human reviews the `github/docs-content` pull request and acknowledges they are responsible for getting it merged. - -## Step 3: Clone the translation repositories +## Step 2: Clone the translation repositories The full scrape needs local clones of the eight translation repositories: @@ -54,7 +48,7 @@ npm run clone-translations This clones every language into `./translations/` inside your `github/docs-internal` checkout, which the scrape reads by default. No `.env` configuration is needed. It clones eight repositories and can take several minutes. To keep your clones elsewhere, use per-language `TRANSLATIONS_ROOT_*` variables instead, described in [the appendix](#reference-configuring-the-translation-repositories). -## Step 4: Create the archive repository +## Step 3: Create the archive repository Each deprecated version's docs live in their own repository, for example `github/docs-ghes-3.11`. Create the new one: @@ -66,19 +60,19 @@ npm run deprecate-ghes -- create-repo --version {{ release-number }} You can continue once the human has said they completed that step. -## Step 5: Create the deprecation branch +## Step 4: Create the deprecation branch -Create the branch that holds every `github/docs-internal` change in this deprecation. Keep it through Step 13: +Create the branch that holds every `github/docs-internal` change in this deprecation. Keep it through Step 12: ```shell git checkout -b deprecate-{{ release-number }} ``` -## Step 6: Fix the deprecation date if needed +## Step 5: Fix the deprecation date if needed If the date in the [release date list](https://github.com/github/enterprise-releases/blob/master/releases.json) differs from `src/ghes-releases/lib/enterprise-dates.json`, update `enterprise-dates.json` to match and commit it on your branch. The pre-deprecation banner reads its dates from that file, so fix it before scraping. -## Step 7: Dry-run the scrape +## Step 6: Dry-run the scrape Update your translation clones to the latest `main`. Then hide the search components so they don't get scraped into the static archive: in `src/frame/components/page-header/Header.tsx`, temporarily set the `SubdomainNavBar.Search` component's `className` to `"visually-hidden"`. Also wrap the `SearchOverlay` in `src/search/components/input/SearchOverlayContainer.tsx` in a `
`. @@ -89,15 +83,19 @@ npm run build npm run deprecate-ghes-archive -- --dry-run --local-dev ``` -Open a few HTML files in `tmpArchivalDir_{{ release-number }}` and confirm they render, styles load, and the version dropdowns work. +Serve the output over HTTP to review it. Opening the files directly doesn't work, because `--local-dev` asset paths are relative to the inner directory: + +```shell +python3 -m http.server 8080 --bind 127.0.0.1 -d tmpArchivalDir_{{ release-number }}/{{ release-number }} +``` -Offer to open the files for the human in their text editor or browser. +Confirm that a few pages in a couple of languages render, styles load, and the version dropdowns work. Offer to open the pages for the human in their browser. 🛑 **HUMAN**: Review the dry-run output before the full scrape. You can continue after the human confirms they have reviewed the dry-run output. -## Step 8: Run the full scrape +## Step 7: Run the full scrape Scrape every page. This takes 20-30 minutes and overwrites the dry-run output: @@ -111,15 +109,23 @@ Revert the search component edits: git checkout src/frame/components/page-header/Header.tsx src/search/components/input/SearchOverlayContainer.tsx ``` -## Step 9: Publish the archive +## Step 8: Publish the archive The scrape writes the publishable site to an inner directory, `tmpArchivalDir_{{ release-number }}/{{ release-number }}/`, which holds `en/`, the other language directories, and the redirect files. Copy the contents of that inner directory into the root of the `github/docs-ghes-{{ release-number }}` repository, so pages land at `/en/...` with no nested `{{ release-number }}/` directory. If you are unsure, please look at some previous `github/docs-ghes-*` repos for the right organization. +Before you ask the human, check the output: + +* No empty files: `find tmpArchivalDir_{{ release-number }} -type f -size 0`. The script deletes its output directory before every run, so re-scrape an empty page into a separate directory, for example `npm run deprecate-ghes-archive -- --page -o tmpRescrape`, and copy the new files over the empty ones. +* Each language has about the same number of HTML files as `en`. +* No error pages. Search the HTML for `Ooops!`. +* Pages show the deprecation banner and don't show search. +* Nothing links to `localhost:4001`, and assets point at `https://github.github.com/docs-ghes-{{ release-number }}/`. + 🛑 **HUMAN**: Confirm the file count, organization, and contents. -After the human confirms, commit and push. GitHub Pages builds the static site automatically. Wait a few minutes. Preview a few pages at the full Pages URL, for example `https://github.github.com/docs-ghes-{{ release-number }}/en/enterprise-server@{{ release-number }}/get-started/index.html`, across a couple of languages. Then remove `tmpArchivalDir_{{ release-number }}` from `github/docs-internal`. +After the human confirms, commit and push. The archive is about as large as the previous `github/docs-ghes-*` repository, so `git add` and the push can take a while. GitHub Pages builds the static site automatically. Wait a few minutes. Preview a few pages at the full Pages URL, for example `https://github.github.com/docs-ghes-{{ release-number }}/en/enterprise-server@{{ release-number }}/get-started/index.html`, across a couple of languages. Then remove `tmpArchivalDir_{{ release-number }}` from `github/docs-internal`. -## Step 10: Remove the version from github/docs-internal +## Step 9: Remove the version from github/docs-internal Back on your `deprecate-{{ release-number }}` branch, move the version out of the supported list. In `src/versions/lib/enterprise-server-releases.ts`, remove `'{{ release-number }}'` from `supported` and add it as the first element of `deprecatedWithFunctionalRedirects`. @@ -131,16 +137,21 @@ npm run deprecate-ghes -- content npm run lint-content -- --paths content data --rules liquid-ifversion-versions --fix ``` -Some `data/variables/*.yml` files can't be autofixed and show as lint errors. Open each one, find the key named in the error, and remove the deprecated Liquid while keeping the content for supported versions. +`deprecate-ghes -- content` rewrites the whole frontmatter of each file it changes, which can requote values, reflow long strings, and drop comments. Review its diff. In any file with frontmatter changes beyond `versions` or `children`, redo the edit by hand so only those lines change. + +Some YAML files can't be autofixed and show as lint errors. Open each one, find the key named in the error, and remove the deprecated Liquid while keeping the content for supported versions. Then clean up empty data files and run the linter again: ```shell npm run deprecate-ghes -- data -npm run lint-content -- --fix +files=$(git diff --name-only --diff-filter=d "$(git merge-base origin/main HEAD)" -- content data) +[ -n "$files" ] && npm run lint-content -- --fix --paths $files ``` -## Step 11: Clean up content artifacts +Pass `--paths`, because without it `lint-content` only lints uncommitted changes. The "Feature files with all versions" list from `deprecate-ghes -- data` is for information only. After each `--fix`, review the diff and restore changes the deprecation didn't cause. + +## Step 10: Clean up content artifacts The content team flags issues on every deprecation, so fix them before requesting review: @@ -155,7 +166,7 @@ The content team flags issues on every deprecation, so fix them before requestin The list in this step should increase after each deprecation to improve the output of this process and reduce human effort. -## Step 12: Fix CI the codemod doesn't touch +## Step 11: Fix CI the codemod doesn't touch The deprecation scripts only rewrite `content/` and `data/`, so version references in tests and fixtures can break CI. Run: @@ -163,9 +174,11 @@ The deprecation scripts only rewrite `content/` and `data/`, so version referenc npm test -- src/versions/tests src/redirects/tests ``` +Tests in other directories can also hardcode the version, so search the tests for `{{ release-number }}` and run any that reference it. + Fix any failures. For example, when a deprecation fully removes content rather than just a version of it, redirect fixtures in `src/fixtures/fixtures/rest-redirects.json` may still point at the removed version; repoint them at the current location. -## Step 13: Review, smoke test, and open the pull request +## Step 12: Review, smoke test, and open the pull request 🛑 **HUMAN**: Review the full diff before the smoke test. @@ -188,7 +201,7 @@ This pull request deprecates GHES {{ release-number }} on docs.github.com. ## For reviewers -[Step 11 of the runbook](https://github.com/github/docs-internal/blob/main/src/ghes-releases/lib/deprecation-steps.md#step-11-clean-up-content-artifacts) lists the content problems that recur. If you spot issues this pull request missed, please add to Step 11 so the next deprecation catches it. +[Step 10 of the runbook](https://github.com/github/docs-internal/blob/main/src/ghes-releases/lib/deprecation-steps.md#step-10-clean-up-content-artifacts) lists the content problems that recur. If you spot issues this pull request missed, please add to Step 10 so the next deprecation catches it. ``` ```` @@ -201,21 +214,21 @@ Offer to open the pull request in the human's browser. Once the humans approves the pull request, mark as ready for review. Add the pull request to the [docs-content review board](https://github.com/orgs/github/projects/2936/views/2). -🛑 **HUMAN**: Get a content team review before merging the pull request. You should add any issues the content team finds to Step 11 of this runbook. +🛑 **HUMAN**: Get a content team review before merging the pull request. You should add any issues the content team finds to Step 10 of this runbook. You can proceed once the human acknowledges they will need to get a content team review and handle merging the pull request and adding any feedback. You do not need to wait for the pull request to get a content team review or to be merged. -## Step 14: Tag the deprecation +## Step 13: Tag the deprecation -Tag `main` so we can find where in history the version was removed. You can tag now. There's no need to wait for the deprecation pull request to merge, which matches the recent deprecations. +Tag `main` so we can find the last point in history where the version was still supported. The [re-scraping appendix](#reference-re-scraping-a-page-or-all-pages) checks out this tag, so create it any time before the deprecation pull request merges. Tag `origin/main` and push only the new tag: ```shell -git checkout main && git pull -git tag enterprise-{{ release-number }}-deprecation -git push --tags --no-verify +git fetch origin main +git tag enterprise-{{ release-number }}-deprecation origin/main +git push --no-verify origin refs/tags/enterprise-{{ release-number }}-deprecation ``` -## Step 15: Deprecate the OpenAPI description in github/github +## Step 14: Deprecate the OpenAPI description in github/github In `github/github`, edit `app/api/description/config/releases/ghes-{{ release-number }}.yaml` and change `deprecated: false` to `deprecated: true`. Open a pull request and get the required code owner approvals. A docs-content team member can approve for the docs team. @@ -223,7 +236,7 @@ In `github/github`, edit `app/api/description/config/releases/ghes-{{ release-nu Continue on after the human acknowledges they will need to deploy the `github/github` pull request after the `github/docs-internal` pull request merges. -## Step 16: Capture process improvements +## Step 15: Capture process improvements Keep notes throughout the deprecation of anything wrong, slow, or confusing: runbook bugs, tooling failures, manual workarounds, and content-team feedback. When you finish, open a separate pull request, not part of the deprecation pull request, that fixes this runbook and the deprecation tooling for the next release. Write it for the next deprecation's agent. @@ -231,13 +244,13 @@ Keep notes throughout the deprecation of anything wrong, slow, or confusing: run After the human approves the process improvement pull request, you can continue. -## Step 17: Summarize +## Step 16: Summarize Summarize the work completed in this workflow, and link to each of the pull requests with the next action needed from the human. ## Reference: Configuring the translation repositories -`npm run clone-translations` from Step 3 is the simplest setup: it clones every language into `./translations/`, which the scrape reads by default with no extra configuration. +`npm run clone-translations` from Step 2 is the simplest setup: it clones every language into `./translations/`, which the scrape reads by default with no extra configuration. To keep your clones elsewhere instead, clone the repositories below and map each one with a `TRANSLATIONS_ROOT_*` variable in your `.env` file: From 43e20318bdd1b15e8dedae9bacd73eb39a359096 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 14:53:36 +0000 Subject: [PATCH 03/19] Fix GHD022 handling of ifversion not conditions (#63656) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3725887d-99f1-4760-b935-f3fe2443bbd6 --- .../liquid-ifversion-versions.ts | 24 +++++++------ .../tests/unit/liquid-ifversion-versions.ts | 36 ++++++++++++++++++- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts b/src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts index b1471c314e3c..3b24e453a0f9 100644 --- a/src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts +++ b/src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts @@ -9,9 +9,7 @@ import { } from '../helpers/liquid-utils' import { getFrontmatter, getFrontmatterLines } from '../helpers/utils' import getApplicableVersions from '@/versions/lib/get-applicable-versions' -import { allVersions } from '@/versions/lib/all-versions' import { difference } from 'lodash-es' -import { convertVersionsToFrontmatter } from '@/automated-pipelines/lib/update-markdown' import { isAllVersions, getFeatureVersionsObject, @@ -231,9 +229,14 @@ function setLiquidErrors(condTagItems: CondTagItem[], onError: RuleErrorCallback async function getApplicableVersionFromLiquidTag(conditionStr: string): Promise { const newConditionObject: VersionsObject = {} - const condition = conditionStr.replace('not ', '') - const liquidTagVersions = condition.split(' or ').map((item) => item.trim()) + const notProducts: string[] = [] + const liquidTagVersions = conditionStr.split(' or ').map((item) => item.trim()) for (const ver of liquidTagVersions) { + const notMatch = ver.match(/^not (fpt|ghec|ghes)$/) + if (notMatch) { + notProducts.push(notMatch[1]) + continue + } // Bare product and feature names, such as fpt or ghec, map directly to frontmatter versions. if (ver.split(' ').length === 1) { // Frontmatter represents one feature version at a time. @@ -266,13 +269,12 @@ async function getApplicableVersionFromLiquidTag(conditionStr: string): Promise< newConditionObject[version] = versionFmString } } - if (conditionStr.includes('not ')) { - const all = Object.keys(allVersions) - const allApplicable = getApplicableVersions(newConditionObject, '', { - doNotThrow: true, - includeNextVersion: true, - }) - return (await convertVersionsToFrontmatter(difference(all, allApplicable))) as VersionsObject + // The ifversion tag negates only the next product, so not fpt means every other product. + // Apply these after the loop so a range term such as ghes > 3.20 can't narrow them. + for (const notProduct of notProducts) { + for (const product of ['fpt', 'ghec', 'ghes']) { + if (product !== notProduct) newConditionObject[product] = '*' + } } return newConditionObject } diff --git a/src/content-linter/tests/unit/liquid-ifversion-versions.ts b/src/content-linter/tests/unit/liquid-ifversion-versions.ts index 17319eb7b650..35a30478a1f3 100644 --- a/src/content-linter/tests/unit/liquid-ifversion-versions.ts +++ b/src/content-linter/tests/unit/liquid-ifversion-versions.ts @@ -125,10 +125,11 @@ describe(liquidIfversionVersions.names.join(' - '), () => { expect(errors[0].ruleNames[0]).toBe('GHD022') }) - test.skip("ifversion using 'not' can't be tested", async () => { + test('ifversion using not negates only the next version', async () => { const markdown = [ ...placeholderAllVersionsFm, `{% ifversion ghes or fpt or not ghec %}{% endif %}`, + `{% ifversion not fpt or ghec %}{% endif %}`, ].join('\n') const result = await runRule(liquidIfversionVersions, { @@ -138,6 +139,39 @@ describe(liquidIfversionVersions.names.join(' - '), () => { expect(errors.length).toBe(0) }) + test('ifversion using not that covers all versions', async () => { + const markdown = [ + ...placeholderAllVersionsFm, + `{% ifversion not ghec or ghec %}{% endif %}`, + ].join('\n') + + const result = await runRule(liquidIfversionVersions, { + strings: { markdown }, + }) + const errors = result.markdown + expect(errors.length).toBe(2) + expect(errors[0].errorDetail).toContain('applies to all versions') + }) + + test('ifversion using not drops products missing from frontmatter', async () => { + const markdown = [ + '---', + 'title: "Hello"', + 'versions:', + ' fpt: "*"', + ' ghec: "*"', + '---', + `{% ifversion not fpt or ghec %}{% endif %}`, + ].join('\n') + + const result = await runRule(liquidIfversionVersions, { + strings: { markdown }, + }) + const errors = result.markdown + expect(errors.length).toBe(1) + expect(errors[0].fixInfo?.insertText).toBe('ifversion ghec') + }) + test('does not crash with nested if blocks inside ifversion', async () => { const markdown = [ ...placeholderAllVersionsFm, From 248ce073cac7f709b408f69446a8898b064dd6b7 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 14:53:40 +0000 Subject: [PATCH 04/19] Unskip GHD022 test for all-versions feature conditions (#63657) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3725887d-99f1-4760-b935-f3fe2443bbd6 --- src/content-linter/tests/unit/liquid-ifversion-versions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content-linter/tests/unit/liquid-ifversion-versions.ts b/src/content-linter/tests/unit/liquid-ifversion-versions.ts index 35a30478a1f3..9ce76fd52967 100644 --- a/src/content-linter/tests/unit/liquid-ifversion-versions.ts +++ b/src/content-linter/tests/unit/liquid-ifversion-versions.ts @@ -99,7 +99,7 @@ describe(liquidIfversionVersions.names.join(' - '), () => { expect(errors.length).toBe(0) }) - test.skip('ifversion using feature based version with all versions', async () => { + test('ifversion using feature based version with all versions', async () => { // features/them-and-all.yml covers all versions. const markdown = [...placeholderAllVersionsFm, `{% ifversion them-and-all %}{% endif %}`].join( '\n', From 94a53bb2aff25bd985c1852bc7a87321421ae0e1 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 14:53:45 +0000 Subject: [PATCH 05/19] Remove unused validateIfversionConditionalsVersions (#63658) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3725887d-99f1-4760-b935-f3fe2443bbd6 --- .../lib/linting-rules/liquid-versioning.ts | 101 ----------------- .../tests/unit/liquid-ifversion-versions.ts | 106 ------------------ 2 files changed, 207 deletions(-) diff --git a/src/content-linter/lib/linting-rules/liquid-versioning.ts b/src/content-linter/lib/linting-rules/liquid-versioning.ts index 18dc5fe79c16..1751fb0fca7c 100644 --- a/src/content-linter/lib/linting-rules/liquid-versioning.ts +++ b/src/content-linter/lib/linting-rules/liquid-versioning.ts @@ -1,4 +1,3 @@ -import semver from 'semver' import { TokenKind } from 'liquidjs' import type { TagToken } from 'liquidjs' import { addError } from 'markdownlint-rule-helpers' @@ -8,7 +7,6 @@ import { allVersions, allVersionShortnames } from '@/versions/lib/all-versions' import { supported, next, nextNext, deprecated } from '@/versions/lib/enterprise-server-releases' import allowedVersionOperators from '@/content-render/liquid/ifversion-supported-operators' import { getDeepDataByLanguage } from '@/data-directory/lib/get-data' -import getApplicableVersions from '@/versions/lib/get-applicable-versions' import { getLiquidTokens, getPositionData } from '../helpers/liquid-utils' import type { RuleParams, RuleErrorCallback } from '@/content-linter/types' @@ -29,15 +27,6 @@ const getAllFeatures = memoize( (): AllFeatures => getDeepDataByLanguage('features', 'en', process.env.ROOT) as AllFeatures, ) -const allVersionNames: string[] = Object.keys(allVersions) - -function isAllVersions(versions: string[]): boolean { - if (versions.length === allVersionNames.length) { - return versions.every((version) => allVersionNames.includes(version)) - } - return false -} - function memoize(func: () => T): () => T { let cached: T | null = null return (): T => { @@ -182,93 +171,3 @@ function validateIfversionConditionals(cond: string, possibleVersionNames: Set = {} - let hasFutureLessThan: boolean = false - for (const part of cond.split(/\sor\s/)) { - if (/(^|\s)not(\s|$)/.test(part)) { - // Skip combined-version checks for "not", because this rule does not implement inversion. - return [] - } - for (const [ver, value] of Object.entries(getVersionsObject(part.trim(), allFeatures))) { - // Less-than ranges can match upcoming GHES releases, so avoid flagging them as always true. - if (/<=?[\d.]+/.test(value)) { - hasFutureLessThan = true - } - - if (ver in versions) { - versions[ver] = lowestVersion(value, versions[ver]) - } else { - versions[ver] = value - } - } - } - - const applicableVersions: string[] = [] - try { - applicableVersions.push(...getApplicableVersions(versions)) - } catch { - // A rejected range leaves applicableVersions empty, so this check reports no error. - } - - if (isAllVersions(applicableVersions) && !hasFutureLessThan) { - errors.push( - `The Liquid ifversion condition '${cond}' includes all possible versions and will always be true`, - ) - } - return errors -} - -function getVersionsObject(part: string, allFeatures: AllFeatures): Record { - const versions: Record = {} - if (part in allFeatures) { - for (const [shortName, version] of Object.entries(allFeatures[part].versions)) { - const versionOperator: string = - version in allFeatures - ? Object.values(getVersionsObject(version, allFeatures))[0] || '*' - : (version as string) - if (shortName in versions) { - versions[shortName] = lowestVersion(versionOperator, versions[shortName]) - } else { - versions[shortName] = versionOperator - } - } - } else if (allShortnames.includes(part)) { - versions[part] = '*' - } else if (allShortnames.some((v) => part.startsWith(v))) { - const shortNamed = allShortnames.find((v) => part.startsWith(v)) - if (shortNamed) { - const rest = part.replace(shortNamed, '').trim() - versions[shortNamed] = rest - } - } else { - throw new Error(`The version '${part}' is neither a short version name or a feature name`) - } - return versions -} - -function lowestVersion(version1: string, version2: string): string { - if (version1 === '*' || version2 === '*') { - return '*' - } - const min1 = semver.minVersion(version1) - const min2 = semver.minVersion(version2) - if (min1 && min2 && semver.lt(min1, min2)) { - return version1 - } else { - return version2 - } -} diff --git a/src/content-linter/tests/unit/liquid-ifversion-versions.ts b/src/content-linter/tests/unit/liquid-ifversion-versions.ts index 9ce76fd52967..fb7a00570f4c 100644 --- a/src/content-linter/tests/unit/liquid-ifversion-versions.ts +++ b/src/content-linter/tests/unit/liquid-ifversion-versions.ts @@ -1,18 +1,9 @@ import { afterAll, beforeAll, describe, expect, test } from 'vitest' import { runRule } from '../../lib/init-test' -import { validateIfversionConditionalsVersions } from '../../lib/linting-rules/liquid-versioning' import { liquidIfversionVersions } from '../../lib/linting-rules/liquid-ifversion-versions' import { supported } from '@/versions/lib/enterprise-server-releases' -type FeatureVersions = { - versions: { - [key: string]: string - } -} - -type AllFeatures = Record - describe(liquidIfversionVersions.names.join(' - '), () => { const envVarValueBefore: string | undefined = process.env.ROOT @@ -241,100 +232,3 @@ describe(liquidIfversionVersions.names.join(' - '), () => { expect(errors.length).toBe(0) }) }) - -describe.skip('test validateIfversionConditionalsVersions function', () => { - test('most basic example without feature', () => { - const condition = 'ghes or ghec or fpt' - const allFeatures: AllFeatures = {} - const errors = validateIfversionConditionalsVersions(condition, allFeatures) - expect(errors.length).toBe(1) - }) - test('most basic example with feature', () => { - const condition = 'some-feature' - const allFeatures: AllFeatures = { - 'some-feature': { - versions: { - ghec: '*', - fpt: '*', - ghes: '*', - }, - }, - } - const errors = validateIfversionConditionalsVersions(condition, allFeatures) - expect(errors.length).toBe(1) - }) - test("any 'and' always yields no errors", () => { - const condition = 'ghes and ghec or fpt' - const allFeatures: AllFeatures = {} - const errors = validateIfversionConditionalsVersions(condition, allFeatures) - expect(errors.length).toBe(0) - }) - test("any 'not' always yields no errors", () => { - const condition = 'ghes or ghec or not fpt' - const allFeatures: AllFeatures = {} - const errors = validateIfversionConditionalsVersions(condition, allFeatures) - expect(errors.length).toBe(0) - }) - test('combined with feature it is all versions', () => { - const condition = 'ghec or fpt or some-feature' - const allFeatures: AllFeatures = { - 'some-feature': { - versions: { - ghes: `>=${supported.at(-1)}`, - }, - }, - } - const errors = validateIfversionConditionalsVersions(condition, allFeatures) - expect(errors.length).toBe(1) - }) - test('less or equal than a future version', () => { - const condition = 'ghec or fpt or some-feature' - const latestToday = parseFloat(supported.at(-1)!) - const allFeatures: AllFeatures = { - 'some-feature': { - versions: { - ghes: `<=${latestToday + 0.1}`, - }, - }, - } - const errors = validateIfversionConditionalsVersions(condition, allFeatures) - expect(errors.length).toBe(0) - }) - test('less than a future version', () => { - const condition = 'ghec or fpt or some-feature' - const latestToday = parseFloat(supported.at(-1)!) - const allFeatures: AllFeatures = { - 'some-feature': { - versions: { - ghes: `<${latestToday + 0.1}`, - }, - }, - } - const errors = validateIfversionConditionalsVersions(condition, allFeatures) - expect(errors.length).toBe(0) - }) - test('combined with feature it is eventually all versions (1)', () => { - const condition = `ghec or fpt or ghes >${supported.at(-1)} or some-feature` - const allFeatures: AllFeatures = { - 'some-feature': { - versions: { - ghes: `>=${supported.at(-1)}`, - }, - }, - } - const errors = validateIfversionConditionalsVersions(condition, allFeatures) - expect(errors.length).toBe(1) - }) - test('combined with feature it is eventually all versions (2)', () => { - const condition = `ghec or fpt or ghes >=${supported.at(-1)} or some-feature` - const allFeatures: AllFeatures = { - 'some-feature': { - versions: { - ghes: `>${supported.at(-1)}`, - }, - }, - } - const errors = validateIfversionConditionalsVersions(condition, allFeatures) - expect(errors.length).toBe(1) - }) -}) From c5a7a291ecb762804d9ff51902d5584be2b219b4 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 14:53:50 +0000 Subject: [PATCH 06/19] Flag GHD022 feature conditions that cover all versions with products (#63660) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3725887d-99f1-4760-b935-f3fe2443bbd6 --- .../liquid-ifversion-versions.ts | 38 ++++++-- .../tests/unit/liquid-ifversion-versions.ts | 87 +++++++++++++++++-- .../data/features/cloud-and-capped-ghes.yml | 4 + .../data/features/cloud-and-older-ghes.yml | 4 + 4 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 src/fixtures/fixtures/data/features/cloud-and-capped-ghes.yml create mode 100644 src/fixtures/fixtures/data/features/cloud-and-older-ghes.yml diff --git a/src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts b/src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts index 3b24e453a0f9..e08d46a780ca 100644 --- a/src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts +++ b/src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts @@ -11,12 +11,12 @@ import { getFrontmatter, getFrontmatterLines } from '../helpers/utils' import getApplicableVersions from '@/versions/lib/get-applicable-versions' import { difference } from 'lodash-es' import { - isAllVersions, getFeatureVersionsObject, isInAllGhes, isGhesReleaseDeprecated, } from '@/ghes-releases/scripts/version-utils' -import { oldestSupported } from '@/versions/lib/enterprise-server-releases' +import { nextNext, oldestSupported } from '@/versions/lib/enterprise-server-releases' +import versionSatisfiesRange from '@/versions/lib/version-satisfies-range' import type { RuleParams, RuleErrorCallback } from '@/content-linter/types' // getLiquidIfVersionTokens exposes runtime properties that liquidjs TopLevelToken omits. @@ -328,6 +328,30 @@ async function initTagObject( return condTagItem } +function coversAllVersions(item: CondTagItem): boolean { + const options = { doNotThrow: true, includeNextVersion: true } + const products = Object.fromEntries( + Object.entries(item.versionsObj).filter(([key]) => key !== 'feature'), + ) + const sources: VersionsObject[] = [products, item.featureVersionsObj || {}] + const versions = new Set(sources.flatMap((source) => getApplicableVersions(source, '', options))) + const allApplicableVersions = getApplicableVersions( + { fpt: '*', ghec: '*', ghes: '*' }, + '', + options, + ) + // Ranges can only list known releases. An upper bound, such as < 3.25, = 3.24, or 3.20 - 3.24, + // can exclude future releases, so one lower-bound-only range must include the newest known release. + const coversFutureGhes = sources.some( + ({ ghes }) => + ghes === '*' || + (!!ghes && + /^(\s*>=?\s*\d+(\.\d+)*)+\s*$/.test(ghes) && + versionSatisfiesRange(nextNext, ghes)), + ) + return coversFutureGhes && allApplicableVersions.every((version) => versions.has(version)) +} + // Rather than filtering out items with no versions, give every item a blank // action and let updateConditionals decide which ones become delete or change. // setLiquidErrors turns the resulting actions into flaws later on. @@ -353,13 +377,9 @@ function updateConditionals(condTagItems: CondTagItem[]) { for (let i = 0; i < condTagItems.length - 1; i++) { const item = condTagItems[i] - // Collapse feature conditions that cover all versions. - if ( - isAllVersions( - item.featureVersionsObj || - ((item as unknown as { versionObj?: VersionsObject }).versionObj as VersionsObject), - ) - ) { + // Collapse feature conditions that cover all versions, including products named beside the feature. + // Check the union of versions, because a feature's ghes range must not hide a plain ghes. + if (item.featureVersionsObj && coversAllVersions(item)) { processConditionals(item, condTagItems, i) break } diff --git a/src/content-linter/tests/unit/liquid-ifversion-versions.ts b/src/content-linter/tests/unit/liquid-ifversion-versions.ts index fb7a00570f4c..106ac2bb5045 100644 --- a/src/content-linter/tests/unit/liquid-ifversion-versions.ts +++ b/src/content-linter/tests/unit/liquid-ifversion-versions.ts @@ -103,17 +103,94 @@ describe(liquidIfversionVersions.names.join(' - '), () => { expect(errors[0].ruleNames[0]).toBe('GHD022') }) - test.skip('ifversion using feature based version extended with shortname all versions', async () => { + test('ifversion using feature based version extended with shortname all versions', async () => { // features/volvo.yml contains fpt: "*" and ghec: "*". - const markdown = ` - {% ifversion volvo or ghes %}{% endif %} - ` + const markdown = [...placeholderAllVersionsFm, `{% ifversion volvo or ghes %}{% endif %}`].join( + '\n', + ) const result = await runRule(liquidIfversionVersions, { strings: { markdown }, }) const errors = result.markdown - expect(errors.length).toBe(1) + expect(errors.length).toBe(2) expect(errors[0].ruleNames[0]).toBe('GHD022') + expect(errors[0].errorDetail).toContain('applies to all versions') + }) + + test('ifversion using feature with a ghes range extended with ghes', async () => { + // features/cloud-and-older-ghes.yml contains fpt: "*", ghec: "*", and ghes: "<3.20". The + // bounded range never covers every GHES release, so only the plain ghes completes it. + const markdown = [ + ...placeholderAllVersionsFm, + `{% ifversion cloud-and-older-ghes or ghes %}{% endif %}`, + ].join('\n') + const result = await runRule(liquidIfversionVersions, { + strings: { markdown }, + }) + const errors = result.markdown + expect(errors.length).toBe(2) + expect(errors[0].errorDetail).toContain('applies to all versions') + }) + + test('ifversion using feature with a ghes range completed by a ghes range', async () => { + // features/cloud-and-older-ghes.yml contains ghes: "<3.20", so ghes >= 3.20 completes GHES. + const markdown = [ + ...placeholderAllVersionsFm, + `{% ifversion cloud-and-older-ghes or ghes >= 3.20 %}{% endif %}`, + ].join('\n') + const result = await runRule(liquidIfversionVersions, { + strings: { markdown }, + }) + const errors = result.markdown + expect(errors.length).toBe(2) + expect(errors[0].errorDetail).toContain('applies to all versions') + }) + + test('ifversion using feature with a ghes range and a ghes upper bound', async () => { + // Together the ranges cover every known release, but releases after 99.0 are missing. + const markdown = [ + ...placeholderAllVersionsFm, + `{% ifversion cloud-and-older-ghes or ghes >= 3.20 and ghes < 99.0 %}{% endif %}`, + ].join('\n') + const result = await runRule(liquidIfversionVersions, { + strings: { markdown }, + }) + const errors = result.markdown + expect( + errors.filter((error) => error.errorDetail?.includes('applies to all versions')), + ).toEqual([]) + }) + + test.each(['3.20 - 99.0', '^3.20'])( + 'ifversion using feature with a ghes range and implicit upper bound %s', + async (range) => { + // These ranges have no < but still exclude future releases, such as 100.0 or 4.0. + const markdown = [ + ...placeholderAllVersionsFm, + `{% ifversion cloud-and-older-ghes or ghes ${range} %}{% endif %}`, + ].join('\n') + const result = await runRule(liquidIfversionVersions, { + strings: { markdown }, + }) + const errors = result.markdown + expect( + errors.filter((error) => error.errorDetail?.includes('applies to all versions')), + ).toEqual([]) + }, + ) + + test('ifversion using feature with a ghes upper bound does not cover all versions', async () => { + // features/cloud-and-capped-ghes.yml contains fpt: "*", ghec: "*", and ghes: "<99.0". The + // range covers every current release but excludes future ones, so it is not all versions. + const markdown = [ + ...placeholderAllVersionsFm, + `{% ifversion cloud-and-capped-ghes %}{% endif %}`, + ].join('\n') + const result = await runRule(liquidIfversionVersions, { + strings: { markdown }, + }) + const errors = result.markdown + expect(errors.length).toBe(0) }) test('ifversion using not negates only the next version', async () => { diff --git a/src/fixtures/fixtures/data/features/cloud-and-capped-ghes.yml b/src/fixtures/fixtures/data/features/cloud-and-capped-ghes.yml new file mode 100644 index 000000000000..e846abe733ff --- /dev/null +++ b/src/fixtures/fixtures/data/features/cloud-and-capped-ghes.yml @@ -0,0 +1,4 @@ +versions: + fpt: '*' + ghec: '*' + ghes: '<99.0' diff --git a/src/fixtures/fixtures/data/features/cloud-and-older-ghes.yml b/src/fixtures/fixtures/data/features/cloud-and-older-ghes.yml new file mode 100644 index 000000000000..f5aad2d7cfad --- /dev/null +++ b/src/fixtures/fixtures/data/features/cloud-and-older-ghes.yml @@ -0,0 +1,4 @@ +versions: + fpt: '*' + ghec: '*' + ghes: '<3.20' From 8efb0b1cf8097ac6d44e32764e9c259ff7819d43 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 14:53:55 +0000 Subject: [PATCH 07/19] Remove skipped webhook payload example test (#63661) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3725887d-99f1-4760-b935-f3fe2443bbd6 --- src/webhooks/tests/rendering.ts | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/webhooks/tests/rendering.ts b/src/webhooks/tests/rendering.ts index f35df5099e4b..47248b3b5d34 100644 --- a/src/webhooks/tests/rendering.ts +++ b/src/webhooks/tests/rendering.ts @@ -54,25 +54,4 @@ describe('webhooks events and payloads', () => { const $lead = $(leadSelector) expect($lead.length).toBe(1) }) - - // Not all webhook types have examples in the schema yet. - describe.skip('rendering', () => { - test('every webhook event has at least one payload example', async () => { - const versions = Object.values(allVersions).map((value) => value.version) - - // nextUntil finds payload code blocks in later siblings, not only the next one. - for (const version of versions) { - const page = `/${version}/webhooks-and-events/webhooks/webhook-events-and-payloads` - const $ = await getDOM(page) - const payloadExampleElem = $('[id^=webhook-payload-example]') - - payloadExampleElem.each((i, elem) => { - const siblings = $(elem) - .nextUntil('[id^=webhook-payload-example]') - .filter((idx, sibling) => $(sibling).hasClass('height-constrained-code-block')) - expect(siblings.length).toBeGreaterThan(0) - }) - } - }) - }) }) From 8ec6dd16cfc16ed257080200f5eb3f00a6c7be78 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 14:54:00 +0000 Subject: [PATCH 08/19] Unskip sidebar custom link tests (#63662) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3725887d-99f1-4760-b935-f3fe2443bbd6 --- src/landings/tests/sidebar-custom-links.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/landings/tests/sidebar-custom-links.ts b/src/landings/tests/sidebar-custom-links.ts index f2d631dc78a4..7b2c9f0aef89 100644 --- a/src/landings/tests/sidebar-custom-links.ts +++ b/src/landings/tests/sidebar-custom-links.ts @@ -3,12 +3,12 @@ import { describe, expect, test } from 'vitest' import { getDOMCached as getDOM } from '@/tests/helpers/e2etest' describe('sidebar custom links', () => { - test.skip('page with sidebarLink frontmatter shows custom link in sidebar', async () => { + test('page with sidebarLink frontmatter shows custom link in sidebar', async () => { const $ = await getDOM('/get-started/sidebar-test') const customLink = $('[data-testid="sidebar"] a:contains("All sidebar test items")') expect(customLink.length).toBe(1) - expect(customLink.attr('href')).toBe('/get-started/sidebar-test') + expect(customLink.attr('href')).toBe('/en/get-started/sidebar-test') }) test('page without sidebarLink frontmatter does not show custom link', async () => { @@ -19,19 +19,19 @@ describe('sidebar custom links', () => { expect(customLinks.length).toBe(0) }) - test.skip('sidebarLink with custom text appears correctly', async () => { + test('sidebarLink with custom text appears correctly', async () => { const $ = await getDOM('/get-started/sidebar-test') const customLink = $('[data-testid="sidebar"] a:contains("All sidebar test items")') expect(customLink.text().trim()).toBe('All sidebar test items') }) - test.skip('sidebarLink appears in correct location within sidebar', async () => { + test('sidebarLink appears in correct location within sidebar', async () => { const $ = await getDOM('/get-started/sidebar-test') const customLink = $('[data-testid="sidebar"] a:contains("All sidebar test items")') expect(customLink.length).toBe(1) - expect(customLink.attr('href')).toBe('/get-started/sidebar-test') + expect(customLink.attr('href')).toBe('/en/get-started/sidebar-test') const testSection = customLink.closest('[role="group"], ul') const allLinks = testSection.find('a') From e8b0a578ec45b3aecddc9f6f899d3420969a0761 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 14:54:06 +0000 Subject: [PATCH 09/19] Fix and unskip category pages test (#63665) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3725887d-99f1-4760-b935-f3fe2443bbd6 --- content/admin/managing-iam/index.md | 1 + content/codespaces/about-codespaces/index.md | 1 + content/get-started/learning-to-code/index.md | 1 + content/migrations/ado/index.md | 1 + content/migrations/elm/index.md | 1 + content/migrations/troubleshooting/index.md | 1 + .../index.md | 1 + .../nonprofit-enterprise-plan/index.md | 1 + .../nonprofit/nonprofit-teams-plan/index.md | 1 + content/nonprofit/troubleshooting/index.md | 1 + src/content-linter/tests/category-pages.ts | 47 ++++--------------- 11 files changed, 19 insertions(+), 38 deletions(-) diff --git a/content/admin/managing-iam/index.md b/content/admin/managing-iam/index.md index 709bb8629949..75c3a89bbc63 100644 --- a/content/admin/managing-iam/index.md +++ b/content/admin/managing-iam/index.md @@ -5,6 +5,7 @@ redirect_from: - /enterprise/admin/authentication - /admin/authentication - /admin/identity-and-access-management +allowTitleToDifferFromFilename: true versions: ghec: '*' ghes: '*' diff --git a/content/codespaces/about-codespaces/index.md b/content/codespaces/about-codespaces/index.md index a698c51bd5b9..766331124326 100644 --- a/content/codespaces/about-codespaces/index.md +++ b/content/codespaces/about-codespaces/index.md @@ -2,6 +2,7 @@ title: 'Getting started with {% data variables.product.prodname_github_codespaces %}' shortTitle: Getting started intro: 'Learn how to get started with {% data variables.product.prodname_github_codespaces %}, then find out more about how a codespace works.' +allowTitleToDifferFromFilename: true versions: fpt: '*' ghec: '*' diff --git a/content/get-started/learning-to-code/index.md b/content/get-started/learning-to-code/index.md index 2ce8da0d41a1..68d2ea1f7e68 100644 --- a/content/get-started/learning-to-code/index.md +++ b/content/get-started/learning-to-code/index.md @@ -2,6 +2,7 @@ title: Learn to code with GitHub Copilot shortTitle: Learn to code intro: "Learn how {% data variables.product.github %} and {% data variables.product.prodname_copilot_short %} can help you build programming skills, write better code, and ship secure projects." +allowTitleToDifferFromFilename: true versions: fpt: '*' layout: journey-landing diff --git a/content/migrations/ado/index.md b/content/migrations/ado/index.md index 80bb113c4f30..0e635461a288 100644 --- a/content/migrations/ado/index.md +++ b/content/migrations/ado/index.md @@ -2,6 +2,7 @@ title: Migrating from Azure DevOps shortTitle: Migrate from Azure DevOps intro: Plan and execute a migration from Azure DevOps to {% data variables.product.prodname_ghe_cloud %}. This six-part guide explains how to configure access, migrate, and the follow-up tasks needed to get your repositories ready for work. +allowTitleToDifferFromFilename: true versions: fpt: '*' ghec: '*' diff --git a/content/migrations/elm/index.md b/content/migrations/elm/index.md index 04149e7779be..263bcf5989cf 100644 --- a/content/migrations/elm/index.md +++ b/content/migrations/elm/index.md @@ -2,6 +2,7 @@ title: Live migrations from GitHub Enterprise Server to GHE.com shortTitle: Live migrations (GHES to GHE.com) intro: 'Use the Enterprise Live Migrations tool to migrate repositories with minimal downtime for developers.' +allowTitleToDifferFromFilename: true versions: fpt: '*' ghec: '*' diff --git a/content/migrations/troubleshooting/index.md b/content/migrations/troubleshooting/index.md index b4c6b2b05f22..0c31ce7268a4 100644 --- a/content/migrations/troubleshooting/index.md +++ b/content/migrations/troubleshooting/index.md @@ -2,6 +2,7 @@ title: Troubleshooting migrations shortTitle: Troubleshoot migrations intro: 'Troubleshoot common issues when migrating repositories with {% data variables.product.prodname_importer_proper_name %}.' +allowTitleToDifferFromFilename: true versions: ghec: '*' children: diff --git a/content/nonprofit/contributing-to-open-source-for-good/index.md b/content/nonprofit/contributing-to-open-source-for-good/index.md index aa925ef30006..c42d06d7f210 100644 --- a/content/nonprofit/contributing-to-open-source-for-good/index.md +++ b/content/nonprofit/contributing-to-open-source-for-good/index.md @@ -1,6 +1,7 @@ --- title: Open Source Projects for Good intro: Nonprofit organizations can partner with open source collaborators to build solutions together. +allowTitleToDifferFromFilename: true versions: fpt: '*' children: diff --git a/content/nonprofit/nonprofit-enterprise-plan/index.md b/content/nonprofit/nonprofit-enterprise-plan/index.md index 296727093c78..f3f245236c85 100644 --- a/content/nonprofit/nonprofit-enterprise-plan/index.md +++ b/content/nonprofit/nonprofit-enterprise-plan/index.md @@ -1,6 +1,7 @@ --- title: GitHub Enterprise Cloud for nonprofits intro: Learn how to apply for a discounted GitHub Enterprise Cloud plan for nonprofits. +allowTitleToDifferFromFilename: true versions: fpt: '*' shortTitle: Discounted GitHub Enterprise Cloud plan for nonprofits diff --git a/content/nonprofit/nonprofit-teams-plan/index.md b/content/nonprofit/nonprofit-teams-plan/index.md index 109351cf88ee..036ee9a5ebf4 100644 --- a/content/nonprofit/nonprofit-teams-plan/index.md +++ b/content/nonprofit/nonprofit-teams-plan/index.md @@ -1,6 +1,7 @@ --- title: GitHub Team plan for nonprofits intro: Learn how to apply for a free GitHub Team plan for nonprofits. +allowTitleToDifferFromFilename: true versions: fpt: '*' shortTitle: Free GitHub Team plan for nonprofits diff --git a/content/nonprofit/troubleshooting/index.md b/content/nonprofit/troubleshooting/index.md index 584b5dca7e1d..a856d8654f61 100644 --- a/content/nonprofit/troubleshooting/index.md +++ b/content/nonprofit/troubleshooting/index.md @@ -1,6 +1,7 @@ --- title: Frequently asked questions intro: Troubleshooting for GitHub for Nonprofits applications +allowTitleToDifferFromFilename: true versions: fpt: '*' children: diff --git a/src/content-linter/tests/category-pages.ts b/src/content-linter/tests/category-pages.ts index 7a81b6e21783..a8f14cc7f390 100644 --- a/src/content-linter/tests/category-pages.ts +++ b/src/content-linter/tests/category-pages.ts @@ -10,7 +10,6 @@ import { beforeAll, describe, expect, test } from 'vitest' import matter from '@/frame/lib/read-frontmatter' import { renderContent } from '@/content-render/index' -import getApplicableVersions from '@/versions/lib/get-applicable-versions' import contextualize from '@/frame/middleware/context/context' import shortVersions from '@/versions/middleware/short-versions' import { ROOT } from '@/frame/lib/constants' @@ -26,7 +25,7 @@ function getFrontmatterData(markdown: string): MarkdownFrontmatter { return parsed.data as MarkdownFrontmatter } -describe.skip('category pages', () => { +describe('category pages', () => { const walkOptions = { globs: ['*/index.md', 'enterprise/*/index.md'], ignore: [ @@ -76,14 +75,10 @@ describe.skip('category pages', () => { (indexRelPath, indexAbsPath, indexLink) => { let publishedArticlePaths: string[] = [] let availableArticlePaths: string[] = [] - let categoryVersions: string[] = [] let allowTitleToDifferFromFilename: boolean | undefined = false let indexTitle: string = '' let indexShortTitle: string = '' - const articleVersions: { - [articlePath: string]: string[] - } = {} beforeAll(async () => { const categoryDir = path.dirname(indexAbsPath) @@ -92,7 +87,6 @@ describe.skip('category pages', () => { const parsed = matter(indexContents) if (!parsed.data) throw new Error('No frontmatter') const categoryData = parsed.data as MarkdownFrontmatter - categoryVersions = getApplicableVersions(categoryData.versions, indexAbsPath) allowTitleToDifferFromFilename = categoryData.allowTitleToDifferFromFilename const articleLinks = categoryData.children.filter((child) => { const mdPath = getPath(productDir, indexLink, child) @@ -112,17 +106,14 @@ describe.skip('category pages', () => { await contextualize(req as ExtendedRequest, res as Response, next) await shortVersions(req as ExtendedRequest, res as Response, next) - const productIndexContents = await fs.promises.readFile(productIndex, 'utf8') - const productIndexData = getFrontmatterData(productIndexContents) + indexTitle = categoryData.title.includes('{') + ? await renderContent(categoryData.title, req.context, { textOnly: true }) + : categoryData.title - indexTitle = productIndexData.title.includes('{') - ? await renderContent(productIndexData.title, req.context, { textOnly: true }) - : productIndexData.title - - if (productIndexData.shortTitle) { - indexShortTitle = productIndexData.shortTitle.includes('{') - ? await renderContent(productIndexData.shortTitle, req.context, { textOnly: true }) - : productIndexData.shortTitle + if (categoryData.shortTitle) { + indexShortTitle = categoryData.shortTitle.includes('{') + ? await renderContent(categoryData.shortTitle, req.context, { textOnly: true }) + : categoryData.shortTitle } else { indexShortTitle = '' } @@ -145,7 +136,7 @@ describe.skip('category pages', () => { const childEntries = await fs.promises.readdir(categoryDir, { withFileTypes: true }) const childFileEntries = childEntries.filter( - (ent) => ent.isFile() && ent.name !== 'index.md', + (ent) => ent.isFile() && ent.name.endsWith('.md') && ent.name !== 'index.md', ) const childFilePaths = childFileEntries.map((ent) => path.join(categoryDir, ent.name)) @@ -163,18 +154,6 @@ describe.skip('category pages', () => { }), ) ).filter(Boolean) as string[] - - await Promise.all( - childFilePaths.map(async (articlePath) => { - const articleContents = await fs.promises.readFile(articlePath, 'utf8') - const versionData = getFrontmatterData(articleContents) - - articleVersions[articlePath] = getApplicableVersions( - versionData.versions, - articlePath, - ) as string[] - }), - ) }) test('contains all expected articles', () => { @@ -189,14 +168,6 @@ describe.skip('category pages', () => { expect(unexpectedArticles.length, errorMessage).toBe(0) }) - test('contains only articles and subcategories with versions that are also available in the parent category', () => { - for (const [articleName, versions] of Object.entries(articleVersions)) { - const unexpectedVersions = difference(versions, categoryVersions) - const errorMessage = `${articleName} has versions that are not available in parent category` - expect(unexpectedVersions.length, errorMessage).toBe(0) - } - }) - test('slugified title matches parent directory name', () => { if (allowTitleToDifferFromFilename) return From b4bd00d9db8d53c432598d34b51e3bbd8aa81045 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 14:54:13 +0000 Subject: [PATCH 10/19] Replace skipped languages search test with a Japanese search page test (#63666) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3725887d-99f1-4760-b935-f3fe2443bbd6 --- src/languages/tests/search.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/languages/tests/search.ts b/src/languages/tests/search.ts index 49384e1351da..ad18f5937c02 100644 --- a/src/languages/tests/search.ts +++ b/src/languages/tests/search.ts @@ -1,15 +1,19 @@ -import { describe, expect, test } from 'vitest' +import { expect, test, vi } from 'vitest' -import { languageKeys } from '@/languages/lib/languages-server' -import { get } from '@/tests/helpers/e2etest' +import { describeIfElasticsearchURL } from '@/tests/helpers/conditional-runs' +import { getDOM } from '@/tests/helpers/e2etest' -const langs = languageKeys.filter((lang) => lang !== 'en') +// Fixture indexing creates only English and Japanese indexes, so Japanese +// stands in for every non-English language. +describeIfElasticsearchURL('search page in non-English languages', () => { + vi.setConfig({ testTimeout: 60 * 1000 }) -// Fixture indexing creates only English and Japanese indexes, so this suite cannot -// run against every non-English language in CI. -describe.skip('search', () => { - test.each(langs)('search in %s', async (lang) => { - const res = await get(`/search?language=${lang}&version=dotcom&query=pages`) - expect(res.statusCode).toBe(200) + // src/search/tests/fixtures/search-indexes/tests_github-docs_general-search_fpt_ja-records.json has title "フー". + test('renders Japanese search results', async () => { + const $ = await getDOM('/ja/search?query=foo') + const titles = $('[data-testid="search-result"] h2') + .map((i, el) => $(el).text()) + .get() + expect(titles).toContain('フー') }) }) From 5a41e5df17af1b0e8a18d75373c76925241876c0 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 14:54:19 +0000 Subject: [PATCH 11/19] Keep GHES ranges in GHD022 condition fixes (#63668) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3725887d-99f1-4760-b935-f3fe2443bbd6 --- .../liquid-ifversion-versions.ts | 46 +++++++--- .../tests/unit/liquid-ifversion-versions.ts | 92 +++++++++++++++++++ 2 files changed, 123 insertions(+), 15 deletions(-) diff --git a/src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts b/src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts index e08d46a780ca..5d1e8b682635 100644 --- a/src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts +++ b/src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts @@ -454,7 +454,15 @@ function updateConditionals(condTagItems: CondTagItem[]) { for (const key of versionsNotInFrontmatter) { delete item.versionsObj[key] } - item.action.cond = Object.keys(item.versionsObj).join(' or ') + // Also drop deprecated lower bounds so one fix pass leaves no GHD022 error. + if (item.versionsObj.ghes && item.versionsObj.ghes !== '*') { + item.versionsObj.ghes = getSimplifiedSemverRange( + rangeTerms(item.versionsObj.ghes) + .map((term) => term.join(' ')) + .join(' '), + ) + } + item.action.cond = toLiquidCondition(item.versionsObj) item.action.type = 'change' continue } @@ -467,7 +475,7 @@ function updateConditionals(condTagItems: CondTagItem[]) { if (simplifiedSemver === '' && Object.keys(item.versionsObj).length > 1) { item.action.type = 'change' delete item.versionsObj.ghes - item.action.cond = Object.keys(item.versionsObj).join(' or ') + item.action.cond = toLiquidCondition(item.versionsObj) continue } @@ -475,19 +483,7 @@ function updateConditionals(condTagItems: CondTagItem[]) { if (item.versionsObjAll.ghes !== simplifiedSemver && !item.versionsObjAll.feature) { item.action.type = 'change' item.versionsObj.ghes = simplifiedSemver - - // Translate the simplified range back to Liquid condition syntax. - if (simplifiedSemver !== '*') { - const newVersions = Object.entries(item.versionsObj).map(([key, value]) => { - if (key === 'ghes') { - if (value === '*') return key - return `${key} ${value}` - } else return key - }) - item.action.cond = newVersions.join(' or ') - } else { - item.action.cond = Object.keys(item.versionsObj).join(' or ') - } + item.action.cond = toLiquidCondition(item.versionsObj) } } @@ -523,6 +519,26 @@ function updateConditionals(condTagItems: CondTagItem[]) { } } +// Turn a versions object back into Liquid, such as { ghes: '> 3.18 < 3.22', fpt: '*' } +// into 'ghes > 3.18 and ghes < 3.22 or fpt'. +function toLiquidCondition(versionsObj: VersionsObject): string { + return Object.entries(versionsObj) + .map(([key, value]) => { + if (key === 'feature') return value + const terms = rangeTerms(value).map(([operator, release]) => `${key} ${operator} ${release}`) + return terms.length ? terms.join(' and ') : key + }) + .join(' or ') +} + +// Split spaced and compact ranges, such as '> 3.18 <3.22', into [['>', '3.18'], ['<', '3.22']]. +function rangeTerms(range: string): [string, string][] { + return [...range.matchAll(/(!=|>=|<=|=|>|<)\s*([^\s<>=!]+)/g)].map(([, operator, release]) => [ + operator, + release, + ]) +} + function processConditionals( item: CondTagItem, condTagItems: CondTagItem[], diff --git a/src/content-linter/tests/unit/liquid-ifversion-versions.ts b/src/content-linter/tests/unit/liquid-ifversion-versions.ts index 106ac2bb5045..930dfe6b610d 100644 --- a/src/content-linter/tests/unit/liquid-ifversion-versions.ts +++ b/src/content-linter/tests/unit/liquid-ifversion-versions.ts @@ -308,4 +308,96 @@ describe(liquidIfversionVersions.names.join(' - '), () => { const errors = result.markdown expect(errors.length).toBe(0) }) + + describe('keeps GHES ranges when removing products not in frontmatter', () => { + const oldest = supported.at(-1) + const newest = supported[0] + const cases = [ + { + fm: [' ghes: "*"'], + cond: `ghes > ${oldest} or fpt`, + expected: `ghes > ${oldest}`, + }, + { + fm: [' ghes: "*"'], + cond: `fpt or ghes < ${newest}`, + expected: `ghes < ${newest}`, + }, + { + fm: [' ghes: "*"'], + cond: `ghes > ${oldest} and ghes < ${newest} or fpt`, + expected: `ghes > ${oldest} and ghes < ${newest}`, + }, + { + fm: [' ghes: "*"'], + cond: `ghes > 3.10 and ghes < ${newest} or fpt`, + expected: `ghes < ${newest}`, + }, + { + fm: [' ghes: "*"'], + cond: `ghes >3.10 and ghes <${newest} or fpt`, + expected: `ghes < ${newest}`, + }, + { + fm: [' ghes: "*"'], + cond: `fpt or ghes <${newest}`, + expected: `ghes < ${newest}`, + }, + { + fm: [' ghes: "*"'], + cond: `ghes >=${supported.at(-2)} or ghec`, + expected: `ghes >= ${supported.at(-2)}`, + }, + { + fm: [' fpt: "*"', ' ghes: "*"'], + cond: `ghes > ${oldest} or ghec`, + expected: `ghes > ${oldest}`, + }, + { + fm: [' fpt: "*"', ' ghec: "*"'], + cond: `ghes > ${oldest} or fpt`, + expected: 'fpt', + }, + ] + for (const { fm, cond, expected } of cases) { + test(`${cond} with ${fm.map((line) => line.trim().split(':')[0]).join(', ')}`, async () => { + const toMarkdown = (condition: string) => + [ + '---', + 'title: "Hello"', + 'versions:', + ...fm, + '---', + `{% ifversion ${condition} %}x{% endif %}`, + ].join('\n') + + const result = await runRule(liquidIfversionVersions, { + strings: { markdown: toMarkdown(cond) }, + }) + const errors = result.markdown + expect(errors.length).toBe(1) + expect(errors[0].fixInfo?.insertText).toBe(`ifversion ${expected}`) + + // The suggested condition passes the rule on the next run. + const fixed = await runRule(liquidIfversionVersions, { + strings: { markdown: toMarkdown(expected) }, + }) + expect(fixed.markdown.length).toBe(0) + }) + } + }) + + test('simplified GHES range has no extra space', async () => { + const markdown = [ + ...placeholderAllVersionsFm, + `{% ifversion ghes > 3.10 and ghes < ${supported[0]} or fpt %}x{% endif %}`, + ].join('\n') + + const result = await runRule(liquidIfversionVersions, { + strings: { markdown }, + }) + const errors = result.markdown + expect(errors.length).toBe(1) + expect(errors[0].fixInfo?.insertText).toBe(`ifversion ghes < ${supported[0]} or fpt`) + }) }) From b1bc86797608597b8f2cae582db11f87634b8b1e Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 15:09:31 +0000 Subject: [PATCH 12/19] Add exported repository rulesets (#63517) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bb497298-69e7-493c-a13c-cba326a9871f --- .github/rulesets/README.md | 35 +++ .../docs-internal/copilot-code-review.json | 29 ++ .../frozen-archive-branches.json | 36 +++ .../docs-internal/main-branch-protection.json | 271 ++++++++++++++++++ .../docs-internal/megabranch-guardrail.json | 71 +++++ .../rulesets/docs/main-branch-protection.json | 220 ++++++++++++++ 6 files changed, 662 insertions(+) create mode 100644 .github/rulesets/README.md create mode 100644 .github/rulesets/docs-internal/copilot-code-review.json create mode 100644 .github/rulesets/docs-internal/frozen-archive-branches.json create mode 100644 .github/rulesets/docs-internal/main-branch-protection.json create mode 100644 .github/rulesets/docs-internal/megabranch-guardrail.json create mode 100644 .github/rulesets/docs/main-branch-protection.json diff --git a/.github/rulesets/README.md b/.github/rulesets/README.md new file mode 100644 index 000000000000..f322df05134c --- /dev/null +++ b/.github/rulesets/README.md @@ -0,0 +1,35 @@ +# Rulesets + +Copies of the repository rulesets on github/docs-internal and github/docs, exported from the live settings. One file per ruleset, named after the ruleset. + +| Folder | Applies to | +| --- | --- | +| `docs-internal/` | github/docs-internal | +| `docs/` | github/docs | + +This folder is mirrored to github/docs along with the rest of the repo, so both folders appear in both repos. The folder name says which repo a file belongs to, not the repo you are reading it in. A ruleset payload has no field naming its repo, and both repos have a ruleset called `main branch protection`. + +GitHub does not apply these files. Editing a file changes nothing live, and editing a ruleset in the UI changes nothing here. Keep the two matching by hand. + +The weekday [ruleset drift check](https://github.com/github/technical-content/tree/main/.github/scripts/ruleset-drift) in github/technical-content reads these files and alerts when the live rules on `main` are weaker than what they say. [Branch protection with rulesets](https://github.com/github/technical-content/blob/main/engineering/branch-protection-rulesets.md) explains why each rule exists. + +## Change a ruleset + +Edit the file in a pull request here in docs-internal, including files under `docs/`. After it merges, apply it as a repo admin of the repo named by the folder: + +```sh +repo=docs-internal +name="main branch protection" +id=$(gh api "repos/github/$repo/rulesets" --jq ".[] | select(.name == \"$name\") | .id") +gh api -X PUT "repos/github/$repo/rulesets/$id" --input ".github/rulesets/$repo/main-branch-protection.json" +``` + +`PUT` replaces the whole ruleset. For a new ruleset, `POST` instead: `gh api -X POST repos/github/REPO/rulesets --input .github/rulesets/REPO/FILE.json`. + +When you add or remove a test suite in `.github/workflows/test.yml`, update `required_status_checks` in both `docs-internal/main-branch-protection.json` and `docs/main-branch-protection.json`, except for suites the matrix excludes on github/docs. + +## Export a ruleset + +```sh +gh api repos/github/REPO/rulesets/ID --jq '{name, target, enforcement, bypass_actors, conditions, rules}' > .github/rulesets/REPO/FILE.json +``` diff --git a/.github/rulesets/docs-internal/copilot-code-review.json b/.github/rulesets/docs-internal/copilot-code-review.json new file mode 100644 index 000000000000..cf9dc264afa4 --- /dev/null +++ b/.github/rulesets/docs-internal/copilot-code-review.json @@ -0,0 +1,29 @@ +{ + "name": "Copilot code review", + "target": "branch", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 5, + "actor_type": "RepositoryRole", + "bypass_mode": "always" + } + ], + "conditions": { + "ref_name": { + "exclude": [], + "include": [ + "~DEFAULT_BRANCH" + ] + } + }, + "rules": [ + { + "type": "copilot_code_review", + "parameters": { + "review_on_push": false, + "review_draft_pull_requests": false + } + } + ] +} diff --git a/.github/rulesets/docs-internal/frozen-archive-branches.json b/.github/rulesets/docs-internal/frozen-archive-branches.json new file mode 100644 index 000000000000..822b370443b2 --- /dev/null +++ b/.github/rulesets/docs-internal/frozen-archive-branches.json @@ -0,0 +1,36 @@ +{ + "name": "frozen archive branches", + "target": "branch", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 5, + "actor_type": "RepositoryRole", + "bypass_mode": "always" + } + ], + "conditions": { + "ref_name": { + "exclude": [], + "include": [ + "refs/heads/old-nanoc-site-archive", + "refs/heads/gh-pages-archive", + "refs/heads/old-main" + ] + } + }, + "rules": [ + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + }, + { + "type": "update" + }, + { + "type": "creation" + } + ] +} diff --git a/.github/rulesets/docs-internal/main-branch-protection.json b/.github/rulesets/docs-internal/main-branch-protection.json new file mode 100644 index 000000000000..29ed7c5ca4df --- /dev/null +++ b/.github/rulesets/docs-internal/main-branch-protection.json @@ -0,0 +1,271 @@ +{ + "name": "main branch protection", + "target": "branch", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 5, + "actor_type": "RepositoryRole", + "bypass_mode": "always" + }, + { + "actor_id": 87537, + "actor_type": "Integration", + "bypass_mode": "always" + }, + { + "actor_id": 87541, + "actor_type": "Integration", + "bypass_mode": "always" + }, + { + "actor_id": 325922, + "actor_type": "Team", + "bypass_mode": "always" + } + ], + "conditions": { + "ref_name": { + "exclude": [], + "include": [ + "refs/heads/main" + ] + } + }, + "rules": [ + { + "type": "creation" + }, + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + }, + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 1, + "dismiss_stale_reviews_on_push": false, + "required_reviewers": [], + "require_code_owner_review": true, + "dismissal_restriction": { + "enabled": false, + "allowed_actors": [] + }, + "require_last_push_approval": false, + "required_review_thread_resolution": false, + "ignore_approvals_from_contributors": false, + "require_extra_approval_for_unattributed_changes": true, + "allowed_merge_methods": [ + "merge", + "squash", + "rebase" + ] + } + }, + { + "type": "merge_queue", + "parameters": { + "merge_method": "SQUASH", + "max_entries_to_build": 20, + "min_entries_to_merge": 1, + "max_entries_to_merge": 10, + "min_entries_to_merge_wait_minutes": 5, + "grouping_strategy": "ALLGREEN", + "check_response_timeout_minutes": 60, + "check_run_retries_limit": 0, + "actor_controlled_merging": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "strict_required_status_checks_policy": true, + "do_not_enforce_on_create": false, + "required_status_checks": [ + { + "context": "archives", + "integration_id": 15368 + }, + { + "context": "article-api", + "integration_id": 15368 + }, + { + "context": "assets", + "integration_id": 15368 + }, + { + "context": "audit-logs", + "integration_id": 15368 + }, + { + "context": "automated-pipelines", + "integration_id": 15368 + }, + { + "context": "color-schemes", + "integration_id": 15368 + }, + { + "context": "content-linter", + "integration_id": 15368 + }, + { + "context": "content-render", + "integration_id": 15368 + }, + { + "context": "data-directory", + "integration_id": 15368 + }, + { + "context": "early-access", + "integration_id": 15368 + }, + { + "context": "events", + "integration_id": 15368 + }, + { + "context": "fixtures", + "integration_id": 15368 + }, + { + "context": "frame", + "integration_id": 15368 + }, + { + "context": "ghes-releases", + "integration_id": 15368 + }, + { + "context": "github-apps", + "integration_id": 15368 + }, + { + "context": "graphql", + "integration_id": 15368 + }, + { + "context": "journeys", + "integration_id": 15368 + }, + { + "context": "landings", + "integration_id": 15368 + }, + { + "context": "languages", + "integration_id": 15368 + }, + { + "context": "links", + "integration_id": 15368 + }, + { + "context": "observability", + "integration_id": 15368 + }, + { + "context": "products", + "integration_id": 15368 + }, + { + "context": "redirects", + "integration_id": 15368 + }, + { + "context": "release-notes", + "integration_id": 15368 + }, + { + "context": "rest", + "integration_id": 15368 + }, + { + "context": "search", + "integration_id": 15368 + }, + { + "context": "secret-scanning", + "integration_id": 15368 + }, + { + "context": "shielding", + "integration_id": 15368 + }, + { + "context": "versions", + "integration_id": 15368 + }, + { + "context": "webhooks", + "integration_id": 15368 + }, + { + "context": "workflows", + "integration_id": 15368 + }, + { + "context": "lint-content", + "integration_id": 15368 + }, + { + "context": "line-endings", + "integration_id": 15368 + }, + { + "context": "lint-code", + "integration_id": 15368 + }, + { + "context": "local-dev", + "integration_id": 15368 + }, + { + "context": "playwright-tests (playwright-a11y)", + "integration_id": 15368 + }, + { + "context": "playwright-tests (playwright-rendering)", + "integration_id": 15368 + }, + { + "context": "playwright-tests (playwright-secret-scanning)", + "integration_id": 15368 + }, + { + "context": "set-vault-keys", + "integration_id": 15368 + }, + { + "context": "docs-internal-moda-config-bundle / docs-internal-moda-config-bundle", + "integration_id": 15368 + }, + { + "context": "docs-internal-docker-image / docs-internal-docker-image", + "integration_id": 15368 + }, + { + "context": "docs-internal-docker-security / docs-internal-docker-security", + "integration_id": 15368 + }, + { + "context": "merge-queue-restriction", + "integration_id": 15368 + } + ] + } + }, + { + "type": "required_deployments", + "parameters": { + "required_deployment_environments": [ + "production" + ] + } + } + ] +} diff --git a/.github/rulesets/docs-internal/megabranch-guardrail.json b/.github/rulesets/docs-internal/megabranch-guardrail.json new file mode 100644 index 000000000000..f3eb50591005 --- /dev/null +++ b/.github/rulesets/docs-internal/megabranch-guardrail.json @@ -0,0 +1,71 @@ +{ + "name": "megabranch guardrail", + "target": "branch", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 5, + "actor_type": "RepositoryRole", + "bypass_mode": "always" + }, + { + "actor_id": 87537, + "actor_type": "Integration", + "bypass_mode": "always" + }, + { + "actor_id": 87541, + "actor_type": "Integration", + "bypass_mode": "always" + }, + { + "actor_id": 325922, + "actor_type": "Team", + "bypass_mode": "always" + } + ], + "conditions": { + "ref_name": { + "exclude": [], + "include": [ + "refs/heads/*megabranch*" + ] + } + }, + "rules": [ + { + "type": "creation" + }, + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + }, + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 1, + "dismiss_stale_reviews_on_push": false, + "required_reviewers": [], + "require_code_owner_review": true, + "dismissal_restriction": { + "enabled": false, + "allowed_actors": [] + }, + "require_last_push_approval": false, + "required_review_thread_resolution": false, + "ignore_approvals_from_contributors": false, + "require_extra_approval_for_unattributed_changes": true, + "allowed_merge_methods": [ + "merge", + "squash", + "rebase" + ] + } + }, + { + "type": "update" + } + ] +} diff --git a/.github/rulesets/docs/main-branch-protection.json b/.github/rulesets/docs/main-branch-protection.json new file mode 100644 index 000000000000..4444e4cc6967 --- /dev/null +++ b/.github/rulesets/docs/main-branch-protection.json @@ -0,0 +1,220 @@ +{ + "name": "main branch protection", + "target": "branch", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 5, + "actor_type": "RepositoryRole", + "bypass_mode": "always" + } + ], + "conditions": { + "ref_name": { + "exclude": [], + "include": [ + "refs/heads/main" + ] + } + }, + "rules": [ + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + }, + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 1, + "dismiss_stale_reviews_on_push": true, + "required_reviewers": [], + "require_code_owner_review": true, + "dismissal_restriction": { + "enabled": false, + "allowed_actors": [] + }, + "require_last_push_approval": false, + "required_review_thread_resolution": false, + "ignore_approvals_from_contributors": false, + "require_extra_approval_for_unattributed_changes": true, + "allowed_merge_methods": [ + "merge", + "squash", + "rebase" + ] + } + }, + { + "type": "merge_queue", + "parameters": { + "merge_method": "SQUASH", + "max_entries_to_build": 2, + "min_entries_to_merge": 1, + "max_entries_to_merge": 1, + "min_entries_to_merge_wait_minutes": 5, + "grouping_strategy": "ALLGREEN", + "check_response_timeout_minutes": 60, + "actor_controlled_merging": false + } + }, + { + "type": "required_status_checks", + "parameters": { + "strict_required_status_checks_policy": true, + "do_not_enforce_on_create": false, + "required_status_checks": [ + { + "context": "archives", + "integration_id": 15368 + }, + { + "context": "article-api", + "integration_id": 15368 + }, + { + "context": "assets", + "integration_id": 15368 + }, + { + "context": "audit-logs", + "integration_id": 15368 + }, + { + "context": "automated-pipelines", + "integration_id": 15368 + }, + { + "context": "color-schemes", + "integration_id": 15368 + }, + { + "context": "content-linter", + "integration_id": 15368 + }, + { + "context": "content-render", + "integration_id": 15368 + }, + { + "context": "data-directory", + "integration_id": 15368 + }, + { + "context": "early-access", + "integration_id": 15368 + }, + { + "context": "events", + "integration_id": 15368 + }, + { + "context": "fixtures", + "integration_id": 15368 + }, + { + "context": "frame", + "integration_id": 15368 + }, + { + "context": "ghes-releases", + "integration_id": 15368 + }, + { + "context": "github-apps", + "integration_id": 15368 + }, + { + "context": "graphql", + "integration_id": 15368 + }, + { + "context": "journeys", + "integration_id": 15368 + }, + { + "context": "landings", + "integration_id": 15368 + }, + { + "context": "links", + "integration_id": 15368 + }, + { + "context": "observability", + "integration_id": 15368 + }, + { + "context": "products", + "integration_id": 15368 + }, + { + "context": "redirects", + "integration_id": 15368 + }, + { + "context": "release-notes", + "integration_id": 15368 + }, + { + "context": "rest", + "integration_id": 15368 + }, + { + "context": "search", + "integration_id": 15368 + }, + { + "context": "secret-scanning", + "integration_id": 15368 + }, + { + "context": "shielding", + "integration_id": 15368 + }, + { + "context": "versions", + "integration_id": 15368 + }, + { + "context": "webhooks", + "integration_id": 15368 + }, + { + "context": "workflows", + "integration_id": 15368 + }, + { + "context": "lint-content", + "integration_id": 15368 + }, + { + "context": "line-endings", + "integration_id": 15368 + }, + { + "context": "lint-code", + "integration_id": 15368 + }, + { + "context": "local-dev", + "integration_id": 15368 + }, + { + "context": "playwright-tests (playwright-a11y)", + "integration_id": 15368 + }, + { + "context": "playwright-tests (playwright-rendering)", + "integration_id": 15368 + }, + { + "context": "playwright-tests (playwright-secret-scanning)", + "integration_id": 15368 + } + ] + } + } + ] +} From 1230337ad5f6292d236569791a6fe5d79777c76f Mon Sep 17 00:00:00 2001 From: hubwriter Date: Mon, 5 Oct 2026 15:21:34 +0000 Subject: [PATCH 13/19] Copilot dynamic workflows: small addition about availability (#63681) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- content/copilot/concepts/agents/dynamic-workflows.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/content/copilot/concepts/agents/dynamic-workflows.md b/content/copilot/concepts/agents/dynamic-workflows.md index 2e27266ce558..5b24f0c5b841 100644 --- a/content/copilot/concepts/agents/dynamic-workflows.md +++ b/content/copilot/concepts/agents/dynamic-workflows.md @@ -66,6 +66,10 @@ To see what dynamic workflows are available, just ask {% data variables.product. What dynamic workflows are available? ``` +## Availability of dynamic workflows + +Dynamic workflows are available in the {% data variables.copilot.github_copilot_app_short %}, {% data variables.copilot.copilot_cli_short %}, and {% data variables.copilot.copilot_sdk_short %}. They are available to users on all {% data variables.product.prodname_copilot_short %} plans except {% data variables.copilot.copilot_pro_short %} and {% data variables.copilot.copilot_pro_plus_short %} subscribers on existing annual plans who remain on legacy premium request-based billing. For more information about this billing model, see [AUTOTITLE](/copilot/reference/copilot-billing/request-based-billing-legacy). + ## How dynamic workflows differ from autopilot and fleet If you already use autopilot mode and the `/fleet` command, a dynamic workflow might seem to be the same sort of thing. The main difference is how work is planned and controlled, not simply how many agents are used. From d58d5e34c0a6bf57495d19b6652949d56031354d Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 15:31:25 +0000 Subject: [PATCH 14/19] Remove redundant ifversion tags from GitHub App transfer article (#63659) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3725887d-99f1-4760-b935-f3fe2443bbd6 --- .../transferring-ownership-of-a-github-app.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/content/apps/maintaining-github-apps/transferring-ownership-of-a-github-app.md b/content/apps/maintaining-github-apps/transferring-ownership-of-a-github-app.md index eee359e6f073..7d0f305a88a6 100644 --- a/content/apps/maintaining-github-apps/transferring-ownership-of-a-github-app.md +++ b/content/apps/maintaining-github-apps/transferring-ownership-of-a-github-app.md @@ -31,9 +31,7 @@ You can transfer apps from a user or organization to another account. You cannot 1. Select the {% data variables.product.prodname_github_app %} whose ownership you want to transfer. {% data reusables.user-settings.github_apps_advanced %} 1. Click **Transfer ownership**. -1. Under "New owner's {% data variables.product.prodname_dotcom %} {% ifversion fpt or enterprise-apps-public-beta %}username, organization, or enterprise name",{% else %}username or organization name",{% endif %} type the name of the account you want to transfer the {% data variables.product.prodname_github_app %} to. -{%- ifversion fpt or enterprise-apps-public-beta %} +1. Under "New owner's {% data variables.product.prodname_dotcom %} username, organization, or enterprise name", type the name of the account you want to transfer the {% data variables.product.prodname_github_app %} to. 1. Select the account from the dropdown that you wish to transfer to. Be aware that enterprises and organizations can have the same name, so check that you are transferring to the correct account type. 1. If transferring the app would uninstall it from your account, a warning will appear. -{%- endif %} 1. Click **Transfer this {% data variables.product.prodname_github_app %}**. From b9ef0953765c5f98e7d08044d1c5dae19e1ee128 Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Mon, 5 Oct 2026 15:43:41 +0000 Subject: [PATCH 15/19] Add aspire.dev to Copilot cloud agent allowlist (#63667) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- content/copilot/reference/copilot-allowlist-reference.md | 1 + 1 file changed, 1 insertion(+) diff --git a/content/copilot/reference/copilot-allowlist-reference.md b/content/copilot/reference/copilot-allowlist-reference.md index 632ee70e6d62..496085e9b0ed 100644 --- a/content/copilot/reference/copilot-allowlist-reference.md +++ b/content/copilot/reference/copilot-allowlist-reference.md @@ -304,6 +304,7 @@ The allowlist allows access to the following hosts: * `deno.land` * `registry.bower.io` * `binaries.prisma.sh` +* `aspire.dev` ### Programming Languages & Package Managers: Perl From 4a5124b7b69a0a56f1c6bb1684dc771ba6a301da Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 5 Oct 2026 15:53:07 +0000 Subject: [PATCH 16/19] Allow 10 category directory names to differ from titles (#63664) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3725887d-99f1-4760-b935-f3fe2443bbd6 From b7184a264bb69179bb675fabc5a4efd0246c6435 Mon Sep 17 00:00:00 2001 From: chiph Date: Mon, 5 Oct 2026 15:53:53 +0000 Subject: [PATCH 17/19] Follow-up to add section on GitHub's role in incident response (we don't touch data) (#63370) Co-authored-by: Justin Alex Paramanandan <1155821+jusuchin85@users.noreply.github.com> Co-authored-by: Stacy Carter Co-authored-by: Stacy Carter Co-authored-by: Copilot Co-authored-by: Cory Calahan Co-authored-by: Anne-Marie <102995847+am-stead@users.noreply.github.com> Co-authored-by: Laura Coursen Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- ...ow-github-support-can-help-during-a-security-incident.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/content/support/learning-about-github-support/understanding-how-github-support-can-help-during-a-security-incident.md b/content/support/learning-about-github-support/understanding-how-github-support-can-help-during-a-security-incident.md index 55c6b3792b9b..5b4425a5254a 100644 --- a/content/support/learning-about-github-support/understanding-how-github-support-can-help-during-a-security-incident.md +++ b/content/support/learning-about-github-support/understanding-how-github-support-can-help-during-a-security-incident.md @@ -41,6 +41,12 @@ If you need guidance using these features or want to request a feature, see [AUT {% data variables.contact.github_support %} does not join or lead your incident response process. To investigate and contain a threat, use {% data variables.product.github %}'s audit log, security, and access-management tools. +### No revert or point-in-time restore + +{% data variables.contact.github_support %} cannot revert changes, restore deleted data, or roll a branch or repository back to a point in time before an incident. Recovering your data is [outside the scope of Support](/support/learning-about-github-support/about-github-support#scope-of-support). + +To review changes made to a repository, use the activity view. You can see a detailed history of changes, such as pushes, merges, force pushes, and branch changes, and associates these changes with commits and authenticated users. See [AUTOTITLE](/repositories/viewing-activity-and-data-for-your-repository/using-the-activity-view-to-see-changes-to-a-repository). + ### No log preservation {% data variables.contact.github_support %} cannot fulfill requests to preserve logs or audit data, extend their retention periods, or place them on hold for your investigation. Opening a support ticket does not change how long data remains available. To retain data for an investigation, export it while it is available or configure audit log streaming in advance to storage you control. From eb102ab0f55e1b8f36821f219e3d9693a61ae394 Mon Sep 17 00:00:00 2001 From: docs-bot <77750099+docs-bot@users.noreply.github.com> Date: Mon, 5 Oct 2026 16:44:36 +0000 Subject: [PATCH 18/19] Sync secret scanning data (#63686) Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> --- .../data/pattern-docs/fpt/public-docs.yml | 10 ++++++++++ .../data/pattern-docs/ghec/public-docs.yml | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/secret-scanning/data/pattern-docs/fpt/public-docs.yml b/src/secret-scanning/data/pattern-docs/fpt/public-docs.yml index 0012a3e9f550..feb7e473ae70 100644 --- a/src/secret-scanning/data/pattern-docs/fpt/public-docs.yml +++ b/src/secret-scanning/data/pattern-docs/fpt/public-docs.yml @@ -4824,6 +4824,16 @@ hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false +- provider: Tavily + supportedSecret: Tavily API Key + secretType: tavily_api_key + isPublic: false + isPrivateWithGhas: true + hasPushProtection: false + hasValidityCheck: false + hasExtendedMetadata: false + base64Supported: false + isduplicate: false - provider: Telegram supportedSecret: Telegram Bot Token secretType: telegram_bot_token diff --git a/src/secret-scanning/data/pattern-docs/ghec/public-docs.yml b/src/secret-scanning/data/pattern-docs/ghec/public-docs.yml index 0012a3e9f550..feb7e473ae70 100644 --- a/src/secret-scanning/data/pattern-docs/ghec/public-docs.yml +++ b/src/secret-scanning/data/pattern-docs/ghec/public-docs.yml @@ -4824,6 +4824,16 @@ hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false +- provider: Tavily + supportedSecret: Tavily API Key + secretType: tavily_api_key + isPublic: false + isPrivateWithGhas: true + hasPushProtection: false + hasValidityCheck: false + hasExtendedMetadata: false + base64Supported: false + isduplicate: false - provider: Telegram supportedSecret: Telegram Bot Token secretType: telegram_bot_token From 2e3a124178f6688e8e43cf95370db449d8f05f60 Mon Sep 17 00:00:00 2001 From: docs-bot <77750099+docs-bot@users.noreply.github.com> Date: Mon, 5 Oct 2026 16:46:45 +0000 Subject: [PATCH 19/19] GraphQL schema update (#63689) Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> --- src/graphql/data/fpt/category-map.json | 4 + src/graphql/data/fpt/changelog.json | 50 ++++ src/graphql/data/fpt/schema-pulls.json | 224 +++++++++++++++++- .../data/fpt/schema-security-advisories.json | 49 ++++ src/graphql/data/fpt/schema.docs.graphql | 175 +++++++++++++- src/graphql/data/ghec/category-map.json | 4 + src/graphql/data/ghec/schema-pulls.json | 224 +++++++++++++++++- .../data/ghec/schema-security-advisories.json | 49 ++++ src/graphql/data/ghec/schema.docs.graphql | 175 +++++++++++++- 9 files changed, 948 insertions(+), 6 deletions(-) diff --git a/src/graphql/data/fpt/category-map.json b/src/graphql/data/fpt/category-map.json index 707c648eb014..484bff95aa33 100644 --- a/src/graphql/data/fpt/category-map.json +++ b/src/graphql/data/fpt/category-map.json @@ -259,6 +259,7 @@ "reopenpullrequest": "pulls", "requestreviews": "pulls", "requestreviewsbylogin": "pulls", + "rerequestreviews": "pulls", "resolvereviewthread": "pulls", "revertpullrequest": "pulls", "submitpullrequestreview": "pulls", @@ -813,6 +814,7 @@ "projectv2workflowedge": "projects", "removedfromprojectv2event": "projects", "addedtomergequeueevent": "pulls", + "addedtostackevent": "pulls", "automergedisabledevent": "pulls", "automergeenabledevent": "pulls", "automergerequest": "pulls", @@ -873,6 +875,7 @@ "pullrequesttimelineitemsedge": "pulls", "readyforreviewevent": "pulls", "removedfrommergequeueevent": "pulls", + "removedfromstackevent": "pulls", "requestedreviewerconnection": "pulls", "requestedrevieweredge": "pulls", "requiredreviewerconfiguration": "pulls", @@ -1754,6 +1757,7 @@ "requestreviewsbylogininput": "pulls", "requestreviewsinput": "pulls", "requiredreviewerconfigurationinput": "pulls", + "rerequestreviewsinput": "pulls", "resolvereviewthreadinput": "pulls", "revertpullrequestinput": "pulls", "submitpullrequestreviewinput": "pulls", diff --git a/src/graphql/data/fpt/changelog.json b/src/graphql/data/fpt/changelog.json index c89226c41fc7..5f0cbabccd69 100644 --- a/src/graphql/data/fpt/changelog.json +++ b/src/graphql/data/fpt/changelog.json @@ -1,4 +1,54 @@ [ + { + "schemaChanges": [ + { + "title": "The GraphQL schema includes these changes:", + "changes": [ + "

Type AddedToStackEvent was added

", + "

AddedToStackEvent object implements Node interface

", + "

Field actor was added to object type AddedToStackEvent

", + "

Field createdAt was added to object type AddedToStackEvent

", + "

Field id was added to object type AddedToStackEvent

", + "

Field pullRequest was added to object type AddedToStackEvent

", + "

Field stackId was added to object type AddedToStackEvent

", + "

Field stackNumber was added to object type AddedToStackEvent

", + "

Type RemovedFromStackEvent was added

", + "

RemovedFromStackEvent object implements Node interface

", + "

Field actor was added to object type RemovedFromStackEvent

", + "

Field createdAt was added to object type RemovedFromStackEvent

", + "

Field id was added to object type RemovedFromStackEvent

", + "

Field pullRequest was added to object type RemovedFromStackEvent

", + "

Field stackId was added to object type RemovedFromStackEvent

", + "

Field stackNumber was added to object type RemovedFromStackEvent

", + "

Type RerequestReviewsInput was added

", + "

Input field botIds of type '[ID!]was added to input object typeRerequestReviewsInput'

", + "

Input field clientMutationId of type String was added to input object type RerequestReviewsInput

", + "

Input field pullRequestId of type ID! was added to input object type RerequestReviewsInput

", + "

Input field teamIds of type '[ID!]was added to input object typeRerequestReviewsInput'

", + "

Input field userIds of type '[ID!]was added to input object typeRerequestReviewsInput'

", + "

Type RerequestReviewsPayload was added

", + "

Field actor was added to object type RerequestReviewsPayload

", + "

Field clientMutationId was added to object type RerequestReviewsPayload

", + "

Field pullRequest was added to object type RerequestReviewsPayload

", + "

Field requestedReviewersEdge was added to object type RerequestReviewsPayload

", + "

Field rerequestReviews was added to object type Mutation

", + "

Argument input: RerequestReviewsInput! added to field Mutation.rerequestReviews

", + "

Member AddedToStackEvent was added to Union type PullRequestTimelineItems

", + "

Member RemovedFromStackEvent was added to Union type PullRequestTimelineItems

", + "

Argument isWithdrawn: Boolean added to field Query.securityAdvisories

", + "

Argument 'severities: [SecurityAdvisorySeverity!]added to fieldQuery.securityAdvisories'

", + "

Field cveId was added to object type SecurityAdvisory

", + "

Field githubReviewedAt was added to object type SecurityAdvisory

", + "

Field nvdPublishedAt was added to object type SecurityAdvisory

", + "

Field repositoryAdvisoryUrl was added to object type SecurityAdvisory

", + "

Field sourceCodeLocation was added to object type SecurityAdvisory

" + ] + } + ], + "previewChanges": [], + "upcomingChanges": [], + "date": "2026-10-05" + }, { "schemaChanges": [ { diff --git a/src/graphql/data/fpt/schema-pulls.json b/src/graphql/data/fpt/schema-pulls.json index ca30aa2fcee6..7a7990018d9c 100644 --- a/src/graphql/data/fpt/schema-pulls.json +++ b/src/graphql/data/fpt/schema-pulls.json @@ -178,7 +178,7 @@ "name": "archivePullRequest", "id": "archivepullrequest", "href": "/graphql/reference/pulls#mutation-archivepullrequest", - "description": "

Archive a pull request. Closes, locks, and marks the pull request as archived.\nOnly repository admins can archive pull requests.

", + "description": "

Archive a pull request. Closes and marks the pull request as archived. Only repository admins can archive pull requests.

", "isDeprecated": false, "inputFields": [ { @@ -806,6 +806,52 @@ ], "category": "pulls" }, + { + "name": "rerequestReviews", + "id": "rerequestreviews", + "href": "/graphql/reference/pulls#mutation-rerequestreviews", + "description": "

Rerequest reviews on a pull request.

", + "isDeprecated": false, + "inputFields": [ + { + "name": "input", + "type": "RerequestReviewsInput!", + "id": "rerequestreviewsinput", + "href": "/graphql/reference/pulls#input-object-rerequestreviewsinput" + } + ], + "returnFields": [ + { + "name": "actor", + "type": "Actor", + "id": "actor", + "href": "/graphql/reference/users#interface-actor", + "description": "

Identifies the actor who performed the event.

" + }, + { + "name": "clientMutationId", + "type": "String", + "id": "string", + "href": "/graphql/reference/other#scalar-string", + "description": "

A unique identifier for the client performing the mutation.

" + }, + { + "name": "pullRequest", + "type": "PullRequest", + "id": "pullrequest", + "href": "/graphql/reference/pulls#object-pullrequest", + "description": "

The pull request that is getting requests.

" + }, + { + "name": "requestedReviewersEdge", + "type": "UserEdge", + "id": "useredge", + "href": "/graphql/reference/users#object-useredge", + "description": "

The edge from the pull request to the requested reviewers.

" + } + ], + "category": "pulls" + }, { "name": "resolveReviewThread", "id": "resolvereviewthread", @@ -1233,6 +1279,65 @@ ], "category": "pulls" }, + { + "name": "AddedToStackEvent", + "id": "addedtostackevent", + "href": "/graphql/reference/pulls#object-addedtostackevent", + "description": "

Represents anadded_to_stackevent on a given pull request.

", + "isDeprecated": false, + "implements": [ + { + "name": "Node", + "id": "node", + "href": "/graphql/reference/meta#interface-node" + } + ], + "fields": [ + { + "name": "actor", + "description": "

Identifies the actor who performed the event.

", + "type": "Actor", + "id": "actor", + "href": "/graphql/reference/users#interface-actor" + }, + { + "name": "createdAt", + "description": "

Identifies the date and time when the object was created.

", + "type": "DateTime!", + "id": "datetime", + "href": "/graphql/reference/other#scalar-datetime" + }, + { + "name": "id", + "description": "

The Node ID of the AddedToStackEvent object.

", + "type": "ID!", + "id": "id", + "href": "/graphql/reference/other#scalar-id" + }, + { + "name": "pullRequest", + "description": "

Pull request added to the stack.

", + "type": "PullRequest", + "id": "pullrequest", + "href": "/graphql/reference/pulls#object-pullrequest" + }, + { + "name": "stackId", + "description": "

Database ID of the stack.

", + "type": "BigInt", + "id": "bigint", + "href": "/graphql/reference/other#scalar-bigint" + }, + { + "name": "stackNumber", + "description": "

Number of the stack in its repository.

", + "type": "Int", + "id": "int", + "href": "/graphql/reference/other#scalar-int" + } + ], + "category": "pulls" + }, { "name": "AutoMergeDisabledEvent", "id": "automergedisabledevent", @@ -7285,6 +7390,65 @@ ], "category": "pulls" }, + { + "name": "RemovedFromStackEvent", + "id": "removedfromstackevent", + "href": "/graphql/reference/pulls#object-removedfromstackevent", + "description": "

Represents aremoved_from_stackevent on a given pull request.

", + "isDeprecated": false, + "implements": [ + { + "name": "Node", + "id": "node", + "href": "/graphql/reference/meta#interface-node" + } + ], + "fields": [ + { + "name": "actor", + "description": "

Identifies the actor who performed the event.

", + "type": "Actor", + "id": "actor", + "href": "/graphql/reference/users#interface-actor" + }, + { + "name": "createdAt", + "description": "

Identifies the date and time when the object was created.

", + "type": "DateTime!", + "id": "datetime", + "href": "/graphql/reference/other#scalar-datetime" + }, + { + "name": "id", + "description": "

The Node ID of the RemovedFromStackEvent object.

", + "type": "ID!", + "id": "id", + "href": "/graphql/reference/other#scalar-id" + }, + { + "name": "pullRequest", + "description": "

Pull request removed from the stack.

", + "type": "PullRequest", + "id": "pullrequest", + "href": "/graphql/reference/pulls#object-pullrequest" + }, + { + "name": "stackId", + "description": "

Database ID of the stack.

", + "type": "BigInt", + "id": "bigint", + "href": "/graphql/reference/other#scalar-bigint" + }, + { + "name": "stackNumber", + "description": "

Number of the stack in its repository.

", + "type": "Int", + "id": "int", + "href": "/graphql/reference/other#scalar-int" + } + ], + "category": "pulls" + }, { "name": "RequestedReviewerConnection", "id": "requestedreviewerconnection", @@ -8935,6 +9099,11 @@ "id": "addedtoprojectv2event", "href": "/graphql/reference/projects#object-addedtoprojectv2event" }, + { + "name": "AddedToStackEvent", + "id": "addedtostackevent", + "href": "/graphql/reference/pulls#object-addedtostackevent" + }, { "name": "AssignedEvent", "id": "assignedevent", @@ -9230,6 +9399,11 @@ "id": "removedfromprojectv2event", "href": "/graphql/reference/projects#object-removedfromprojectv2event" }, + { + "name": "RemovedFromStackEvent", + "id": "removedfromstackevent", + "href": "/graphql/reference/pulls#object-removedfromstackevent" + }, { "name": "RenamedTitleEvent", "id": "renamedtitleevent", @@ -10587,6 +10761,54 @@ ], "category": "pulls" }, + { + "name": "RerequestReviewsInput", + "id": "rerequestreviewsinput", + "href": "/graphql/reference/pulls#input-object-rerequestreviewsinput", + "description": "

Autogenerated input type of RerequestReviews.

", + "inputFields": [ + { + "name": "botIds", + "description": "

The Node IDs of the bots to rerequest.

", + "type": "[ID!]", + "id": "id", + "href": "/graphql/reference/other#scalar-id", + "isDeprecated": false + }, + { + "name": "clientMutationId", + "description": "

A unique identifier for the client performing the mutation.

", + "type": "String", + "id": "string", + "href": "/graphql/reference/other#scalar-string" + }, + { + "name": "pullRequestId", + "description": "

The Node ID of the pull request to modify.

", + "type": "ID!", + "id": "id", + "href": "/graphql/reference/other#scalar-id", + "isDeprecated": false + }, + { + "name": "teamIds", + "description": "

The Node IDs of the teams to rerequest.

", + "type": "[ID!]", + "id": "id", + "href": "/graphql/reference/other#scalar-id", + "isDeprecated": false + }, + { + "name": "userIds", + "description": "

The Node IDs of the users to rerequest.

", + "type": "[ID!]", + "id": "id", + "href": "/graphql/reference/other#scalar-id", + "isDeprecated": false + } + ], + "category": "pulls" + }, { "name": "ResolveReviewThreadInput", "id": "resolvereviewthreadinput", diff --git a/src/graphql/data/fpt/schema-security-advisories.json b/src/graphql/data/fpt/schema-security-advisories.json index 73adc4fed1ef..c3fda6561f6c 100644 --- a/src/graphql/data/fpt/schema-security-advisories.json +++ b/src/graphql/data/fpt/schema-security-advisories.json @@ -56,6 +56,13 @@ "href": "/graphql/reference/security-advisories#input-object-securityadvisoryidentifierfilter", "description": "

Filter advisories by identifier, e.g. GHSA or CVE.

" }, + { + "name": "isWithdrawn", + "type": "Boolean", + "id": "boolean", + "href": "/graphql/reference/other#scalar-boolean", + "description": "

Filter advisories by withdrawn status. True returns only withdrawn advisories. False excludes withdrawn advisories.

" + }, { "name": "last", "type": "Int", @@ -77,6 +84,13 @@ "href": "/graphql/reference/other#scalar-datetime", "description": "

Filter advisories to those published since a time in the past.

" }, + { + "name": "severities", + "type": "[SecurityAdvisorySeverity!]", + "id": "securityadvisoryseverity", + "href": "/graphql/reference/security-advisories#enum-securityadvisoryseverity", + "description": "

A list of advisory severities to filter advisories by.

" + }, { "name": "updatedSince", "type": "DateTime", @@ -377,6 +391,13 @@ "id": "securityadvisoryclassification", "href": "/graphql/reference/security-advisories#enum-securityadvisoryclassification" }, + { + "name": "cveId", + "description": "

The Common Vulnerabilities and Exposures ID.

", + "type": "String", + "id": "string", + "href": "/graphql/reference/other#scalar-string" + }, { "name": "cvss", "description": "

The CVSS associated with this advisory.

", @@ -466,6 +487,13 @@ "id": "string", "href": "/graphql/reference/other#scalar-string" }, + { + "name": "githubReviewedAt", + "description": "

When the GitHub Security team reviewed this advisory, which may differ from when GitHub published it.

", + "type": "DateTime", + "id": "datetime", + "href": "/graphql/reference/other#scalar-datetime" + }, { "name": "id", "description": "

The Node ID of the SecurityAdvisory object.

", @@ -487,6 +515,13 @@ "id": "uri", "href": "/graphql/reference/other#scalar-uri" }, + { + "name": "nvdPublishedAt", + "description": "

When NVD published its advisory record, independent of GitHub's publishing timeline.

", + "type": "DateTime", + "id": "datetime", + "href": "/graphql/reference/other#scalar-datetime" + }, { "name": "origin", "description": "

The organization that originated the advisory.

", @@ -515,6 +550,13 @@ "id": "securityadvisoryreference", "href": "/graphql/reference/security-advisories#object-securityadvisoryreference" }, + { + "name": "repositoryAdvisoryUrl", + "description": "

The URL for the associated repository security advisory, if this global advisory is linked to a repository-level advisory.

", + "type": "URI", + "id": "uri", + "href": "/graphql/reference/other#scalar-uri" + }, { "name": "severity", "description": "

The severity of the advisory.

", @@ -522,6 +564,13 @@ "id": "securityadvisoryseverity", "href": "/graphql/reference/security-advisories#enum-securityadvisoryseverity" }, + { + "name": "sourceCodeLocation", + "description": "

URL to the upstream source code relevant to the advisory.

", + "type": "URI", + "id": "uri", + "href": "/graphql/reference/other#scalar-uri" + }, { "name": "summary", "description": "

A short plaintext summary of the advisory.

", diff --git a/src/graphql/data/fpt/schema.docs.graphql b/src/graphql/data/fpt/schema.docs.graphql index 508138ae0dd6..b0cb7d619b67 100644 --- a/src/graphql/data/fpt/schema.docs.graphql +++ b/src/graphql/data/fpt/schema.docs.graphql @@ -1555,6 +1555,41 @@ type AddedToProjectV2Event implements Node & ProjectV2Event @docsCategory(name: wasAutomated: Boolean! } +""" +Represents an 'added_to_stack' event on a given pull request. +""" +type AddedToStackEvent implements Node @docsCategory(name: "pulls") { + """ + Identifies the actor who performed the event. + """ + actor: Actor + + """ + Identifies the date and time when the object was created. + """ + createdAt: DateTime! + + """ + The Node ID of the AddedToStackEvent object + """ + id: ID! + + """ + Pull request added to the stack. + """ + pullRequest: PullRequest + + """ + Database ID of the stack. + """ + stackId: BigInt + + """ + Number of the stack in its repository. + """ + stackNumber: Int +} + """ Represents configuration for assigning Copilot to an issue (public variant) """ @@ -27631,8 +27666,7 @@ type Mutation @docsCategory(name: "meta") { ): ArchiveProjectV2ItemPayload @docsCategory(name: "projects") """ - Archive a pull request. Closes, locks, and marks the pull request as archived. - Only repository admins can archive pull requests. + Archive a pull request. Closes and marks the pull request as archived. Only repository admins can archive pull requests. """ archivePullRequest( """ @@ -29153,6 +29187,16 @@ type Mutation @docsCategory(name: "meta") { input: RerequestCheckSuiteInput! ): RerequestCheckSuitePayload @docsCategory(name: "checks") + """ + Rerequest reviews on a pull request. + """ + rerequestReviews( + """ + Parameters for RerequestReviews + """ + input: RerequestReviewsInput! + ): RerequestReviewsPayload @docsCategory(name: "pulls") + """ Marks a review thread as resolved. """ @@ -47462,6 +47506,7 @@ union PullRequestTimelineItems @docsCategory(name: "pulls") = | AddedToMergeQueueEvent | AddedToProjectEvent | AddedToProjectV2Event + | AddedToStackEvent | AssignedEvent | AutoMergeDisabledEvent | AutoMergeEnabledEvent @@ -47521,6 +47566,7 @@ union PullRequestTimelineItems @docsCategory(name: "pulls") = | RemovedFromMergeQueueEvent | RemovedFromProjectEvent | RemovedFromProjectV2Event + | RemovedFromStackEvent | RenamedTitleEvent | ReopenedEvent | ReviewDismissedEvent @@ -48522,6 +48568,11 @@ type Query implements Node @docsCategory(name: "meta") { """ identifier: SecurityAdvisoryIdentifierFilter + """ + Filter advisories by withdrawn status. True returns only withdrawn advisories. False excludes withdrawn advisories. + """ + isWithdrawn: Boolean + """ Returns the last _n_ elements from the list. """ @@ -48537,6 +48588,11 @@ type Query implements Node @docsCategory(name: "meta") { """ publishedSince: DateTime + """ + A list of advisory severities to filter advisories by. + """ + severities: [SecurityAdvisorySeverity!] + """ Filter advisories to those updated since a time in the past. """ @@ -50787,6 +50843,41 @@ type RemovedFromProjectV2Event implements Node & ProjectV2Event @docsCategory(na wasAutomated: Boolean! } +""" +Represents a 'removed_from_stack' event on a given pull request. +""" +type RemovedFromStackEvent implements Node @docsCategory(name: "pulls") { + """ + Identifies the actor who performed the event. + """ + actor: Actor + + """ + Identifies the date and time when the object was created. + """ + createdAt: DateTime! + + """ + The Node ID of the RemovedFromStackEvent object + """ + id: ID! + + """ + Pull request removed from the stack. + """ + pullRequest: PullRequest + + """ + Database ID of the stack. + """ + stackId: BigInt + + """ + Number of the stack in its repository. + """ + stackNumber: Int +} + """ Represents a 'renamed' event on a given issue or pull request """ @@ -59634,6 +59725,61 @@ type RerequestCheckSuitePayload { clientMutationId: String } +""" +Autogenerated input type of RerequestReviews +""" +input RerequestReviewsInput { + """ + The Node IDs of the bots to rerequest. + """ + botIds: [ID!] @possibleTypes(concreteTypes: ["Bot"]) + + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + The Node ID of the pull request to modify. + """ + pullRequestId: ID! @possibleTypes(concreteTypes: ["PullRequest"]) + + """ + The Node IDs of the teams to rerequest. + """ + teamIds: [ID!] @possibleTypes(concreteTypes: ["EnterpriseTeam", "Team"], abstractType: "TeamReviewRequestable") + + """ + The Node IDs of the users to rerequest. + """ + userIds: [ID!] @possibleTypes(concreteTypes: ["User"]) +} + +""" +Autogenerated return type of RerequestReviews. +""" +type RerequestReviewsPayload { + """ + Identifies the actor who performed the event. + """ + actor: Actor + + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + The pull request that is getting requests. + """ + pullRequest: PullRequest + + """ + The edge from the pull request to the requested reviewers. + """ + requestedReviewersEdge: UserEdge +} + """ Autogenerated input type of ResolveReviewThread """ @@ -60631,6 +60777,11 @@ type SecurityAdvisory implements Node @docsCategory(name: "security-advisories") """ classification: SecurityAdvisoryClassification! + """ + The Common Vulnerabilities and Exposures ID + """ + cveId: String + """ The CVSS associated with this advisory """ @@ -60689,6 +60840,11 @@ type SecurityAdvisory implements Node @docsCategory(name: "security-advisories") """ ghsaId: String! + """ + When the GitHub Security team reviewed this advisory, which may differ from when GitHub published it + """ + githubReviewedAt: DateTime + """ The Node ID of the SecurityAdvisory object """ @@ -60704,6 +60860,11 @@ type SecurityAdvisory implements Node @docsCategory(name: "security-advisories") """ notificationsPermalink: URI + """ + When NVD published its advisory record, independent of GitHub's publishing timeline + """ + nvdPublishedAt: DateTime + """ The organization that originated the advisory """ @@ -60724,11 +60885,21 @@ type SecurityAdvisory implements Node @docsCategory(name: "security-advisories") """ references: [SecurityAdvisoryReference!]! + """ + The URL for the associated repository security advisory, if this global advisory is linked to a repository-level advisory. + """ + repositoryAdvisoryUrl: URI + """ The severity of the advisory """ severity: SecurityAdvisorySeverity! + """ + URL to the upstream source code relevant to the advisory + """ + sourceCodeLocation: URI + """ A short plaintext summary of the advisory """ diff --git a/src/graphql/data/ghec/category-map.json b/src/graphql/data/ghec/category-map.json index 707c648eb014..484bff95aa33 100644 --- a/src/graphql/data/ghec/category-map.json +++ b/src/graphql/data/ghec/category-map.json @@ -259,6 +259,7 @@ "reopenpullrequest": "pulls", "requestreviews": "pulls", "requestreviewsbylogin": "pulls", + "rerequestreviews": "pulls", "resolvereviewthread": "pulls", "revertpullrequest": "pulls", "submitpullrequestreview": "pulls", @@ -813,6 +814,7 @@ "projectv2workflowedge": "projects", "removedfromprojectv2event": "projects", "addedtomergequeueevent": "pulls", + "addedtostackevent": "pulls", "automergedisabledevent": "pulls", "automergeenabledevent": "pulls", "automergerequest": "pulls", @@ -873,6 +875,7 @@ "pullrequesttimelineitemsedge": "pulls", "readyforreviewevent": "pulls", "removedfrommergequeueevent": "pulls", + "removedfromstackevent": "pulls", "requestedreviewerconnection": "pulls", "requestedrevieweredge": "pulls", "requiredreviewerconfiguration": "pulls", @@ -1754,6 +1757,7 @@ "requestreviewsbylogininput": "pulls", "requestreviewsinput": "pulls", "requiredreviewerconfigurationinput": "pulls", + "rerequestreviewsinput": "pulls", "resolvereviewthreadinput": "pulls", "revertpullrequestinput": "pulls", "submitpullrequestreviewinput": "pulls", diff --git a/src/graphql/data/ghec/schema-pulls.json b/src/graphql/data/ghec/schema-pulls.json index 72f59588dd37..ee1fafe56ad7 100644 --- a/src/graphql/data/ghec/schema-pulls.json +++ b/src/graphql/data/ghec/schema-pulls.json @@ -178,7 +178,7 @@ "name": "archivePullRequest", "id": "archivepullrequest", "href": "/graphql/reference/pulls#mutation-archivepullrequest", - "description": "

Archive a pull request. Closes, locks, and marks the pull request as archived.\nOnly repository admins can archive pull requests.

", + "description": "

Archive a pull request. Closes and marks the pull request as archived. Only repository admins can archive pull requests.

", "isDeprecated": false, "inputFields": [ { @@ -806,6 +806,52 @@ ], "category": "pulls" }, + { + "name": "rerequestReviews", + "id": "rerequestreviews", + "href": "/graphql/reference/pulls#mutation-rerequestreviews", + "description": "

Rerequest reviews on a pull request.

", + "isDeprecated": false, + "inputFields": [ + { + "name": "input", + "type": "RerequestReviewsInput!", + "id": "rerequestreviewsinput", + "href": "/graphql/reference/pulls#input-object-rerequestreviewsinput" + } + ], + "returnFields": [ + { + "name": "actor", + "type": "Actor", + "id": "actor", + "href": "/graphql/reference/users#interface-actor", + "description": "

Identifies the actor who performed the event.

" + }, + { + "name": "clientMutationId", + "type": "String", + "id": "string", + "href": "/graphql/reference/other#scalar-string", + "description": "

A unique identifier for the client performing the mutation.

" + }, + { + "name": "pullRequest", + "type": "PullRequest", + "id": "pullrequest", + "href": "/graphql/reference/pulls#object-pullrequest", + "description": "

The pull request that is getting requests.

" + }, + { + "name": "requestedReviewersEdge", + "type": "UserEdge", + "id": "useredge", + "href": "/graphql/reference/users#object-useredge", + "description": "

The edge from the pull request to the requested reviewers.

" + } + ], + "category": "pulls" + }, { "name": "resolveReviewThread", "id": "resolvereviewthread", @@ -1233,6 +1279,65 @@ ], "category": "pulls" }, + { + "name": "AddedToStackEvent", + "id": "addedtostackevent", + "href": "/graphql/reference/pulls#object-addedtostackevent", + "description": "

Represents anadded_to_stackevent on a given pull request.

", + "isDeprecated": false, + "implements": [ + { + "name": "Node", + "id": "node", + "href": "/graphql/reference/meta#interface-node" + } + ], + "fields": [ + { + "name": "actor", + "description": "

Identifies the actor who performed the event.

", + "type": "Actor", + "id": "actor", + "href": "/graphql/reference/users#interface-actor" + }, + { + "name": "createdAt", + "description": "

Identifies the date and time when the object was created.

", + "type": "DateTime!", + "id": "datetime", + "href": "/graphql/reference/other#scalar-datetime" + }, + { + "name": "id", + "description": "

The Node ID of the AddedToStackEvent object.

", + "type": "ID!", + "id": "id", + "href": "/graphql/reference/other#scalar-id" + }, + { + "name": "pullRequest", + "description": "

Pull request added to the stack.

", + "type": "PullRequest", + "id": "pullrequest", + "href": "/graphql/reference/pulls#object-pullrequest" + }, + { + "name": "stackId", + "description": "

Database ID of the stack.

", + "type": "BigInt", + "id": "bigint", + "href": "/graphql/reference/other#scalar-bigint" + }, + { + "name": "stackNumber", + "description": "

Number of the stack in its repository.

", + "type": "Int", + "id": "int", + "href": "/graphql/reference/other#scalar-int" + } + ], + "category": "pulls" + }, { "name": "AutoMergeDisabledEvent", "id": "automergedisabledevent", @@ -7285,6 +7390,65 @@ ], "category": "pulls" }, + { + "name": "RemovedFromStackEvent", + "id": "removedfromstackevent", + "href": "/graphql/reference/pulls#object-removedfromstackevent", + "description": "

Represents aremoved_from_stackevent on a given pull request.

", + "isDeprecated": false, + "implements": [ + { + "name": "Node", + "id": "node", + "href": "/graphql/reference/meta#interface-node" + } + ], + "fields": [ + { + "name": "actor", + "description": "

Identifies the actor who performed the event.

", + "type": "Actor", + "id": "actor", + "href": "/graphql/reference/users#interface-actor" + }, + { + "name": "createdAt", + "description": "

Identifies the date and time when the object was created.

", + "type": "DateTime!", + "id": "datetime", + "href": "/graphql/reference/other#scalar-datetime" + }, + { + "name": "id", + "description": "

The Node ID of the RemovedFromStackEvent object.

", + "type": "ID!", + "id": "id", + "href": "/graphql/reference/other#scalar-id" + }, + { + "name": "pullRequest", + "description": "

Pull request removed from the stack.

", + "type": "PullRequest", + "id": "pullrequest", + "href": "/graphql/reference/pulls#object-pullrequest" + }, + { + "name": "stackId", + "description": "

Database ID of the stack.

", + "type": "BigInt", + "id": "bigint", + "href": "/graphql/reference/other#scalar-bigint" + }, + { + "name": "stackNumber", + "description": "

Number of the stack in its repository.

", + "type": "Int", + "id": "int", + "href": "/graphql/reference/other#scalar-int" + } + ], + "category": "pulls" + }, { "name": "RequestedReviewerConnection", "id": "requestedreviewerconnection", @@ -8935,6 +9099,11 @@ "id": "addedtoprojectv2event", "href": "/graphql/reference/projects#object-addedtoprojectv2event" }, + { + "name": "AddedToStackEvent", + "id": "addedtostackevent", + "href": "/graphql/reference/pulls#object-addedtostackevent" + }, { "name": "AssignedEvent", "id": "assignedevent", @@ -9230,6 +9399,11 @@ "id": "removedfromprojectv2event", "href": "/graphql/reference/projects#object-removedfromprojectv2event" }, + { + "name": "RemovedFromStackEvent", + "id": "removedfromstackevent", + "href": "/graphql/reference/pulls#object-removedfromstackevent" + }, { "name": "RenamedTitleEvent", "id": "renamedtitleevent", @@ -10587,6 +10761,54 @@ ], "category": "pulls" }, + { + "name": "RerequestReviewsInput", + "id": "rerequestreviewsinput", + "href": "/graphql/reference/pulls#input-object-rerequestreviewsinput", + "description": "

Autogenerated input type of RerequestReviews.

", + "inputFields": [ + { + "name": "botIds", + "description": "

The Node IDs of the bots to rerequest.

", + "type": "[ID!]", + "id": "id", + "href": "/graphql/reference/other#scalar-id", + "isDeprecated": false + }, + { + "name": "clientMutationId", + "description": "

A unique identifier for the client performing the mutation.

", + "type": "String", + "id": "string", + "href": "/graphql/reference/other#scalar-string" + }, + { + "name": "pullRequestId", + "description": "

The Node ID of the pull request to modify.

", + "type": "ID!", + "id": "id", + "href": "/graphql/reference/other#scalar-id", + "isDeprecated": false + }, + { + "name": "teamIds", + "description": "

The Node IDs of the teams to rerequest.

", + "type": "[ID!]", + "id": "id", + "href": "/graphql/reference/other#scalar-id", + "isDeprecated": false + }, + { + "name": "userIds", + "description": "

The Node IDs of the users to rerequest.

", + "type": "[ID!]", + "id": "id", + "href": "/graphql/reference/other#scalar-id", + "isDeprecated": false + } + ], + "category": "pulls" + }, { "name": "ResolveReviewThreadInput", "id": "resolvereviewthreadinput", diff --git a/src/graphql/data/ghec/schema-security-advisories.json b/src/graphql/data/ghec/schema-security-advisories.json index 73adc4fed1ef..c3fda6561f6c 100644 --- a/src/graphql/data/ghec/schema-security-advisories.json +++ b/src/graphql/data/ghec/schema-security-advisories.json @@ -56,6 +56,13 @@ "href": "/graphql/reference/security-advisories#input-object-securityadvisoryidentifierfilter", "description": "

Filter advisories by identifier, e.g. GHSA or CVE.

" }, + { + "name": "isWithdrawn", + "type": "Boolean", + "id": "boolean", + "href": "/graphql/reference/other#scalar-boolean", + "description": "

Filter advisories by withdrawn status. True returns only withdrawn advisories. False excludes withdrawn advisories.

" + }, { "name": "last", "type": "Int", @@ -77,6 +84,13 @@ "href": "/graphql/reference/other#scalar-datetime", "description": "

Filter advisories to those published since a time in the past.

" }, + { + "name": "severities", + "type": "[SecurityAdvisorySeverity!]", + "id": "securityadvisoryseverity", + "href": "/graphql/reference/security-advisories#enum-securityadvisoryseverity", + "description": "

A list of advisory severities to filter advisories by.

" + }, { "name": "updatedSince", "type": "DateTime", @@ -377,6 +391,13 @@ "id": "securityadvisoryclassification", "href": "/graphql/reference/security-advisories#enum-securityadvisoryclassification" }, + { + "name": "cveId", + "description": "

The Common Vulnerabilities and Exposures ID.

", + "type": "String", + "id": "string", + "href": "/graphql/reference/other#scalar-string" + }, { "name": "cvss", "description": "

The CVSS associated with this advisory.

", @@ -466,6 +487,13 @@ "id": "string", "href": "/graphql/reference/other#scalar-string" }, + { + "name": "githubReviewedAt", + "description": "

When the GitHub Security team reviewed this advisory, which may differ from when GitHub published it.

", + "type": "DateTime", + "id": "datetime", + "href": "/graphql/reference/other#scalar-datetime" + }, { "name": "id", "description": "

The Node ID of the SecurityAdvisory object.

", @@ -487,6 +515,13 @@ "id": "uri", "href": "/graphql/reference/other#scalar-uri" }, + { + "name": "nvdPublishedAt", + "description": "

When NVD published its advisory record, independent of GitHub's publishing timeline.

", + "type": "DateTime", + "id": "datetime", + "href": "/graphql/reference/other#scalar-datetime" + }, { "name": "origin", "description": "

The organization that originated the advisory.

", @@ -515,6 +550,13 @@ "id": "securityadvisoryreference", "href": "/graphql/reference/security-advisories#object-securityadvisoryreference" }, + { + "name": "repositoryAdvisoryUrl", + "description": "

The URL for the associated repository security advisory, if this global advisory is linked to a repository-level advisory.

", + "type": "URI", + "id": "uri", + "href": "/graphql/reference/other#scalar-uri" + }, { "name": "severity", "description": "

The severity of the advisory.

", @@ -522,6 +564,13 @@ "id": "securityadvisoryseverity", "href": "/graphql/reference/security-advisories#enum-securityadvisoryseverity" }, + { + "name": "sourceCodeLocation", + "description": "

URL to the upstream source code relevant to the advisory.

", + "type": "URI", + "id": "uri", + "href": "/graphql/reference/other#scalar-uri" + }, { "name": "summary", "description": "

A short plaintext summary of the advisory.

", diff --git a/src/graphql/data/ghec/schema.docs.graphql b/src/graphql/data/ghec/schema.docs.graphql index 508138ae0dd6..b0cb7d619b67 100644 --- a/src/graphql/data/ghec/schema.docs.graphql +++ b/src/graphql/data/ghec/schema.docs.graphql @@ -1555,6 +1555,41 @@ type AddedToProjectV2Event implements Node & ProjectV2Event @docsCategory(name: wasAutomated: Boolean! } +""" +Represents an 'added_to_stack' event on a given pull request. +""" +type AddedToStackEvent implements Node @docsCategory(name: "pulls") { + """ + Identifies the actor who performed the event. + """ + actor: Actor + + """ + Identifies the date and time when the object was created. + """ + createdAt: DateTime! + + """ + The Node ID of the AddedToStackEvent object + """ + id: ID! + + """ + Pull request added to the stack. + """ + pullRequest: PullRequest + + """ + Database ID of the stack. + """ + stackId: BigInt + + """ + Number of the stack in its repository. + """ + stackNumber: Int +} + """ Represents configuration for assigning Copilot to an issue (public variant) """ @@ -27631,8 +27666,7 @@ type Mutation @docsCategory(name: "meta") { ): ArchiveProjectV2ItemPayload @docsCategory(name: "projects") """ - Archive a pull request. Closes, locks, and marks the pull request as archived. - Only repository admins can archive pull requests. + Archive a pull request. Closes and marks the pull request as archived. Only repository admins can archive pull requests. """ archivePullRequest( """ @@ -29153,6 +29187,16 @@ type Mutation @docsCategory(name: "meta") { input: RerequestCheckSuiteInput! ): RerequestCheckSuitePayload @docsCategory(name: "checks") + """ + Rerequest reviews on a pull request. + """ + rerequestReviews( + """ + Parameters for RerequestReviews + """ + input: RerequestReviewsInput! + ): RerequestReviewsPayload @docsCategory(name: "pulls") + """ Marks a review thread as resolved. """ @@ -47462,6 +47506,7 @@ union PullRequestTimelineItems @docsCategory(name: "pulls") = | AddedToMergeQueueEvent | AddedToProjectEvent | AddedToProjectV2Event + | AddedToStackEvent | AssignedEvent | AutoMergeDisabledEvent | AutoMergeEnabledEvent @@ -47521,6 +47566,7 @@ union PullRequestTimelineItems @docsCategory(name: "pulls") = | RemovedFromMergeQueueEvent | RemovedFromProjectEvent | RemovedFromProjectV2Event + | RemovedFromStackEvent | RenamedTitleEvent | ReopenedEvent | ReviewDismissedEvent @@ -48522,6 +48568,11 @@ type Query implements Node @docsCategory(name: "meta") { """ identifier: SecurityAdvisoryIdentifierFilter + """ + Filter advisories by withdrawn status. True returns only withdrawn advisories. False excludes withdrawn advisories. + """ + isWithdrawn: Boolean + """ Returns the last _n_ elements from the list. """ @@ -48537,6 +48588,11 @@ type Query implements Node @docsCategory(name: "meta") { """ publishedSince: DateTime + """ + A list of advisory severities to filter advisories by. + """ + severities: [SecurityAdvisorySeverity!] + """ Filter advisories to those updated since a time in the past. """ @@ -50787,6 +50843,41 @@ type RemovedFromProjectV2Event implements Node & ProjectV2Event @docsCategory(na wasAutomated: Boolean! } +""" +Represents a 'removed_from_stack' event on a given pull request. +""" +type RemovedFromStackEvent implements Node @docsCategory(name: "pulls") { + """ + Identifies the actor who performed the event. + """ + actor: Actor + + """ + Identifies the date and time when the object was created. + """ + createdAt: DateTime! + + """ + The Node ID of the RemovedFromStackEvent object + """ + id: ID! + + """ + Pull request removed from the stack. + """ + pullRequest: PullRequest + + """ + Database ID of the stack. + """ + stackId: BigInt + + """ + Number of the stack in its repository. + """ + stackNumber: Int +} + """ Represents a 'renamed' event on a given issue or pull request """ @@ -59634,6 +59725,61 @@ type RerequestCheckSuitePayload { clientMutationId: String } +""" +Autogenerated input type of RerequestReviews +""" +input RerequestReviewsInput { + """ + The Node IDs of the bots to rerequest. + """ + botIds: [ID!] @possibleTypes(concreteTypes: ["Bot"]) + + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + The Node ID of the pull request to modify. + """ + pullRequestId: ID! @possibleTypes(concreteTypes: ["PullRequest"]) + + """ + The Node IDs of the teams to rerequest. + """ + teamIds: [ID!] @possibleTypes(concreteTypes: ["EnterpriseTeam", "Team"], abstractType: "TeamReviewRequestable") + + """ + The Node IDs of the users to rerequest. + """ + userIds: [ID!] @possibleTypes(concreteTypes: ["User"]) +} + +""" +Autogenerated return type of RerequestReviews. +""" +type RerequestReviewsPayload { + """ + Identifies the actor who performed the event. + """ + actor: Actor + + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + The pull request that is getting requests. + """ + pullRequest: PullRequest + + """ + The edge from the pull request to the requested reviewers. + """ + requestedReviewersEdge: UserEdge +} + """ Autogenerated input type of ResolveReviewThread """ @@ -60631,6 +60777,11 @@ type SecurityAdvisory implements Node @docsCategory(name: "security-advisories") """ classification: SecurityAdvisoryClassification! + """ + The Common Vulnerabilities and Exposures ID + """ + cveId: String + """ The CVSS associated with this advisory """ @@ -60689,6 +60840,11 @@ type SecurityAdvisory implements Node @docsCategory(name: "security-advisories") """ ghsaId: String! + """ + When the GitHub Security team reviewed this advisory, which may differ from when GitHub published it + """ + githubReviewedAt: DateTime + """ The Node ID of the SecurityAdvisory object """ @@ -60704,6 +60860,11 @@ type SecurityAdvisory implements Node @docsCategory(name: "security-advisories") """ notificationsPermalink: URI + """ + When NVD published its advisory record, independent of GitHub's publishing timeline + """ + nvdPublishedAt: DateTime + """ The organization that originated the advisory """ @@ -60724,11 +60885,21 @@ type SecurityAdvisory implements Node @docsCategory(name: "security-advisories") """ references: [SecurityAdvisoryReference!]! + """ + The URL for the associated repository security advisory, if this global advisory is linked to a repository-level advisory. + """ + repositoryAdvisoryUrl: URI + """ The severity of the advisory """ severity: SecurityAdvisorySeverity! + """ + URL to the upstream source code relevant to the advisory + """ + sourceCodeLocation: URI + """ A short plaintext summary of the advisory """