diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 4286694bb2d1..a2659418df48 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -78,7 +78,7 @@ For complete style guidance, see our [style guide](https://docs.github.com/en/co -**Make changes in a codespace:** See "[Working in a codespace](https://github.com/github/docs/blob/main/contributing/codespace.md)" for documentation-specific setup. +**Make changes in a codespace:** See "[Working on GitHub Docs in a codespace](https://docs.github.com/en/contributing/setting-up-your-environment-to-work-on-github-docs/working-on-github-docs-in-a-codespace)" for documentation-specific setup. **Make changes locally:** 1. Fork the repository (see [official forking guide](https://docs.github.com/en/contributing)) diff --git a/.github/workflows/all-documents.yml b/.github/workflows/all-documents.yml index f52aebfb8374..1f0101e770ed 100644 --- a/.github/workflows/all-documents.yml +++ b/.github/workflows/all-documents.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/article-api-docs.yml b/.github/workflows/article-api-docs.yml index b856b43b8b3b..8bb96924ab4d 100644 --- a/.github/workflows/article-api-docs.yml +++ b/.github/workflows/article-api-docs.yml @@ -22,7 +22,7 @@ jobs: if: github.repository == 'github/docs-internal' || github.repository == 'github/docs' steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/auto-add-ready-for-doc-review.yml b/.github/workflows/auto-add-ready-for-doc-review.yml index 14f91163d2e7..47e74c43aaf0 100644 --- a/.github/workflows/auto-add-ready-for-doc-review.yml +++ b/.github/workflows/auto-add-ready-for-doc-review.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Check team membership id: membership_check diff --git a/.github/workflows/benchmark-pages.yml b/.github/workflows/benchmark-pages.yml index be6d2de96cb7..dc33ed705481 100644 --- a/.github/workflows/benchmark-pages.yml +++ b/.github/workflows/benchmark-pages.yml @@ -21,7 +21,7 @@ jobs: ISSUE_REPO: github/docs-engineering steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: 'false' - name: Generate GitHub App token diff --git a/.github/workflows/changelog-agent.yml b/.github/workflows/changelog-agent.yml index 5f58f6fc4826..5a326ba3b522 100644 --- a/.github/workflows/changelog-agent.yml +++ b/.github/workflows/changelog-agent.yml @@ -743,7 +743,7 @@ jobs: # Local composite actions below require the repository to be checked out. - name: Check out repo if: ${{ failure() && github.event_name != 'workflow_dispatch' }} - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/slack-alert if: ${{ failure() && github.event_name != 'workflow_dispatch' }} diff --git a/.github/workflows/close-on-invalid-label.yaml b/.github/workflows/close-on-invalid-label.yaml index 0e808effc1f9..654d97402bdc 100644 --- a/.github/workflows/close-on-invalid-label.yaml +++ b/.github/workflows/close-on-invalid-label.yaml @@ -39,7 +39,7 @@ jobs: - name: Check out repo if: ${{ failure() && github.event_name != 'pull_request_target' }} - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/slack-alert if: ${{ failure() && github.event_name != 'pull_request_target' }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3f5bdad4cc95..905406b760f5 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -29,7 +29,7 @@ jobs: if: github.repository == 'github/docs-internal' || github.repository == 'github/docs' runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: github/codeql-action/init@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 with: diff --git a/.github/workflows/confirm-internal-staff-work-in-docs.yml b/.github/workflows/confirm-internal-staff-work-in-docs.yml index 039525edb066..bdef245fe393 100644 --- a/.github/workflows/confirm-internal-staff-work-in-docs.yml +++ b/.github/workflows/confirm-internal-staff-work-in-docs.yml @@ -89,7 +89,7 @@ jobs: - name: Check out repo if: ${{ failure() && github.event_name != 'pull_request_target' }} - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/slack-alert if: ${{ failure() && github.event_name != 'pull_request_target' }} with: diff --git a/.github/workflows/content-lint-markdown.yml b/.github/workflows/content-lint-markdown.yml index 80f35ec503a7..adb16aa08720 100644 --- a/.github/workflows/content-lint-markdown.yml +++ b/.github/workflows/content-lint-markdown.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # Fetch 2 commits so tj-actions/changed-files can diff without extra API calls fetch-depth: 2 diff --git a/.github/workflows/content-linter-rules-docs.yml b/.github/workflows/content-linter-rules-docs.yml index 1f0c7ebe9d64..da5de0d478d7 100644 --- a/.github/workflows/content-linter-rules-docs.yml +++ b/.github/workflows/content-linter-rules-docs.yml @@ -25,7 +25,7 @@ jobs: if: github.repository == 'github/docs-internal' || github.repository == 'github/docs' steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/content-pipelines.yml b/.github/workflows/content-pipelines.yml index c33e1ce46856..bb69d006eb9e 100644 --- a/.github/workflows/content-pipelines.yml +++ b/.github/workflows/content-pipelines.yml @@ -41,7 +41,7 @@ jobs: steps: - name: Checkout docs-internal - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index f1cb401ea08f..694548cf43ae 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -29,7 +29,7 @@ jobs: ELASTICSEARCH_URL: http://localhost:9200/ steps: - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 diff --git a/.github/workflows/copy-api-issue-to-internal.yml b/.github/workflows/copy-api-issue-to-internal.yml index 4e1c6243e471..81f24233e866 100644 --- a/.github/workflows/copy-api-issue-to-internal.yml +++ b/.github/workflows/copy-api-issue-to-internal.yml @@ -75,7 +75,7 @@ jobs: - name: Check out repo if: ${{ failure() && github.event_name != 'workflow_dispatch' && github.repository == 'github/docs-internal' }} - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/slack-alert if: ${{ failure() && github.event_name != 'workflow_dispatch' && github.repository == 'github/docs-internal' }} with: diff --git a/.github/workflows/count-translation-corruptions.yml b/.github/workflows/count-translation-corruptions.yml index 1095079dfe8d..14e17b48cb55 100644 --- a/.github/workflows/count-translation-corruptions.yml +++ b/.github/workflows/count-translation-corruptions.yml @@ -33,7 +33,7 @@ jobs: repositories: docs-internal,docs-internal.es-es,docs-internal.ja-jp,docs-internal.pt-br,docs-internal.zh-cn,docs-internal.ru-ru,docs-internal.fr-fr,docs-internal.ko-kr,docs-internal.de-de - name: Checkout English repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # Using a PAT is necessary so that the new commit will trigger the # CI in the PR. (Events from GITHUB_TOKEN don't trigger new workflows.) diff --git a/.github/workflows/create-changelog-pr.yml b/.github/workflows/create-changelog-pr.yml index 324c2bea6a41..a335cb02d939 100644 --- a/.github/workflows/create-changelog-pr.yml +++ b/.github/workflows/create-changelog-pr.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: 'Ensure ${{ env.CHANGELOG_FILE }} exists' run: | diff --git a/.github/workflows/delete-orphan-translation-files.yml b/.github/workflows/delete-orphan-translation-files.yml index c81f0d6de1e9..c5930f322b9d 100644 --- a/.github/workflows/delete-orphan-translation-files.yml +++ b/.github/workflows/delete-orphan-translation-files.yml @@ -60,7 +60,7 @@ jobs: language_repo: github/docs-internal.ko-kr steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 @@ -71,7 +71,7 @@ jobs: repositories: docs-internal.es-es,docs-internal.ja-jp,docs-internal.pt-br,docs-internal.zh-cn,docs-internal.ru-ru,docs-internal.fr-fr,docs-internal.ko-kr,docs-internal.de-de,docs-engineering - name: Checkout the language-specific repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ matrix.language_repo }} token: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/docs-review-collect.yml b/.github/workflows/docs-review-collect.yml index 2d6ff09f556c..b3c49b9d8278 100644 --- a/.github/workflows/docs-review-collect.yml +++ b/.github/workflows/docs-review-collect.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Check out repo content - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Setup Node.js uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 diff --git a/.github/workflows/dont-delete-assets.yml b/.github/workflows/dont-delete-assets.yml index 3d66d1c6dc82..3c3e73d4cacf 100644 --- a/.github/workflows/dont-delete-assets.yml +++ b/.github/workflows/dont-delete-assets.yml @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/dont-delete-features.yml b/.github/workflows/dont-delete-features.yml index 9790d399ed2a..5717e5f670b8 100644 --- a/.github/workflows/dont-delete-features.yml +++ b/.github/workflows/dont-delete-features.yml @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/enterprise-dates.yml b/.github/workflows/enterprise-dates.yml index 047618db6539..1c5419174732 100644 --- a/.github/workflows/enterprise-dates.yml +++ b/.github/workflows/enterprise-dates.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 diff --git a/.github/workflows/enterprise-release-issue.yml b/.github/workflows/enterprise-release-issue.yml index 5b95ca5864d8..2595d2e35079 100644 --- a/.github/workflows/enterprise-release-issue.yml +++ b/.github/workflows/enterprise-release-issue.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 diff --git a/.github/workflows/first-responder-v2-prs-collect.yml b/.github/workflows/first-responder-v2-prs-collect.yml index de07121825db..ff0d2aba47c7 100644 --- a/.github/workflows/first-responder-v2-prs-collect.yml +++ b/.github/workflows/first-responder-v2-prs-collect.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 # Add to the FR project # and set type to "Maintenance" diff --git a/.github/workflows/generate-code-scanning-query-lists.yml b/.github/workflows/generate-code-scanning-query-lists.yml index e3fd557f6014..ea466bf9f2de 100644 --- a/.github/workflows/generate-code-scanning-query-lists.yml +++ b/.github/workflows/generate-code-scanning-query-lists.yml @@ -32,12 +32,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup - name: Checkout codeql repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: github/codeql path: codeql @@ -98,12 +98,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup - name: Checkout codeql repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: github/codeql path: codeql @@ -164,10 +164,10 @@ jobs: needs: [generate-security-query-lists, generate-quality-query-lists] steps: - name: Checkout repository code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Checkout codeql repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: github/codeql path: codeql diff --git a/.github/workflows/headless-tests.yml b/.github/workflows/headless-tests.yml index aa0455cfb49c..54e844c36906 100644 --- a/.github/workflows/headless-tests.yml +++ b/.github/workflows/headless-tests.yml @@ -38,7 +38,7 @@ jobs: timeout-minutes: 60 steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/setup-elasticsearch diff --git a/.github/workflows/hubber-contribution-help.yml b/.github/workflows/hubber-contribution-help.yml index 67f711caaa53..7fb5e989b3cf 100644 --- a/.github/workflows/hubber-contribution-help.yml +++ b/.github/workflows/hubber-contribution-help.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - id: membership_check uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 diff --git a/.github/workflows/index-autocomplete-search.yml b/.github/workflows/index-autocomplete-search.yml index 92816907dd78..ac53708955aa 100644 --- a/.github/workflows/index-autocomplete-search.yml +++ b/.github/workflows/index-autocomplete-search.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup @@ -39,7 +39,7 @@ jobs: - uses: ./.github/actions/setup-elasticsearch if: ${{ github.event_name == 'pull_request' }} - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: token: ${{ steps.app-token.outputs.token }} repository: github/docs-internal-data diff --git a/.github/workflows/index-general-search-pr.yml b/.github/workflows/index-general-search-pr.yml index 55ab5d56bb20..34e5647092f4 100644 --- a/.github/workflows/index-general-search-pr.yml +++ b/.github/workflows/index-general-search-pr.yml @@ -37,7 +37,7 @@ jobs: if: github.repository == 'github/docs-internal' steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token @@ -49,7 +49,7 @@ jobs: repositories: docs-internal-data - name: Clone docs-internal-data - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: github/docs-internal-data # This works because user `docs-bot` has read access to that private repo. diff --git a/.github/workflows/index-general-search.yml b/.github/workflows/index-general-search.yml index c1e66d85d4df..235df429b114 100644 --- a/.github/workflows/index-general-search.yml +++ b/.github/workflows/index-general-search.yml @@ -102,7 +102,7 @@ jobs: - name: Check out repo if: ${{ failure() && github.event_name != 'workflow_dispatch' }} - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/slack-alert if: ${{ failure() && github.event_name != 'workflow_dispatch' }} @@ -134,7 +134,7 @@ jobs: language: ${{ fromJSON(needs.figureOutMatrix.outputs.matrix) }} steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 @@ -145,7 +145,7 @@ jobs: repositories: docs-internal-data,docs-internal.es-es,docs-internal.ja-jp,docs-internal.pt-br,docs-internal.zh-cn,docs-internal.ru-ru,docs-internal.fr-fr,docs-internal.ko-kr,docs-internal.de-de,docs-engineering - name: Clone docs-internal-data - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: github/docs-internal-data # This works because user `docs-bot` has read access to that private repo. @@ -275,7 +275,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 diff --git a/.github/workflows/keep-caches-warm.yml b/.github/workflows/keep-caches-warm.yml index be8f82dfc02b..54971c3a6e60 100644 --- a/.github/workflows/keep-caches-warm.yml +++ b/.github/workflows/keep-caches-warm.yml @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup - uses: ./.github/actions/cache-nextjs diff --git a/.github/workflows/line-endings.yml b/.github/workflows/line-endings.yml index 5ef81767bd12..dc52c09925f9 100644 --- a/.github/workflows/line-endings.yml +++ b/.github/workflows/line-endings.yml @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Check line endings # Re-stage every file through the `.gitattributes` filters and fail if any diff --git a/.github/workflows/link-check-external.yml b/.github/workflows/link-check-external.yml index a8440699ae87..4b86ef5eb2cf 100644 --- a/.github/workflows/link-check-external.yml +++ b/.github/workflows/link-check-external.yml @@ -20,7 +20,7 @@ jobs: timeout-minutes: 180 # 3 hours for external checks steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 diff --git a/.github/workflows/link-check-github-github.yml b/.github/workflows/link-check-github-github.yml index b48532edd83e..7dc07133761f 100644 --- a/.github/workflows/link-check-github-github.yml +++ b/.github/workflows/link-check-github-github.yml @@ -22,7 +22,7 @@ jobs: REPORT_REPOSITORY: github/docs-content steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # To prevent issues with cloning early access content later persist-credentials: 'false' diff --git a/.github/workflows/link-check-internal.yml b/.github/workflows/link-check-internal.yml index 1d8a394e6bae..7be5c087eaa1 100644 --- a/.github/workflows/link-check-internal.yml +++ b/.github/workflows/link-check-internal.yml @@ -37,7 +37,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 @@ -87,7 +87,7 @@ jobs: ELASTICSEARCH_URL: '' steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 @@ -221,7 +221,7 @@ jobs: issues: write steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 diff --git a/.github/workflows/link-check-on-pr.yml b/.github/workflows/link-check-on-pr.yml index 6d77c5627e6a..aadb81733bd6 100644 --- a/.github/workflows/link-check-on-pr.yml +++ b/.github/workflows/link-check-on-pr.yml @@ -27,7 +27,7 @@ jobs: if: github.repository == 'github/docs-internal' || github.repository == 'github/docs' steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # Fetch 2 commits so tj-actions/changed-files can diff without extra API calls fetch-depth: 2 diff --git a/.github/workflows/lint-code.yml b/.github/workflows/lint-code.yml index e64b584d4e66..3600a02cbeb3 100644 --- a/.github/workflows/lint-code.yml +++ b/.github/workflows/lint-code.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/lint-entire-content-data-markdown.yml b/.github/workflows/lint-entire-content-data-markdown.yml index 86e308e0ecdf..10d31c5f4430 100644 --- a/.github/workflows/lint-entire-content-data-markdown.yml +++ b/.github/workflows/lint-entire-content-data-markdown.yml @@ -32,7 +32,7 @@ jobs: run: gh --version - name: Check out repo's default branch - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Set up Node and dependencies uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/local-dev.yml b/.github/workflows/local-dev.yml index d6506a713195..e75217962e0a 100644 --- a/.github/workflows/local-dev.yml +++ b/.github/workflows/local-dev.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/moda-allowed-ips.yml b/.github/workflows/moda-allowed-ips.yml index 796f5cfbbc26..fc1675a882e7 100644 --- a/.github/workflows/moda-allowed-ips.yml +++ b/.github/workflows/moda-allowed-ips.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out the repository - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Update list of allowed IPs run: | echo "Getting a list of Fastly IP addresses...." diff --git a/.github/workflows/move-content.yml b/.github/workflows/move-content.yml index 9e85604d7600..bfd9147230ab 100644 --- a/.github/workflows/move-content.yml +++ b/.github/workflows/move-content.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/move-ready-to-merge-pr.yaml b/.github/workflows/move-ready-to-merge-pr.yaml index ffb43d09ffeb..97d03d17b14b 100644 --- a/.github/workflows/move-ready-to-merge-pr.yaml +++ b/.github/workflows/move-ready-to-merge-pr.yaml @@ -40,7 +40,7 @@ jobs: value: Triage - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/move-reopened-issues-to-triage.yaml b/.github/workflows/move-reopened-issues-to-triage.yaml index 6c7c990d3ac0..233674b2b297 100644 --- a/.github/workflows/move-reopened-issues-to-triage.yaml +++ b/.github/workflows/move-reopened-issues-to-triage.yaml @@ -45,7 +45,7 @@ jobs: - name: Check out repo if: ${{ failure() }} - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/slack-alert if: ${{ failure() }} with: diff --git a/.github/workflows/needs-sme-stale-check.yaml b/.github/workflows/needs-sme-stale-check.yaml index 150b3d54c487..facfbee4920e 100644 --- a/.github/workflows/needs-sme-stale-check.yaml +++ b/.github/workflows/needs-sme-stale-check.yaml @@ -35,7 +35,7 @@ jobs: - name: Check out repo if: ${{ failure() }} - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/slack-alert if: ${{ failure() }} with: diff --git a/.github/workflows/needs-sme-workflow.yml b/.github/workflows/needs-sme-workflow.yml index e136add25d8a..f8e6fb7d9ba3 100644 --- a/.github/workflows/needs-sme-workflow.yml +++ b/.github/workflows/needs-sme-workflow.yml @@ -22,7 +22,7 @@ jobs: issues: write steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 with: @@ -42,7 +42,7 @@ jobs: pull-requests: write steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 with: diff --git a/.github/workflows/no-response.yaml b/.github/workflows/no-response.yaml index 8eb5ef6835c3..3aa6655d60a6 100644 --- a/.github/workflows/no-response.yaml +++ b/.github/workflows/no-response.yaml @@ -57,7 +57,7 @@ jobs: - name: Check out repo if: ${{ failure() }} - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/slack-alert if: ${{ failure() }} with: diff --git a/.github/workflows/notify-about-deployment.yml b/.github/workflows/notify-about-deployment.yml index d635559b34a9..5e7caf05c919 100644 --- a/.github/workflows/notify-about-deployment.yml +++ b/.github/workflows/notify-about-deployment.yml @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 diff --git a/.github/workflows/notify-release-pms.yml b/.github/workflows/notify-release-pms.yml index 24dc618b76a4..2e6fa9972363 100644 --- a/.github/workflows/notify-release-pms.yml +++ b/.github/workflows/notify-release-pms.yml @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/orphaned-features-check.yml b/.github/workflows/orphaned-features-check.yml index 9069eb9db6bf..12c3c739f1cb 100644 --- a/.github/workflows/orphaned-features-check.yml +++ b/.github/workflows/orphaned-features-check.yml @@ -35,7 +35,7 @@ jobs: repositories: docs-internal,docs-internal.es-es,docs-internal.ja-jp,docs-internal.pt-br,docs-internal.zh-cn,docs-internal.ru-ru,docs-internal.fr-fr,docs-internal.ko-kr,docs-internal.de-de - name: Checkout English repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # Using a PAT is necessary so that the new commit will trigger the # CI in the PR. (Events from GITHUB_TOKEN don't trigger new workflows.) diff --git a/.github/workflows/orphaned-files-check.yml b/.github/workflows/orphaned-files-check.yml index cfcfa2928fbb..b2cf7ddeceaf 100644 --- a/.github/workflows/orphaned-files-check.yml +++ b/.github/workflows/orphaned-files-check.yml @@ -40,7 +40,7 @@ jobs: repositories: docs-internal,docs-internal.es-es,docs-internal.ja-jp,docs-internal.pt-br,docs-internal.zh-cn,docs-internal.ru-ru,docs-internal.fr-fr,docs-internal.ko-kr,docs-internal.de-de - name: Checkout English repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # Using a PAT is necessary so that the new commit will trigger the # CI in the PR. (Events from GITHUB_TOKEN don't trigger new workflows.) diff --git a/.github/workflows/os-ready-for-review.yml b/.github/workflows/os-ready-for-review.yml index 9fe64913e680..cd3de5616061 100644 --- a/.github/workflows/os-ready-for-review.yml +++ b/.github/workflows/os-ready-for-review.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo content - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Check if this run was triggered by a member of the docs team uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 diff --git a/.github/workflows/package-lock-lint.yml b/.github/workflows/package-lock-lint.yml index dcc3ffa41f9c..da7f7cae419e 100644 --- a/.github/workflows/package-lock-lint.yml +++ b/.github/workflows/package-lock-lint.yml @@ -25,7 +25,7 @@ jobs: if: github.repository == 'github/docs-internal' || github.repository == 'github/docs' steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Setup Node.js uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 diff --git a/.github/workflows/purge-fastly.yml b/.github/workflows/purge-fastly.yml index 660a26098fb9..84256702d1df 100644 --- a/.github/workflows/purge-fastly.yml +++ b/.github/workflows/purge-fastly.yml @@ -56,7 +56,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/readability.yml b/.github/workflows/readability.yml index 505e5ecaa62c..0c22193d0f6b 100644 --- a/.github/workflows/readability.yml +++ b/.github/workflows/readability.yml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # Fetch 2 commits so tj-actions/changed-files can diff without extra API calls fetch-depth: 2 diff --git a/.github/workflows/ready-for-doc-review.yml b/.github/workflows/ready-for-doc-review.yml index 534402a63740..3b465702ba15 100644 --- a/.github/workflows/ready-for-doc-review.yml +++ b/.github/workflows/ready-for-doc-review.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo content - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: github/docs-internal token: ${{ secrets.DOCS_BOT_PAT_BASE }} diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml index f1c4d100cb0d..685c92415a70 100644 --- a/.github/workflows/repo-sync.yml +++ b/.github/workflows/repo-sync.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Sync repo to branch diff --git a/.github/workflows/review-comment.yml b/.github/workflows/review-comment.yml index 6182eb3816af..e0306a695928 100644 --- a/.github/workflows/review-comment.yml +++ b/.github/workflows/review-comment.yml @@ -38,7 +38,7 @@ jobs: PR_NUMBER: ${{ github.event.pull_request.number }} steps: - name: check out repo content - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup - name: Set APP_URL diff --git a/.github/workflows/reviewers-content-systems.yml b/.github/workflows/reviewers-content-systems.yml index 03c585c12413..64e10a2f02ce 100644 --- a/.github/workflows/reviewers-content-systems.yml +++ b/.github/workflows/reviewers-content-systems.yml @@ -35,7 +35,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token diff --git a/.github/workflows/reviewers-dependabot.yml b/.github/workflows/reviewers-dependabot.yml index 542b9a5f7663..a28264b6a5fe 100644 --- a/.github/workflows/reviewers-dependabot.yml +++ b/.github/workflows/reviewers-dependabot.yml @@ -36,7 +36,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token diff --git a/.github/workflows/reviewers-docs-engineering.yml b/.github/workflows/reviewers-docs-engineering.yml index 98f233cf010e..89e47ab00b1b 100644 --- a/.github/workflows/reviewers-docs-engineering.yml +++ b/.github/workflows/reviewers-docs-engineering.yml @@ -48,7 +48,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token diff --git a/.github/workflows/reviewers-legal.yml b/.github/workflows/reviewers-legal.yml index b104f193c1b2..fc3a1702c505 100644 --- a/.github/workflows/reviewers-legal.yml +++ b/.github/workflows/reviewers-legal.yml @@ -32,7 +32,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # Fetch 2 commits so tj-actions/changed-files can diff without extra API calls fetch-depth: 2 diff --git a/.github/workflows/site-policy-sync.yml b/.github/workflows/site-policy-sync.yml index 8598691c332b..1d6901cda8f2 100644 --- a/.github/workflows/site-policy-sync.yml +++ b/.github/workflows/site-policy-sync.yml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout docs-internal - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token @@ -39,7 +39,7 @@ jobs: repositories: site-policy - name: checkout public site-policy - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: github/site-policy token: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 03636e6f4163..c2127a4afdd6 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -38,7 +38,7 @@ jobs: - name: Check out repo if: ${{ failure() }} - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/slack-alert if: ${{ failure() }} with: diff --git a/.github/workflows/sync-audit-logs.yml b/.github/workflows/sync-audit-logs.yml index 3dc3a8de920b..01bd38d86ef6 100644 --- a/.github/workflows/sync-audit-logs.yml +++ b/.github/workflows/sync-audit-logs.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 diff --git a/.github/workflows/sync-codeql-cli.yml b/.github/workflows/sync-codeql-cli.yml index 0aecdcf207bf..b19fc0821884 100644 --- a/.github/workflows/sync-codeql-cli.yml +++ b/.github/workflows/sync-codeql-cli.yml @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 @@ -42,7 +42,7 @@ jobs: # Check out a nested repository inside of previous checkout - name: Checkout semmle-code repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # By default, only the most recent commit of the `main` branch # will be checked out diff --git a/.github/workflows/sync-graphql.yml b/.github/workflows/sync-graphql.yml index b4521febafe6..a82f51acb37a 100644 --- a/.github/workflows/sync-graphql.yml +++ b/.github/workflows/sync-graphql.yml @@ -25,7 +25,7 @@ jobs: ignored-types: ${{ steps.sync.outputs.ignored-types }} steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 @@ -97,7 +97,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 diff --git a/.github/workflows/sync-llms-txt.yml b/.github/workflows/sync-llms-txt.yml index 36ca5d06d8bf..0c9501cb648b 100644 --- a/.github/workflows/sync-llms-txt.yml +++ b/.github/workflows/sync-llms-txt.yml @@ -35,7 +35,7 @@ jobs: BRANCH: sync-llms-txt steps: - name: Checkout docs-internal - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Generate GitHub App token @@ -105,8 +105,17 @@ jobs: git config user.name "docs-bot" git config user.email "77750099+docs-bot@users.noreply.github.com" git add data/llms-txt/docs.md - git commit -m "Update data/llms-txt/docs.md from popularity data" - git push "https://x-access-token:${GH_TOKEN}@github.com/github/docs-internal.git" "$BRANCH" + # diff_docs compares against main, but the sync branch may already + # exist with this exact content (open PR from a prior run). In that + # case there is nothing new to stage, and `git commit` would exit 1 + # and fail the whole workflow. Skip the commit and push when the + # branch is already up to date. + if git diff --cached --quiet; then + echo "Sync branch already has the latest generated docs.md; nothing to commit." + else + git commit -m "Update data/llms-txt/docs.md from popularity data" + git push "https://x-access-token:${GH_TOKEN}@github.com/github/docs-internal.git" "$BRANCH" + fi - name: Create or update docs-internal PR if: steps.diff_docs.outputs.changed == 'true' diff --git a/.github/workflows/sync-openapi.yml b/.github/workflows/sync-openapi.yml index a8e596864ee3..61a46d6bf060 100644 --- a/.github/workflows/sync-openapi.yml +++ b/.github/workflows/sync-openapi.yml @@ -30,11 +30,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 # Check out a nested repository inside of previous checkout - name: Checkout rest-api-description repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # By default, only the most recent commit of the `main` branch # will be checked out @@ -42,7 +42,7 @@ jobs: path: rest-api-description ref: ${{ inputs.SOURCE_BRANCH }} - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # By default, only the most recent commit of the `main` branch # will be checked out diff --git a/.github/workflows/sync-sdk-docs.yml b/.github/workflows/sync-sdk-docs.yml index 225ed5687897..d94684809603 100644 --- a/.github/workflows/sync-sdk-docs.yml +++ b/.github/workflows/sync-sdk-docs.yml @@ -47,7 +47,7 @@ jobs: steps: - name: Checkout docs-internal - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Fetch SDK docs from copilot-sdk env: diff --git a/.github/workflows/sync-secret-scanning.yml b/.github/workflows/sync-secret-scanning.yml index 22ac69b54d40..099e99b75a36 100644 --- a/.github/workflows/sync-secret-scanning.yml +++ b/.github/workflows/sync-secret-scanning.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 diff --git a/.github/workflows/test-changed-content.yml b/.github/workflows/test-changed-content.yml index 3b1f05f8de58..ef25f3f94677 100644 --- a/.github/workflows/test-changed-content.yml +++ b/.github/workflows/test-changed-content.yml @@ -27,7 +27,7 @@ jobs: # Each of these ifs needs to be repeated at each step to make sure the required check still runs # Even if if doesn't do anything - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # Fetch 2 commits so tj-actions/changed-files can diff without extra API calls fetch-depth: 2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9fa3b501db76..13977e4c30c0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -88,7 +88,7 @@ jobs: # Each of these ifs needs to be repeated at each step to make sure the required check still runs # Even if if doesn't do anything - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # Fetch 2 commits so tj-actions/changed-files can diff without extra API calls fetch-depth: 2 diff --git a/.github/workflows/triage-issue-comments.yml b/.github/workflows/triage-issue-comments.yml index fa2efee04bc2..fb83074ebb81 100644 --- a/.github/workflows/triage-issue-comments.yml +++ b/.github/workflows/triage-issue-comments.yml @@ -43,7 +43,7 @@ jobs: } - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/triage-issues.yml b/.github/workflows/triage-issues.yml index e48f13d7e7a4..55b333e5413c 100644 --- a/.github/workflows/triage-issues.yml +++ b/.github/workflows/triage-issues.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/triage-pull-requests.yml b/.github/workflows/triage-pull-requests.yml index 002da127dd66..39419711d2c5 100644 --- a/.github/workflows/triage-pull-requests.yml +++ b/.github/workflows/triage-pull-requests.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/triage-stale-check.yml b/.github/workflows/triage-stale-check.yml index 9b20ce683aa7..ffb3ef481181 100644 --- a/.github/workflows/triage-stale-check.yml +++ b/.github/workflows/triage-stale-check.yml @@ -45,7 +45,7 @@ jobs: - name: Check out repo if: ${{ failure() }} - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/slack-alert if: ${{ failure() }} with: @@ -90,7 +90,7 @@ jobs: - name: Check out repo if: ${{ failure() }} - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/slack-alert if: ${{ failure() }} with: diff --git a/.github/workflows/triage-unallowed-contributions.yml b/.github/workflows/triage-unallowed-contributions.yml index f15768af2d65..2c7b354a31e2 100644 --- a/.github/workflows/triage-unallowed-contributions.yml +++ b/.github/workflows/triage-unallowed-contributions.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Get files changed uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 diff --git a/.github/workflows/validate-asset-images.yml b/.github/workflows/validate-asset-images.yml index 23effc21b63d..e29eb9eec307 100644 --- a/.github/workflows/validate-asset-images.yml +++ b/.github/workflows/validate-asset-images.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: ./.github/actions/node-npm-setup diff --git a/.github/workflows/validate-github-github-docs-urls.yml b/.github/workflows/validate-github-github-docs-urls.yml index d92f775d82f8..aea43103492b 100644 --- a/.github/workflows/validate-github-github-docs-urls.yml +++ b/.github/workflows/validate-github-github-docs-urls.yml @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo's default branch - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 @@ -46,7 +46,7 @@ jobs: - uses: ./.github/actions/node-npm-setup - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: token: ${{ steps.app-token.outputs.token }} repository: github/github diff --git a/.github/workflows/validate-openapi-check.yml b/.github/workflows/validate-openapi-check.yml index d258207e2351..b602126df401 100644 --- a/.github/workflows/validate-openapi-check.yml +++ b/.github/workflows/validate-openapi-check.yml @@ -28,7 +28,7 @@ jobs: if: github.repository == 'github/docs-internal' steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 63a7e6ffe6f8..2b13f0935714 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false diff --git a/Dockerfile b/Dockerfile index a593f03633b4..efe89c333620 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ # --------------------------------------------------------------- # To update the sha: # https://github.com/github/gh-base-image/pkgs/container/gh-base-image%2Fgh-base-noble -FROM ghcr.io/github/gh-base-image/gh-base-noble:20260630-205522-g4a33dc6bb@sha256:2b8edd7da1718e2e9c25b98fd31dae58e57e756ef19842eac212255765cf4ada AS base +FROM ghcr.io/github/gh-base-image/gh-base-noble:20260706-152702-gdb3226338@sha256:badfd5ddfbea7896bb84dbfc20ae326234fa1cc580bb723458158e8a59cf17ec AS base # Install curl for Node install and determining the early access branch # Install git for cloning docs-early-access & translations repos diff --git a/assets/images/README.md b/assets/images/README.md index 9756be1695c9..73eafdcc09a2 100644 --- a/assets/images/README.md +++ b/assets/images/README.md @@ -2,4 +2,4 @@ The `/assets/images` directory holds all the site's images. -See [imaging and versioning](https://github.com/github/docs/blob/main/contributing/images-and-versioning.md) from the contributing docs for more information. +See [Creating screenshots](https://docs.github.com/en/contributing/writing-for-github-docs/creating-screenshots) from the contributing docs for more information. diff --git a/content/actions/reference/runners/self-hosted-runners.md b/content/actions/reference/runners/self-hosted-runners.md index 4cf1c86960ae..4a46f0c179d8 100644 --- a/content/actions/reference/runners/self-hosted-runners.md +++ b/content/actions/reference/runners/self-hosted-runners.md @@ -195,7 +195,7 @@ Self-hosted runners connect to {% ifversion fpt or ghec %}{% data variables.prod In addition, your workflow may require access to other network resources. -If you use an IP address allow list for your {% data variables.product.prodname_dotcom %} organization or enterprise account, you must add your self-hosted runner's IP address to the allow list. See [Managing allowed IP addresses for your organization](/{% ifversion fpt %}enterprise-cloud@latest/{% endif %}/organizations/keeping-your-organization-secure/managing-allowed-ip-addresses-for-your-organization#using-github-actions-with-an-ip-allow-list) or [Enforcing policies for security settings in your enterprise](/{% ifversion fpt %}enterprise-cloud@latest/{% endif %}admin/policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-security-settings-in-your-enterprise){% ifversion fpt %} in the {% data variables.product.prodname_ghe_cloud %} documentation.{% else %}.{% endif %} +If you use an IP address allow list for your {% data variables.product.prodname_dotcom %} organization or enterprise account, you must add your self-hosted runner's IP address to the allow list. See [Managing allowed IP addresses for your organization](/{% ifversion fpt %}enterprise-cloud@latest/{% endif %}organizations/keeping-your-organization-secure/managing-allowed-ip-addresses-for-your-organization#using-github-actions-with-an-ip-allow-list) or [AUTOTITLE](/{% ifversion fpt %}enterprise-cloud@latest/{% endif %}admin/policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-security-settings-in-your-enterprise){% ifversion fpt %} in the {% data variables.product.prodname_ghe_cloud %} documentation.{% else %}.{% endif %} {% else %} diff --git a/content/code-security/how-tos/secure-your-supply-chain/manage-your-dependency-security/configure-private-registries.md b/content/code-security/how-tos/secure-your-supply-chain/manage-your-dependency-security/configure-private-registries.md index 1b4f083e8feb..f331c325092f 100644 --- a/content/code-security/how-tos/secure-your-supply-chain/manage-your-dependency-security/configure-private-registries.md +++ b/content/code-security/how-tos/secure-your-supply-chain/manage-your-dependency-security/configure-private-registries.md @@ -407,7 +407,7 @@ If you use a monorepo, the `.npmrc` file should live in the project's root direc You can configure {% data variables.product.prodname_dependabot %} to send all registry requests through a specified base URL. In order for {% data variables.product.prodname_dependabot %} to access a public dependency, the registry must either have a cloned copy of the dependency with the requested version, or allow traffic to fetch from a public registry if the dependency is not available. -If there is no global registry defined in a `.npmrc` file, you can set `replaces-base` to `true` in the `dependabot.yml` file. For more information, see "`replaces-base`" in [Top-level `registries` key](/code-security/how-tos/secure-your-supply-chain/manage-your-dependency-security/dependabot-options-reference#top-level-registries-key). +If there is no global registry defined in a `.npmrc` file, you can set `replaces-base` to `true` in the `dependabot.yml` file. For more information, see "`replaces-base`" in [Top-level `registries` key](/code-security/reference/supply-chain-security/dependabot-options-reference#top-level-registries-key). #### Notes @@ -575,7 +575,7 @@ You can either specify the private registry configuration in the `dependabot.yml ##### Defining the private registry configuration in the `dependabot.yml` file -You can define the private registry configuration in your `dependabot.yml` file. For more information, see [Top-level `registries` key](/code-security/how-tos/secure-your-supply-chain/manage-your-dependency-security/dependabot-options-reference#top-level-registries-key). +You can define the private registry configuration in your `dependabot.yml` file. For more information, see [Top-level `registries` key](/code-security/reference/supply-chain-security/dependabot-options-reference#top-level-registries-key). To ensure that the private registry is listed as the dependency source in the project's `yarn.lock` file, you need to run `yarn install` on a machine with private registry access. Yarn should update the resolved field to include the private registry URL. @@ -612,7 +612,7 @@ As with Yarn Classic, you can either specify the private registry configuration ##### Defining the private registry configuration in the `dependabot.yml` file -You can define the private registry configuration in your `dependabot.yml` file. For more information, see [Top-level `registries` key](/code-security/how-tos/secure-your-supply-chain/manage-your-dependency-security/dependabot-options-reference#top-level-registries-key). +You can define the private registry configuration in your `dependabot.yml` file. For more information, see [Top-level `registries` key](/code-security/reference/supply-chain-security/dependabot-options-reference#top-level-registries-key). To ensure the private registry is listed as the dependency source in the project's `yarn.lock` file, run `yarn install` on a machine with private registry access. Yarn should update the resolved field to include the private registry URL. diff --git a/content/contributing/setting-up-your-environment-to-work-on-github-docs/creating-a-local-environment.md b/content/contributing/setting-up-your-environment-to-work-on-github-docs/creating-a-local-environment.md index c0a9c80fe2a7..04b15ca4bded 100644 --- a/content/contributing/setting-up-your-environment-to-work-on-github-docs/creating-a-local-environment.md +++ b/content/contributing/setting-up-your-environment-to-work-on-github-docs/creating-a-local-environment.md @@ -19,7 +19,7 @@ Today it's a dynamic Node.js webserver powered by Express, using middleware to s The tooling for this site has changed over the years, but many of the tried-and-true authoring conventions of the original Jekyll site have been preserved. * Content is written in Markdown files, which live in the `content` directory. - + * Files in the `data` directory are available to templates via the {% raw %}`{% data %}`{% endraw %} tag. * Markdown files can contain [frontmatter](https://jekyllrb.com/docs/front-matter). * The [`redirect_from`](https://github.com/jekyll/jekyll-redirect-from) Jekyll plugin behavior is supported. diff --git a/content/copilot/concepts/agents/copilot-cli/about-cli-extensions.md b/content/copilot/concepts/agents/copilot-cli/about-cli-extensions.md new file mode 100644 index 000000000000..c14b48911591 --- /dev/null +++ b/content/copilot/concepts/agents/copilot-cli/about-cli-extensions.md @@ -0,0 +1,99 @@ +--- +title: About extensions for {% data variables.copilot.copilot_cli %} +shortTitle: CLI extensions +allowTitleToDifferFromFilename: true +intro: 'Extensions let you add your own tools and slash commands to {% data variables.copilot.copilot_cli %}, using only the SDK that ships with the CLI.' +product: '{% data reusables.gated-features.copilot-cli %}' +versions: + feature: copilot +contentType: concepts +category: + - Learn about Copilot # Copilot discovery page + - Learn about Copilot CLI # Copilot CLI bespoke page +docsTeamMetrics: + - copilot-cli +--- + +{% data reusables.copilot.copilot-cli.cli-extensions-experimental %} + +An extension lets you add your own capabilities to {% data variables.copilot.copilot_cli_short %}. Each extension is a small Node.js module that runs as a separate process alongside your interactive session and connects back to it. Through that connection an extension can add: + +* **Tools** that {% data variables.product.prodname_copilot_short %} can call while it works on your behalf. +* **Slash commands** that you run yourself. + +Because an extension runs real code on your machine, you can add capabilities the built-in tools don't have. For example, an extension can watch what happens in a session and keep a running total across tool calls, which a one-off shell command can't do. + +This article explains how extensions work and how {% data variables.copilot.copilot_cli_short %} handles them. For a hands-on guide to building your own extensions, see [AUTOTITLE](/copilot/tutorials/create-an-extension). + +> [!WARNING] +> Extensions execute on your computer with your privileges. Only load extension code that you trust, in the same way you would only run any other script you didn't write yourself. + +## How extensions are discovered + +When {% data variables.copilot.copilot_cli_short %} starts, it looks for extensions in several locations. Each extension lives in its own subdirectory, and that subdirectory must contain an entry file named `extension.mjs`, `extension.cjs`, or `extension.js`. These are all JavaScript files: the {% data variables.copilot.copilot_cli_short %} runs the entry file directly with Node.js, so an extension must be written in JavaScript—TypeScript and other languages are currently not supported. + +The name of the subdirectory becomes the name of the extension. + +| Source | Location | Availability | +| --- | --- | --- | +| **Project** | `.github/extensions/NAME/` in the current repository | Available to anyone working in that repository. | +| **User** | `~/.copilot/extensions/NAME/` | Available in all your CLI sessions, in every directory. | +| **Plugin** | An installed plugin | Available wherever the plugin is enabled. | + +## Choosing where an extension lives + +Where you put the extension directory determines who can use it: + +* Put it in **`.github/extensions/`** in a repository when the extension is specific to that project and you want to share it with everyone who works there. +* Put it in **`~/.copilot/extensions/`** when you want the extension available in all of your own sessions, regardless of which directory you start the CLI in. + +The two locations follow exactly the same structure—a named subdirectory containing an `extension.mjs` file—so you can move an extension from one to the other simply by relocating its folder. + +## Enabling extensions + +Extensions are currently an experimental feature, so you need to turn on experimental features. You can do this by doing either of the following: + +* Start the CLI with the `--experimental` flag. +* Run the `/experimental on` slash command inside an interactive session. + +## Changing how the CLI handles extensions + +You can limit {% data variables.product.prodname_copilot_short %}'s access to extensions, or turn them off entirely, by using the `/extensions mode` command. When you use this command you get three options: + +* **Load & Augment** (the default)—the CLI runs your extensions, _and_ {% data variables.product.prodname_copilot_short %} can manage them. +* **Load Only**—the CLI runs your extensions, but {% data variables.product.prodname_copilot_short %} cannot manage them. +* **Disabled**—extensions are turned off entirely in the current session and will remain disabled in future sessions until you switch to one of the other two settings. Other current sessions are not affected. + +Any change you make takes effect immediately in the current session: + +* Switching **to Disabled** stops any extensions that are currently running. Their processes are shut down and their tools are no longer available. +* Switching **from Disabled** to either of the other settings starts your extensions. +* Switching between **Load Only** and **Load & Augment** just changes whether {% data variables.product.prodname_copilot_short %} can manage your extensions. + +### What "managing extensions" means + +In **Load & Augment** mode, {% data variables.product.prodname_copilot_short %} is given a small set of extra tools that let it work on the extension system directly, as part of carrying out your requests. Using these tools, {% data variables.product.prodname_copilot_short %} can: + +* **List** the extensions it has discovered and see their status. +* **Inspect** an extension, including a tail of its log file, to help diagnose one that has failed or is misbehaving. +* **Scaffold** a new extension—generate a starter `extension.mjs` file for you to build on. +* **Reload** extensions, so that code it (or you) has just written takes effect without restarting the session. + +This is what makes it possible for you to ask {% data variables.product.prodname_copilot_short %} to build an extension for you. {% data variables.product.prodname_copilot_short %} can create the file, write the code, and reload it, all without leaving the session. + +Choose **Load Only** when you want to keep a known, trusted set of extensions running but don't want {% data variables.product.prodname_copilot_short %} creating, reloading, or otherwise changing extension code—which runs on your machine—as a side effect of its work. Your existing extensions still run, and {% data variables.product.prodname_copilot_short %} can still call the tools they provide; it simply can't manage the extensions themselves. + +## Extensions compared with plugins + +Both extensions and plugins add functionality to {% data variables.copilot.copilot_cli_short %}, but they serve different purposes: + +* An **extension** is a single JavaScript module that you write to add tools and slash commands, backed by code that runs in your session. +* A **plugin** is an installable package that bundles reusable components—such as agents, skills, hooks, and integrations—and can be distributed through a marketplace. + +For more information about plugins, see [AUTOTITLE](/copilot/concepts/agents/copilot-cli/about-cli-plugins). + +## Further reading + +* [AUTOTITLE](/copilot/tutorials/create-an-extension) +* [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-command-reference#slash-commands-in-the-interactive-interface) +* [AUTOTITLE](/copilot/how-tos/copilot-cli) diff --git a/content/copilot/concepts/agents/copilot-cli/index.md b/content/copilot/concepts/agents/copilot-cli/index.md index e9eb3de15373..28da0d19082d 100644 --- a/content/copilot/concepts/agents/copilot-cli/index.md +++ b/content/copilot/concepts/agents/copilot-cli/index.md @@ -10,6 +10,7 @@ children: - /comparing-cli-features - /copilot-cli-in-github-actions - /cancel-and-roll-back + - /context-management - /about-remote-control - /about-custom-agents - /autopilot @@ -18,7 +19,7 @@ children: - /chronicle - /rubber-duck - /lsp-servers - - /context-management + - /about-cli-extensions - /tool-search contentType: concepts docsTeamMetrics: diff --git a/content/copilot/concepts/models/auto-model-selection.md b/content/copilot/concepts/models/auto-model-selection.md index cfdb70f72230..8b49b184b1aa 100644 --- a/content/copilot/concepts/models/auto-model-selection.md +++ b/content/copilot/concepts/models/auto-model-selection.md @@ -19,7 +19,7 @@ More than just a model picker, {% data variables.copilot.copilot_auto_model_sele ### Auto with task optimization -> [!NOTE] {% data variables.copilot.copilot_auto_model_selection_short_cap_a %} with task optimization is generally available in {% data variables.copilot.copilot_chat_short %} on the {% data variables.product.github %} website, in {% data variables.product.prodname_vscode_shortname %}, and in {% data variables.copilot.copilot_cli_short %}. +> [!NOTE] {% data variables.copilot.copilot_auto_model_selection_short_cap_a %} with task optimization is generally available in {% data variables.copilot.copilot_chat_short %} on the {% data variables.product.github %} website, in {% data variables.product.prodname_vscode_shortname %}, in {% data variables.copilot.copilot_cli_short %}, and in {% data variables.copilot.github_copilot_app %}. {% data variables.copilot.copilot_auto_model_selection_short_cap_a %} with task optimization combines two systems to provide high quality results and better reliability. One system tracks real-time system health and availability, while the other evaluates task complexity. Putting these together, {% data variables.copilot.copilot_auto_model_selection_short %} routes the task to the optimal model. @@ -60,22 +60,23 @@ When you select **Auto**, {% data variables.copilot.copilot_auto_model_selection ## {% data variables.copilot.copilot_auto_model_selection_short_cap_a %} in {% data variables.product.prodname_copilot_short %} -{% data variables.copilot.copilot_auto_model_selection_short_cap_a %}, optimized for model reliability and availability, is available in these {% data variables.product.prodname_copilot_short %} products: +{% data variables.copilot.copilot_auto_model_selection_short_cap_a %}, with task optimization, is generally available in these {% data variables.product.prodname_copilot_short %} products: * {% data variables.copilot.copilot_chat_short %}, on the {% data variables.product.github %} website and supported IDEs * {% data variables.copilot.copilot_cli_short %} * {% data variables.copilot.copilot_cloud_agent %} +* {% data variables.copilot.github_copilot_app %} > [!TIP] > You can see which model was used for each {% data variables.product.prodname_copilot_short %} response. > * In **{% data variables.copilot.copilot_chat_short %}**, hover over the response. > * In **{% data variables.copilot.copilot_cli_short %}**, the model used for each response displays in the terminal. > * In **{% data variables.copilot.copilot_cloud_agent %}**, the model used for each response displays at the end of the response. +> * In **{% data variables.copilot.github_copilot_app %}**, the model used for each response is shown by the model picker next to **Auto**. -### {% data variables.copilot.copilot_chat_short %} on {% data variables.product.github %} and in IDEs +### {% data variables.copilot.copilot_chat_short %} in IDEs {% data variables.copilot.copilot_auto_model_selection_short_cap_a %}, with task optimization, is generally available in the following IDEs: * {% data variables.product.prodname_vscode_shortname %} - * {% data variables.product.github %} website {% data variables.copilot.copilot_auto_model_selection_short_cap_a %}, optimized for model reliability and availability, is generally available in the following IDEs: * JetBrains IDEs diff --git a/content/copilot/get-started/enterprise-ai-governance.md b/content/copilot/get-started/enterprise-ai-governance.md index 02eb3cb5f590..807bc089c776 100644 --- a/content/copilot/get-started/enterprise-ai-governance.md +++ b/content/copilot/get-started/enterprise-ai-governance.md @@ -26,6 +26,7 @@ journeyTracks: - href: '/copilot/tutorials/roll-out-at-scale/govern-at-scale/maintain-codebase-standards' - href: '/copilot/how-tos/administer-copilot/manage-for-enterprise/review-audit-logs' - href: '/copilot/concepts/preparing-for-new-features-and-models' + - href: '/copilot/tutorials/roll-out-at-scale/govern-at-scale/pilot-a-feature-or-model' - id: 'adopting_agents' title: 'Adopting agents' description: 'Roll out agentic features within secure guardrails.' diff --git a/content/copilot/how-tos/administer-copilot/manage-for-organization/add-copilot-cloud-agent.md b/content/copilot/how-tos/administer-copilot/manage-for-organization/add-copilot-cloud-agent.md index d21320eca614..493d48c9053d 100644 --- a/content/copilot/how-tos/administer-copilot/manage-for-organization/add-copilot-cloud-agent.md +++ b/content/copilot/how-tos/administer-copilot/manage-for-organization/add-copilot-cloud-agent.md @@ -68,4 +68,4 @@ Organization owners can configure the {% data variables.copilot.copilot_cloud_ag * [AUTOTITLE](/copilot/tutorials/cloud-agent/get-the-best-results) * [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-environment) - * [Security best practices](/copilot/rolling-out-github-copilot-at-scale/enabling-developers/using-copilot-cloud-agent-in-org#security-best-practices) + * [AUTOTITLE](/copilot/concepts/agents/cloud-agent/risks-and-mitigations) diff --git a/content/copilot/how-tos/copilot-cli/customize-copilot/change-settings.md b/content/copilot/how-tos/copilot-cli/customize-copilot/change-settings.md new file mode 100644 index 000000000000..0e521e3f2865 --- /dev/null +++ b/content/copilot/how-tos/copilot-cli/customize-copilot/change-settings.md @@ -0,0 +1,164 @@ +--- +title: Changing settings with the `/settings` command +shortTitle: Change settings +allowTitleToDifferFromFilename: true +intro: 'View and change your personal settings for {% data variables.copilot.copilot_cli %} with the `/settings` slash command.' +versions: + feature: copilot +contentType: how-tos +category: + - Configure Copilot # Copilot discovery page + - Configure Copilot CLI # Copilot CLI bespoke page +docsTeamMetrics: + - copilot-cli +--- + +You can use the `/settings` slash command to view and change your settings from within an interactive CLI session. This command controls user-level settings such as whether {% data variables.copilot.copilot_cli_short %} updates itself automatically, which color palette the interface uses, what's displayed in the footer, and many other options. + +Use `/settings` to: + +* **Browse and edit settings interactively** — Run `/settings` on its own to open a searchable editor that lists every setting with a description and its current value. +* **Change a setting with a single command** — Run `/settings KEY VALUE` to change a particular setting without opening the editor. +* **Check a setting's value** — Run `/settings show KEY` to display the current value of a setting. + +You can also use `/config`, which is an alias of `/settings`. + +Changes you make with `/settings` or `/config` are written to your personal settings file (by default, `~/.copilot/settings.json`) and persist across sessions. + +## Opening the settings editor + +To browse and edit settings interactively, enter the slash command with no arguments: + +```copilot copy +/settings +``` + +The editor lists every setting you can change, and the setting's current value. As you move up or down through the list, a short description of the highlighted setting is shown at the bottom of the editor. This allows you to browse and edit settings without needing to know their exact names. + +For more information about the available settings, see [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-config-dir-reference#user-settings-copilotsettingsjson). + +While the editor is open, you can use the following keyboard shortcuts: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyAction
/ Move between settings.
/Search and filter the list by a setting's name or description.
EnterToggle a boolean setting, or edit other types of values.
Ctrl+RReset the highlighted setting to its default.
Ctrl+EOpen settings.json in your configured terminal editor to make advanced changes.
EscClear the current search, or close the editor.
+ +Most changes take effect immediately—for example, switching the color palette updates the interface as soon as you select a new value. + +## Changing a single setting inline + +If you already know which setting you want to change, you can set it directly from the CLI's prompt box, without opening the editor: + +```copilot +/settings KEY VALUE +``` + +For example: + +```copilot copy +/settings autoUpdate off +``` + +{% data variables.product.prodname_copilot_short %} writes the value and confirms it, for example: `Set autoUpdate = false.` + +> [!NOTE] +> * **Boolean settings** accept `on`/`off` or `true`/`false`. For example, `/settings renderMarkdown on`. +> * **Settings with a fixed set of choices** accept one of those values. For example, `/settings theme dim` or `/settings banner never`. +> * **Nested settings** use a dotted path. For example, `/settings footer.showBranch off` hides the Git branch in the footer. + +You can configure settings that take set values without needing to remember the exact key or value, just by using the navigation and Enter keys. For example, to prevent the agent from asking clarifying questions: + +1. Type `/settings`, without pressing Enter, to display a list of options. +1. Use the key to move down the list until `❯` points to `/settings askUser Allow the agent to ask clarifying questions`, then press Enter. +1. Use the key to move down the list of options for this setting until `❯` points to `/settings askUser off`, then press Enter. +1. Press Enter again to submit the `/settings askUser off` command. + + The CLI confirms the change: `Set askUser = false.` + +## Viewing valid values for a setting + +To display the valid values that are available for a setting, enter `/settings KEY` with no value. For example, to see the valid values for `theme`: + +```copilot copy +/settings theme +``` + +## Viewing the current value of a setting + +To display a setting's current value, use `show`. For example: + +```copilot copy +/settings show theme +``` + +This outputs a line such as `theme = high-contrast`. The `show` form is useful for quickly checking a value, or for capturing settings in logs. + +## Changing settings that require a restart + +Most settings apply right away, but a few only take full effect after {% data variables.copilot.copilot_cli_short %} restarts—for example, `experimental` and the proxy settings. When you change one of these, {% data variables.product.prodname_copilot_short %} tells you a restart is needed, and may restart the session for you. + +## Settings you can't change from the command line + +Not every setting is editable through the inline `/settings KEY VALUE` method: + +* **Security-sensitive settings**—such as those that change how credentials are stored or that run a shell command—are deliberately excluded, because flipping them in a single line with no confirmation is risky. +* **List and structured settings** can't be set inline. Enter `/settings` then press Ctrl+E to open `settings.json` in your configured terminal editor, then edit the setting there. +* **Settings managed by your repository or organization** are shown for reference, but their values come from those sources and override your personal value. Changing them in your user settings has no effect. + +In each of these cases, {% data variables.product.prodname_copilot_short %} explains why the change wasn't applied and points you to the right place to make it. + +## Commands replaced by `/settings` + +Some older slash commands have been folded into `/settings`. They still work, but display a notice telling you about the equivalent `/settings` command. + +## Settings you might want to change + +Some commonly used settings include: + +* `autoUpdate` — Automatically download updated CLI versions. +* `theme` — Color palette (`default`, `github`, `dim`, `high-contrast`, or `colorblind`). +* `renderMarkdown` — Render Markdown in the terminal. +* `banner` — How often to show the startup banner (`always`, `once`, or `never`). +* `beep` — Beep when {% data variables.product.prodname_copilot_short %} needs your attention. +* `includeCoAuthoredBy` — Add a `Co-authored-by` trailer to commits. +* `footer.showBranch` — Show the current Git branch in the footer. + +For the full list of available settings and how user, repository, and organization settings combine, see [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-config-dir-reference). + +## Further reading + +* [AUTOTITLE](/copilot/how-tos/copilot-cli/set-up-copilot-cli/configure-copilot-cli) +* [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/overview) diff --git a/content/copilot/how-tos/copilot-cli/customize-copilot/index.md b/content/copilot/how-tos/copilot-cli/customize-copilot/index.md index f989232db78e..98b3e6522053 100644 --- a/content/copilot/how-tos/copilot-cli/customize-copilot/index.md +++ b/content/copilot/how-tos/copilot-cli/customize-copilot/index.md @@ -8,6 +8,7 @@ contentType: how-tos children: - /overview - /add-custom-instructions + - /change-settings - /use-hooks - /add-skills - /add-mcp-servers diff --git a/content/copilot/how-tos/copilot-cli/customize-copilot/overview.md b/content/copilot/how-tos/copilot-cli/customize-copilot/overview.md index f431c9daf976..7c8b8f00485d 100644 --- a/content/copilot/how-tos/copilot-cli/customize-copilot/overview.md +++ b/content/copilot/how-tos/copilot-cli/customize-copilot/overview.md @@ -66,3 +66,9 @@ For more information, see [AUTOTITLE](/copilot/concepts/context/mcp). They bundle multiple customization components together into a single installable unit. You can install plugins directly from a repository, from a plugin marketplace, or from a local path. For more information, see [AUTOTITLE](/copilot/concepts/agents/about-plugins). + +## Personal CLI settings + +The `/settings` slash command allows you to view and change your {% data variables.copilot.copilot_cli %} settings from within an interactive session. It controls user-level settings such as whether {% data variables.product.prodname_copilot_short %} updates itself automatically, which color palette the interface uses, what appears in the footer, and many other options. + +For more information, see [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/change-settings). diff --git a/content/copilot/how-tos/copilot-cli/index.md b/content/copilot/how-tos/copilot-cli/index.md index e33c5920f8e3..3b686d781612 100644 --- a/content/copilot/how-tos/copilot-cli/index.md +++ b/content/copilot/how-tos/copilot-cli/index.md @@ -41,6 +41,7 @@ children: - /content/copilot/concepts/agents/copilot-cli/lsp-servers - /content/copilot/concepts/agents/copilot-cli/research - /content/copilot/concepts/agents/copilot-cli/rubber-duck + - /content/copilot/concepts/agents/copilot-cli/about-cli-extensions - /content/copilot/reference/copilot-cli-reference/acp-server - /content/copilot/reference/copilot-cli-reference/cli-command-reference - /content/copilot/reference/copilot-cli-reference/cli-plugin-reference @@ -52,6 +53,7 @@ children: - /customize-copilot/add-mcp-servers - /customize-copilot/add-skills - /customize-copilot/create-custom-agents-for-cli + - /customize-copilot/change-settings - /customize-copilot/overview - /customize-copilot/plugins-creating - /customize-copilot/plugins-finding-installing diff --git a/content/copilot/how-tos/copilot-cli/use-copilot-cli/overview.md b/content/copilot/how-tos/copilot-cli/use-copilot-cli/overview.md index f8ceb71ca138..c22825f41fdf 100644 --- a/content/copilot/how-tos/copilot-cli/use-copilot-cli/overview.md +++ b/content/copilot/how-tos/copilot-cli/use-copilot-cli/overview.md @@ -305,15 +305,15 @@ Press Ctrl+T to show or hide the model's reasoning process ### Configure settings -You can view and change {% data variables.copilot.copilot_cli_short %} settings using the `/settings` slash command. +You can view and change your personal {% data variables.copilot.copilot_cli_short %} settings using the `/settings` slash command. -* Run `/settings` to open an interactive settings dialog, where you can search for a setting by name and edit it. -* Run `/settings KEY VALUE` to set a setting inline, where KEY is the name of the setting and VALUE is the value you want to set. This also works in scripts and in programmatic sessions started with `-p`. -* Run `/settings reset KEY` to restore a setting to its default value. +* Run `/settings` to open an interactive settings editor, where you can search for a setting by name and edit it. To restore a setting to its default value, highlight it in the list of settings and press Ctrl+R. +* Run `/settings KEY VALUE` to change a setting directly in the CLI's prompt box. KEY is the name of the setting and VALUE is the value you want to set. This also works in scripts and in programmatic sessions started with `-p`. +* Run `/settings show KEY` to display a setting's current value as text, without opening the editor. -Some settings, such as the color mode, take effect immediately when you save them. +Most settings changes take effect immediately, without needing to restart {% data variables.copilot.copilot_cli_short %}. -For the full list of available settings, see [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-config-dir-reference#configuration-file-settings). +For more information, see [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/change-settings). For the full list of available settings, see [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-config-dir-reference#configuration-file-settings). ## Find out more @@ -322,14 +322,12 @@ For a complete list of the command line options and slash commands that you can * Enter `?` in the prompt box in an interactive session. * Enter `copilot help` in your terminal. -For additional information use one of the following commands in your terminal: +For additional information, use one of the following `copilot help` subcommands in your terminal: * **Configuration settings**: `copilot help config` - You can adjust the configuration settings by editing the `settings.json` file, which is located, by default, in the `~/.copilot` directory. This location can be changed by setting the `COPILOT_HOME` environment variable. - * **Environment variables** that affect {% data variables.copilot.copilot_cli_short %}: `copilot help environment` diff --git a/content/copilot/how-tos/copilot-on-github/set-up-copilot/enable-copilot/set-up-a-dedicated-enterprise-for-copilot-business.md b/content/copilot/how-tos/copilot-on-github/set-up-copilot/enable-copilot/set-up-a-dedicated-enterprise-for-copilot-business.md index 27ab6f36cbe2..a4cc94488870 100644 --- a/content/copilot/how-tos/copilot-on-github/set-up-copilot/enable-copilot/set-up-a-dedicated-enterprise-for-copilot-business.md +++ b/content/copilot/how-tos/copilot-on-github/set-up-copilot/enable-copilot/set-up-a-dedicated-enterprise-for-copilot-business.md @@ -22,19 +22,15 @@ With a dedicated enterprise account, you get enterprise-grade identity provider ## Create an enterprise account -> [!IMPORTANT] -> If you purchased {% data variables.copilot.copilot_business_short %} through {% data variables.product.company_short %}'s sales team, your enterprise account is already created. Skip to the next section. +To create an enterprise account, contact {% data variables.product.company_short %}'s [sales team](https://github.com/enterprise/contact?ref_product=copilot&ref_type=purchase). They will provision you with a standard enterprise account with {% data variables.product.prodname_copilot_short %} enabled. -Start a trial of {% data variables.product.prodname_ghe_cloud %} to create your enterprise account. - -Set up a trial of {% data variables.product.prodname_ghe_cloud %} {% octicon "link-external" height:16 aria-label="link-external" %} - -Do not create any organizations during setup. Adding users to organizations assigns {% data variables.product.prodname_enterprise %} licenses, while adding users directly to the enterprise keeps your setup limited to {% data variables.copilot.copilot_business_short %}. ## Add users to your enterprise +Once you have an enterprise account, add the people who will receive {% data variables.copilot.copilot_business_short %} licenses. How you add users depends on your enterprise type. Once you have an enterprise account, add the people who will receive {% data variables.copilot.copilot_business_short %} licenses. How you add users depends on your enterprise type. +Do not create any organizations during setup. Adding users to organizations assigns {% data variables.product.prodname_enterprise %} licenses, while adding users directly to the enterprise keeps your setup limited to {% data variables.copilot.copilot_business_short %}. ### Enterprise with personal accounts Invite users directly to your enterprise. For detailed steps, see [AUTOTITLE](/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/invite-users-directly). @@ -58,7 +54,6 @@ To begin using {% data variables.copilot.copilot_business_short %} after your tr 1. Ensure you are signed in as an enterprise administrator on {% data variables.product.github %}. 1. To purchase {% data variables.product.prodname_copilot %} for your enterprise, [contact {% data variables.product.github %}'s Sales team](https://github.com/enterprise/contact?ref_product=copilot&ref_type=engagement&ref_style=text). -1. A member of the Sales team will work with you to set up {% data variables.product.prodname_copilot_short %} for your enterprise. ## Assign {% data variables.product.prodname_copilot_short %} licenses diff --git a/content/copilot/how-tos/github-copilot-app/agent-sessions.md b/content/copilot/how-tos/github-copilot-app/agent-sessions.md index 2af0912e31e4..46d0a9a4c888 100644 --- a/content/copilot/how-tos/github-copilot-app/agent-sessions.md +++ b/content/copilot/how-tos/github-copilot-app/agent-sessions.md @@ -19,8 +19,8 @@ Each session in the {% data variables.copilot.github_copilot_app %} runs in its 1. In the sidebar next to **Sessions**, click **+** to start a new session. 1. Choose a repository—you can use a local folder, choose from {% data variables.product.github %}, or clone from a URL. -1. From the dropdown under the prompt box, choose where the session should run: in a new working tree, in your local repository, or in a cloud sandbox. Cloud sandboxes for {% data variables.product.prodname_copilot_short %} (public preview) are fully isolated environments hosted by {% data variables.product.github %}. For more information, see [AUTOTITLE](/copilot/concepts/about-cloud-and-local-sandboxes). -1. Select a session mode, model, and reasoning effort from the dropdowns below the prompt field. +1. From the dropdown under the prompt box, choose where the session should run: in a new working tree, in your local repository, or in a cloud sandbox. Cloud sandboxes for {% data variables.product.prodname_copilot_short %} (public preview) are fully isolated environments hosted by {% data variables.product.github %}. +1. Select a session mode, model, and reasoning effort from the dropdowns below the prompt field. If you choose **Auto** for the model, the app selects the optimal model for your task based on task complexity. 1. Describe the task in the prompt field. You can reference issues with `#`, add files with `@`, or use `/` for commands. The agent starts working. Your active sessions appear in the sidebar grouped by repository—click any session to switch to it. @@ -35,7 +35,9 @@ The session mode controls how much autonomy the agent has. You can set the mode ## Choosing a model -You can select a model and reasoning effort from the dropdowns above the prompt field. Higher reasoning effort gives the agent more time to think through complex problems but may take longer. You can change both settings at any time during a session. +You can select a model and reasoning effort from the dropdowns below the prompt field. Higher reasoning effort gives the agent more time to think through complex problems but may take longer. You can change both settings at any time during a session. + +If you choose **Auto** in the model picker, the app automatically selects the optimal model for your task based on task complexity. After the session starts, the picker next to **Auto** shows which model handled each response. For more information, see [AUTOTITLE](/copilot/concepts/auto-model-selection). If you configured your own model provider in the app, those models also appear in the picker. For setup steps, see [AUTOTITLE](/copilot/how-tos/github-copilot-app/use-byok-models). diff --git a/content/copilot/how-tos/use-copilot-agents/cloud-agent/integrate-cloud-agent-with-slack.md b/content/copilot/how-tos/use-copilot-agents/cloud-agent/integrate-cloud-agent-with-slack.md index 3c4defe3d2f2..2990158efe9a 100644 --- a/content/copilot/how-tos/use-copilot-agents/cloud-agent/integrate-cloud-agent-with-slack.md +++ b/content/copilot/how-tos/use-copilot-agents/cloud-agent/integrate-cloud-agent-with-slack.md @@ -54,45 +54,53 @@ You must have write access to the default repository – or the repository speci Users can invoke {% data variables.copilot.copilot_cloud_agent %} on any repository where they have `write` access. For enterprise-owned repositories, administrators must install and configure the [Slack {% data variables.product.prodname_github_app %}](https://github.com/marketplace/slack-github?ref_product=copilot&ref_type=engagement&ref_style=text&ref_plan=enterprise) and specify which repositories the Slack app can access. For more information about configuring {% data variables.product.prodname_github_apps %}, see [AUTOTITLE](/apps/using-github-apps/installing-a-github-app-from-github-marketplace-for-your-organizations). -1. In Slack, open a direct message with the {% data variables.product.prodname_github_app %} or mention the app in a thread by typing `@{% data variables.product.prodname_copilot %}`. -1. Type your prompt, then send it. Optionally, you can specify a repository or branch using the following syntax: +1. In Slack, open a direct message with the {% data variables.product.prodname_github_app %} or mention the app in a thread by typing `@{% data variables.product.github %}`. +1. Type your prompt, then send it. You can describe the repository and branch in natural language as part of your request. For example: - `@{% data variables.product.github %} Add "Hello World" to the README in repo=REPO_OWNER/REPO_NAME branch=BRANCH_NAME` + `@{% data variables.product.github %} Add "Hello World" to the README in octo-org/octo-repo on the develop branch` - > [!NOTE] The repo parameter tells {% data variables.copilot.copilot_cloud_agent %} which repository to use for the request, and the branch parameter specifies an existing branch of the repository that should be used as the base branch for a pull request. By default, {% data variables.product.prodname_copilot_short %} uses your configured default repository and the repository’s default branch. + > [!NOTE] Tell {% data variables.copilot.copilot_cloud_agent %} which repository to use for the request, and, if needed, an existing branch of the repository to use as the base branch for a pull request. If you don't specify a repository, {% data variables.product.prodname_copilot_short %} uses the channel's default repository if one is set, and otherwise asks you which one to use. If you don't specify a branch, {% data variables.product.prodname_copilot_short %} creates a new branch from the repository's default branch. 1. {% data variables.copilot.copilot_cloud_agent %} will initiate a {% data variables.copilot.copilot_cloud_agent_short %} session and, once the {% data variables.copilot.copilot_cloud_agent_short %} has finished, respond with a summary of the changes it plans to make and a link to the pull request it has created in the default repository. -### Creating an issue with {% data variables.product.prodname_copilot_short %} +### Creating issues with {% data variables.product.prodname_copilot_short %} -You can ask {% data variables.product.prodname_copilot_short %} to draft {% data variables.product.github %} issues directly from Slack, turning conversations into actionable tasks. When you mention {% data variables.product.prodname_copilot_short %}, it uses the full thread history as context for the issue it drafts. To keep the context focused, consider starting a new thread or sending a direct message. +You can ask {% data variables.product.prodname_copilot_short %} to create {% data variables.product.github %} issues directly from Slack, turning conversations into actionable tasks. Just describe what you need in natural language, and {% data variables.product.prodname_copilot_short %} creates the issue for you. You can create a single issue or multiple issues at once with child-parent relationships. When you mention {% data variables.product.prodname_copilot_short %}, it uses the full thread history as context for the issues it creates. To keep the context focused, consider starting a new thread or sending a direct message. -1. In Slack, ask {% data variables.product.prodname_copilot_short %} to create an issue, specifying the target repository. +1. In Slack, ask {% data variables.product.prodname_copilot_short %} to create one or more issues, specifying the target repository. - For example: + To create a single issue: ```text @{% data variables.product.github %} In octo-org/octo-repo, create a feature request to add fuzzy matching to search. ``` + To create multiple issues at once: + ```text - @{% data variables.product.github %} Log a bug for a 500 error in octo-org/octo-repo and assign @USER to it. This happens consistently when I try to log into the site. + @{% data variables.product.github %} In octo-org/octo-repo, open separate issues for adding fuzzy matching to search, paginating the results list, and caching search queries. ``` + To create issues with child-parent relationships: + ```text - @{% data variables.product.github %} In octo-org/octo-repo, create an issue and add relevant code snippets to improve the API response format. + @{% data variables.product.github %} In octo-org/octo-repo, create an epic to redesign search, with child issues for fuzzy matching, pagination, and query caching. ``` > [!NOTE] You can only use {% data variables.product.prodname_copilot_short %} to create issues in repositories where you already have permission to create issues. This feature doesn't change your access or bypass repository permissions. -1. Click **Review draft** to open the issue draft. {% data variables.product.prodname_copilot_short %} drafts an issue that includes: +1. {% data variables.product.prodname_copilot_short %} creates the issues and replies with a link to each one. Each issue includes a title and description, and based on your prompt {% data variables.product.prodname_copilot_short %} can also add metadata such as labels, assignees, and issue type. + +### Setting a default repository for a channel + +To avoid specifying a repository in every request, you can set a default repository for a Slack channel. - * A suggested title. - * Details of the required changes. +1. In the channel, type `@{% data variables.product.github %} settings` and send the message. +1. Select the repository you want to use as the default, then save your changes. - Based on your prompt, {% data variables.product.prodname_copilot_short %} can also suggest metadata such as labels, assignees, and issue type. +When no repository is specified in a request, {% data variables.product.prodname_copilot_short %} uses the channel's default repository. -1. Review the draft, and once the issue looks good, click **Create**. +> [!NOTE] The default repository is shared across the channel, so any change applies to everyone using {% data variables.product.prodname_copilot_short %} in that channel. ## Further reading diff --git a/content/copilot/reference/ai-models/supported-models.md b/content/copilot/reference/ai-models/supported-models.md index 18a9a27b6616..7d776333fce1 100644 --- a/content/copilot/reference/ai-models/supported-models.md +++ b/content/copilot/reference/ai-models/supported-models.md @@ -52,10 +52,10 @@ This table lists the supported AI models for {% data variables.copilot.copilot_a {% rowheaders %} -| Model | {% data variables.copilot.copilot_cloud_agent %} | {% data variables.copilot.copilot_chat_short %} | {% data variables.copilot.copilot_cli_short %} | -| --- | --- | --- | --- | +| Model | {% data variables.copilot.copilot_cloud_agent %} | {% data variables.copilot.copilot_chat_short %} | {% data variables.copilot.copilot_cli_short %} | {% data variables.copilot.github_copilot_app %} | +| --- | --- | --- | --- | --- | | {% for model in tables.copilot.auto-model-selection %} | -| {{ model.name }} | {% if model.cloud_agent == true %}{% octicon "check" aria-label="Included" %}{% else %}{% octicon "x" aria-label="Not included" %}{% endif %} | {% if model.chat == true %}{% octicon "check" aria-label="Included" %}{% else %}{% octicon "x" aria-label="Not included" %}{% endif %} | {% if model.cli == true %}{% octicon "check" aria-label="Included" %}{% else %}{% octicon "x" aria-label="Not included" %}{% endif %} | +| {{ model.name }} | {% if model.cloud_agent == true %}{% octicon "check" aria-label="Included" %}{% else %}{% octicon "x" aria-label="Not included" %}{% endif %} | {% if model.chat == true %}{% octicon "check" aria-label="Included" %}{% else %}{% octicon "x" aria-label="Not included" %}{% endif %} | {% if model.cli == true %}{% octicon "check" aria-label="Included" %}{% else %}{% octicon "x" aria-label="Not included" %}{% endif %} | {% if model.app == true %}{% octicon "check" aria-label="Included" %}{% else %}{% octicon "x" aria-label="Not included" %}{% endif %} | | {% endfor %} | {% endrowheaders %} diff --git a/content/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans.md b/content/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans.md index e3860f59c925..10882bd0168e 100644 --- a/content/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans.md +++ b/content/copilot/reference/copilot-billing/request-based-billing-legacy/model-multipliers-for-annual-plans.md @@ -37,7 +37,7 @@ The following table shows the model multipliers per supported model. > * {% data variables.copilot.copilot_gpt_54_mini %} > * The multiplier for {% data variables.copilot.copilot_mai_code_1_flash %} is a promotional rate. > -> If you use {% data variables.copilot.copilot_auto_model_selection_short %} in {% data variables.copilot.copilot_chat_short %}, {% data variables.copilot.copilot_cli_short %}, or {% data variables.copilot.copilot_cloud_agent %}, you qualify for a 10% discount. For example, if a model has a multiplier of 1x you'll be billed at 0.9x instead. +> If you use {% data variables.copilot.copilot_auto_model_selection_short %} in {% data variables.copilot.copilot_chat_short %}, {% data variables.copilot.copilot_cli_short %}, {% data variables.copilot.github_copilot_app %}, or {% data variables.copilot.copilot_cloud_agent %}, you qualify for a 10% discount. For example, if a model has a multiplier of 1x you'll be billed at 0.9x instead. | Model | Multiplier | | --- | ---: | diff --git a/content/copilot/reference/copilot-cli-reference/cli-command-reference.md b/content/copilot/reference/copilot-cli-reference/cli-command-reference.md index efbafcfe7c66..c5437c5418c0 100644 --- a/content/copilot/reference/copilot-cli-reference/cli-command-reference.md +++ b/content/copilot/reference/copilot-cli-reference/cli-command-reference.md @@ -242,18 +242,18 @@ When diff mode is open (entered via `/diff`): | `/resume [SESSION-ID]`, `/continue [SESSION-ID]` | Switch to a different session by choosing from a list (optionally specify a session ID). | | `/review [PROMPT]` | Run the code review agent to analyze changes. See [AUTOTITLE](/copilot/how-tos/copilot-cli/use-copilot-cli/agentic-code-review). | | `/rubber-duck [PROMPT]` | Consult the rubber duck agent for a second opinion on plans, code, and tests. See [AUTOTITLE](/copilot/concepts/agents/copilot-cli/rubber-duck). | -| `/sandbox [enable\|disable]` | Configure shell command sandboxing. | +| `/sandbox [enable\|disable]` | Enable, disable, or configure OS-level sandboxing that restricts filesystem and network access for shell commands, MCP/LSP servers, and built-in file/web tools. Run `/sandbox` with no arguments to open the policy dialog. | | `/search [QUERY]`, `/find [QUERY]` | Search the conversation timeline. {% data reusables.copilot.experimental %} | | `/security-review [PROMPT]` | Run the security review agent to analyze changes for vulnerabilities. | | `/session [info\|checkpoints [n]\|files\|plan\|rename [NAME]\|cleanup\|prune\|delete [ID]\|delete-all]`, `/sessions [info\|checkpoints [n]\|files\|plan\|rename [NAME]\|cleanup\|prune\|delete [ID]\|delete-all]` | Show session information and manage sessions. The `info` subcommand shows session details including the session link (when available). Subcommands: `info`, `checkpoints`, `files`, `plan`, `rename`, `cleanup`, `prune`, `delete`, `delete-all`. | -| `/settings [show\|[KEY VALUE]\|reset KEY]` | Open the settings dialog, set a setting inline with a KEY and VALUE, or reset a setting to its default. See [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-config-dir-reference#configuration-file-settings). | +| `/settings [show KEY\|KEY\|KEY VALUE]`,
`/config [show KEY\|KEY\|KEY VALUE]` | Open the settings editor, open it focused on a specific setting (`KEY`), set a setting inline (`KEY VALUE`), or display a setting's current value (`show KEY`). See [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/change-settings). | | `/share [file\|html\|gist] [session\|research] [PATH]`, `/export [file\|html\|gist] [session\|research] [PATH]` | Share the session to a Markdown file, interactive HTML file, or {% data variables.product.github %} gist. | | `/skills [list\|info\|add\|remove\|reload] [ARGS...]` | Manage skills for enhanced capabilities. See [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/create-skills). | | `/statusline`, `/footer` | Configure which items appear in the status line. | | `/subagents`, `/agents` | Configure default and per-agent subagent models. See [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-config-dir-reference#configuration-file-settings). | | `/tasks` | View and manage tasks (subagents and shell commands). | | `/terminal-setup` | Configure the terminal for multiline input support (Shift+Enter and Ctrl+Enter). | -| `/theme [default\|dim\|high-contrast\|colorblind]` | View or set the color mode. | +| `/theme [default\|github\|dim\|high-contrast\|colorblind]` | View or set the color mode. | | `/tuikit [colors\|icons\|select\|tabbar]` | Preview TUIkit design-system components and color tokens. | | `/undo`, `/rewind` | Rewind the last turn and revert file changes. File tracking is done via the tool layer and does not require Git. | | `/update`, `/upgrade` | Update the CLI to the latest version. | diff --git a/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md b/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md index d8ecb86927ea..1cde94f48e13 100644 --- a/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md +++ b/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md @@ -53,7 +53,9 @@ The following files are designed to be edited by you directly, or managed throug ### `settings.json` -This is the primary configuration file for {% data variables.copilot.copilot_cli_short %}. Within a session, you can use the interactive `/settings` command to change specific values, or run `/settings KEY VALUE` to set a single value, or edit the file directly in a text editor. The file supports JSON with comments (JSONC). +This is the primary configuration file for {% data variables.copilot.copilot_cli_short %}. Within a session, you can use the interactive `/settings` command to change specific values, or run `/settings KEY VALUE` to set a single value. See [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/change-settings). Alternatively, you can edit the `settings.json` file directly in a text editor. The file supports JSON with comments (JSONC). + +By default, this file is located in the `~/.copilot` directory, which is the user-level configuration directory. It contains global user-level defaults for all repositories. You can change the location of this directory by setting the `COPILOT_HOME` environment variable to a different path. > [!NOTE] > User-editable settings were originally stored in `config.json`. They have been moved to `settings.json`. Any user settings present in `config.json` on startup are automatically migrated to `settings.json`. @@ -438,7 +440,7 @@ These settings apply across all your sessions and repositories. You can use the | `beepOnSchedule` | `boolean` | `true` | Play an audible beep when a scheduled `/every` or `/after` run finishes. | | `builtInAgents.rubberDuck` | `boolean` | `true` | Enable the rubber-duck subagent that provides adversarial feedback on agent plans. | | `builtInAgents.rubberDuckAutoInvoke` | `boolean` | `false` | Include proactive prompting for automatic rubber-duck invocation. Set to `true` to opt into additional rubber-duck nudges during agent turns. | -| `colorMode` | `"default"` \| `"github"` \| `"dim"` \| `"high-contrast"` \| `"colorblind"` | `"default"` | Color contrast mode. Managed by the `/settings` and `/theme` slash commands. | +| `colorMode` | `"default"` \| `"github"` \| `"dim"` \| `"high-contrast"` \| `"colorblind"` | `"github"` | Color palette mode. Managed by the `/settings` and `/theme` slash commands. | | `compactPaste` | `boolean` | `true` | Collapse large pastes (more than 10 lines) into compact tokens. | | `companyAnnouncements` | `string[]` | `[]` | Custom messages shown randomly on startup. One message is randomly selected each time the CLI starts. Useful for team announcements or reminders. | | `continueOnAutoMode` | `boolean` | `false` | Automatically switch to auto mode when rate-limited. When `true`, eligible rate limit errors trigger an automatic switch to auto mode and retry. Does not apply to global rate limits or BYOK providers. | diff --git a/content/copilot/tutorials/cloud-agent/pilot-cloud-agent.md b/content/copilot/tutorials/cloud-agent/pilot-cloud-agent.md index 99dc3f693c4f..195027021d06 100644 --- a/content/copilot/tutorials/cloud-agent/pilot-cloud-agent.md +++ b/content/copilot/tutorials/cloud-agent/pilot-cloud-agent.md @@ -50,7 +50,7 @@ As with any other change to working practices, it's important to run a trial to 1. Choose an isolated or low-risk repository, for example, one that contains documentation or internal tools. You could create a fresh repository to use as a playground, but {% data variables.product.prodname_copilot_short %} needs context to be successful, so you would need to add a lot of context, including team processes, development environment, and common dependencies. 1. Enable {% data variables.copilot.copilot_cloud_agent %} in the repository and optionally enable third-party MCP servers for enhanced context sharing. See [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-organization/add-copilot-cloud-agent). 1. Create repository instructions and pre-install any tools required in the development environment {% data variables.product.prodname_copilot_short %} uses. See [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-environment). -1. Identify a few compelling use cases for your organization, for example: test coverage or improving accessibility. See [Choose the right type of tasks to give to Copilot](/copilot/tutorials/cloud-agent/best-practices#choosing-the-right-type-of-tasks-to-give-to-copilot) in the best practice guide. +1. Identify a few compelling use cases for your organization, for example: test coverage or improving accessibility. See [Choose the right type of tasks to give to Copilot](/copilot/tutorials/cloud-agent/get-the-best-results#choosing-the-right-type-of-tasks-to-give-to-copilot) in the best practice guide. 1. Use best practice to create or refine issues for {% data variables.product.prodname_copilot_short %} in your pilot repository. 1. Assign issues to {% data variables.product.prodname_copilot_short %} and prepare team members to review its work. 1. Spend time looking at the codebase or documentation in {% data variables.product.prodname_vscode_shortname %} or {% data variables.product.prodname_dotcom_the_website %}, asking {% data variables.product.prodname_copilot_short %} to create a pull request to fix any bugs or small improvements that you identify. diff --git a/content/copilot/tutorials/create-an-extension.md b/content/copilot/tutorials/create-an-extension.md new file mode 100644 index 000000000000..2d4073b2ff61 --- /dev/null +++ b/content/copilot/tutorials/create-an-extension.md @@ -0,0 +1,359 @@ +--- +title: Creating extensions for {% data variables.copilot.copilot_cli %} +shortTitle: Create CLI extensions +allowTitleToDifferFromFilename: true +intro: 'Build extensions that add your own tools and slash commands to {% data variables.copilot.copilot_cli_short %}.' +versions: + feature: copilot +contentType: tutorials +category: + - Author and optimize with Copilot # Copilot discovery page + - Build with Copilot CLI # Copilot CLI bespoke page +docsTeamMetrics: + - copilot-cli +--- + +{% data reusables.copilot.copilot-cli.cli-extensions-experimental %} + +An extension lets you add your own capabilities to {% data variables.copilot.copilot_cli_short %}. Each extension is a small Node.js module that runs as a separate process alongside your interactive session and connects back to it. Through that connection, an extension can add **tools** that {% data variables.product.prodname_copilot_short %} can call while it works on your behalf, and **slash commands** that you run yourself. + +In this tutorial, you'll build two simple extensions as examples of what you can do: + +* A tool, called `tool-time`, that {% data variables.product.prodname_copilot_short %} can call to report how long its tool calls have taken so far in a session. +* A slash command, called `/tokencount`, that reports how many tokens you've used since you started counting. + +Both examples rely only on the SDK that's bundled with the {% data variables.copilot.copilot_cli_short %}, so there's nothing extra to install. For background on how extensions work, see [AUTOTITLE](/copilot/concepts/agents/copilot-cli/about-cli-extensions). + +> [!WARNING] +> Extensions execute on your computer with your privileges. Only load extension code that you trust, in the same way you would only run any other script you didn't write yourself. + +## Prerequisites + +* **{% data variables.copilot.copilot_cli %}**: You need {% data variables.copilot.copilot_cli_short %} installed and set up. See [AUTOTITLE](/copilot/how-tos/copilot-cli/cli-getting-started). +* **Experimental features enabled**: Extensions are currently an experimental feature. The steps in this tutorial turn on experimental features each time you start the CLI, using the `--experimental` command line option. +* **JavaScript**: Extensions are written in JavaScript, so you'll need to be familiar with this language to create your own extensions. +* **A repository**: The second example adds a project-level extension, so you'll need a local copy of a Git repository in which to add the extension. + +## Example extension 1: A "tool time" tool + +This example adds a **user-level** extension called `tool-time`. It adds a new tool—called `session_tool_time`—that {% data variables.product.prodname_copilot_short %} can call to report how long tool calls have taken so far this session, and how many calls that covers. + +To ensure a tool is used by the CLI, the tool must do something the CLI can't do on its own. In this case, the `session_tool_time` tool keeps track of how long tool calls take by listening to events from the CLI about when tool calls start and finish, and recording the timings itself. Because the CLI doesn't record these timings anywhere that {% data variables.product.prodname_copilot_short %} can read, the only way for {% data variables.product.prodname_copilot_short %} to know them is to call the tool. The tool's description explains this to the model, which steers it toward using the tool when you ask about tool call timings. + +### Step 1: Create the extension file + +1. Create the following directory and file in your home directory: + + ```text + ~/.copilot/extensions/tool-time/extension.mjs + ``` + + Because this is under `~/.copilot/extensions/`, the extension is available in **all** your CLI sessions, in every directory—not just one repository. + +1. Add this code to `extension.mjs`: + + ```javascript copy + // Extension: tool-time + // Adds a session_tool_time tool that reports how long Copilot's tool calls + // have taken this session. Copilot is never told these timings and they + // aren't written anywhere, so calling the tool is the only way to find + // them out. + + import { joinSession } from "@github/copilot-sdk/extension"; + + // The tool's own name, so it can avoid timing its own calls. + const TOOL_NAME = "session_tool_time"; + + // Module-level state persists for the whole session, because the extension + // runs as a single long-lived process. + const startTimes = new Map(); // tool call id -> Date.now() when call started + let totalMs = 0; // total milliseconds spent across finished tool calls + let callCount = 0; // number of finished tool calls measured + + const session = await joinSession({ + tools: [ + { + name: TOOL_NAME, + description: + "Report how long your tool calls have taken in total so far " + + "in THIS session, and how many calls that covers. You are " + + "never told how long your tool calls take and the timings " + + "aren't recorded anywhere you can read, so call this tool " + + "whenever you are asked about it rather than estimating.", + // Always keep this tool's description in the model's tool list, + // even when tool search is active, so Copilot reliably sees it: + defer: "never", + // Force a permissions approval prompt once, for this extension, + // rather than on every call of this tool: + skipPermission: true, + parameters: { type: "object", properties: {} }, + handler: async () => { + const seconds = (totalMs / 1000).toFixed(1); + return `So far this session, Copilot's tool calls have taken ${seconds}s in total across ${callCount} call(s).`; + }, + }, + ], + }); + + // When a tool starts, record the time, keyed by the tool call id so the + // matching completion can be found later. The tool's own calls are skipped. + session.on("tool.execution_start", (event) => { + const data = event.data ?? {}; + if (data.toolName && data.toolName !== TOOL_NAME) { + startTimes.set(data.toolCallId, Date.now()); + } + }); + + // When a tool finishes, add the elapsed time to the running total. + session.on("tool.execution_complete", (event) => { + const data = event.data ?? {}; + const startedAt = startTimes.get(data.toolCallId); + if (startedAt === undefined) { + return; + } + startTimes.delete(data.toolCallId); + totalMs += Date.now() - startedAt; + callCount += 1; + }); + ``` + +> [!NOTE] +> * `@github/copilot-sdk/extension` is the extension SDK, which is bundled with the CLI. The CLI resolves this import automatically when it runs your extension, so you don't need to add it to a `package.json` or run a package manager. +> * The `startTimes`, `totalMs`, and `callCount` values live at module scope. Because the extension runs as a single long-lived process for the whole session, they accumulate for as long as the session is open. + +### Step 2: Load the extension + +1. Start an interactive session with experimental features enabled: + + ```shell copy + copilot --experimental + ``` + + Because the extension lives under `~/.copilot/extensions/`, you can start the CLI from any directory and the extension will be available. + + If you already had a session open, run `/clear` to start a fresh session, which reloads extensions from disk. + +1. Without granting elevated permissions to either the new extension, all extensions, or all tools, you'll be prompted to allow the new extension to skip tool permission prompts. Choose either **Yes** or **Yes, and always allow "user:tool-time" in this directory**. + + > [!NOTE] + > For the minimum elevated permissions to prevent seeing this message when you start the CLI, add this to your CLI startup command: + > + > ```shell copy + > --allow-tool='extension-permission-access(user:tool-time)' + > ``` + > + > For more information, see [AUTOTITLE](/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools). + +### Step 3: Confirm the extension is running + +Run the `/extensions manage` command to open the extension manager. Your `tool-time` extension should be listed under the **User** group with a status of **running**. Press Esc to close the manager. + +### Step 4: Try it out + +1. Unlike a slash command, you don't invoke a tool yourself—{% data variables.product.prodname_copilot_short %} calls it when it's useful. First, give {% data variables.product.prodname_copilot_short %} some work that involves a few tool calls, for example: + + ```copilot copy + Explore the files in the current directory and give me a short summary of what's here. + ``` + +1. Once {% data variables.product.prodname_copilot_short %} finishes responding, ask: + + ```copilot copy + How long have tool calls taken so far this session? + ``` + + The agent calls the `session_tool_time` tool, from the new extension, and uses it to answer your question. + + > [!TIP] + > You can confirm that the agent used the new tool by looking at the start of {% data variables.product.prodname_copilot_short %}'s response. The response should be prefixed by the name of the tool that was used; in this case, `session_tool_time`. + +### How the tool works + +The single call to `joinSession` is what turns a plain Node.js file into a {% data variables.copilot.copilot_cli_short %} extension. It connects the running process to your session and registers everything the extension adds to the CLI—in this case, a single tool. + +A tool is defined by these fields: + +* **`name`**—the identifier {% data variables.product.prodname_copilot_short %} uses to call the tool. +* **`description`**—what the tool does. The model relies on this text to decide when to call the tool, so it's worth being explicit. This description tells the model that it isn't told these timings itself, which steers it toward calling the tool instead of attempting to work out the timings some other way. +* **`parameters`**—a JSON Schema describing the tool's arguments. This tool takes none, so the schema is an object with no properties. +* **`handler`**—an async function that runs when {% data variables.product.prodname_copilot_short %} calls the tool. Whatever string it returns becomes the tool's result, which the model reads. + +Two further fields shape how the tool is offered to the model: + +* **`defer: "never"`**—keeps the tool's description in the model's tool list at all times. By default, when many tools are available, the CLI can defer rarely-used tools and let the model search for them on demand. Setting `defer` to `"never"` opts this tool out of that behavior, so {% data variables.product.prodname_copilot_short %} always sees it. + + > [!IMPORTANT] + > `defer: "never"` simply makes the tool _available_ to {% data variables.product.prodname_copilot_short %}. It doesn't _force_ {% data variables.product.prodname_copilot_short %} to call it. There is no way for an extension to mandate that a particular tool should always be used if there is an alternative means of generating an appropriate response to a prompt. The model always decides for itself which tool to use. In this example, the new tool is reliably used because there's no other way for the model to know the information the tool provides. + +* **`skipPermission: true`** allows the tool to run without asking you to approve each call. This is appropriate here because the tool only reads totals the extension has already collected; it doesn't touch your files or run commands. + +The timings are gathered by watching the session. The extension subscribes to two events the CLI emits around every tool call: + +```javascript +session.on("tool.execution_start", (event) => { /* ... */ }); +session.on("tool.execution_complete", (event) => { /* ... */ }); +``` + +A `tool.execution_start` event carries the tool's name (`event.data.toolName`). A `tool.execution_complete` event carries a `success` flag. Neither event carries the other's information, so the extension correlates them using the `toolCallId` that appears on each: when a tool starts, it records the current time under that ID. When the matching completion arrives, it adds the elapsed milliseconds to the running total. The tool skips its own calls so the figure reflects {% data variables.product.prodname_copilot_short %}'s real work. + +Because the totals live in the long-lived extension process, they cover the whole session. This is the kind of job extensions are good at: observing what's happening in the session and keeping state across calls. + +> [!NOTE] +> * The totals are held in memory, so they reset whenever the extension is reloaded or the session restarts (for example, after `/clear`). +> * The figure is wall-clock time measured from the extension's point of view—the gap between each tool call's start and completion events—so it includes any time a call spent waiting for you to approve it. + +## Example extension 2: A token usage slash command + +This example adds a project extension called `token-counter`. The extension adds a `/tokencount` slash command that you can use to check how many tokens you've used when interacting with {% data variables.product.prodname_copilot_short %} in the CLI. You run `/tokencount start` when you want to start measuring your token usage, and then you can run `/tokencount` at any later point to check how many tokens you've used since starting the count. + +The extension keeps a running total of tokens used in the session by subscribing to events emitted by the CLI, which is something a one-off shell command can't do. + +### Step 1: Create the extension file + +1. In the root of the Git repository for your project, create the following directories and file: + + ```text + .github/extensions/token-counter/extension.mjs + ``` + +1. Add this code to `extension.mjs`: + + ```javascript copy + // Extension: token-counter + // Adds a /tokencount slash command that reports how many tokens you've used + // since you started counting. + + import { joinSession } from "@github/copilot-sdk/extension"; + + // Module-level state. The extension runs as a single long-lived process for + // the whole session, so these values persist between command invocations. + let tokensUsed = 0; // Running total of tokens used so far this session. + let startedAt = null; // tokensUsed when "/tokencount start" was last run. + // null = not started. + // startedAt allows you to count tokens multiple times in a session. + + const session = await joinSession({ + commands: [ + { + name: "tokencount", + description: "Report how many tokens you've used since " + + "'/tokencount start'.", + handler: async (ctx) => { + const arg = (ctx.args ?? "").trim(); + + if (arg === "start") { + // Reset: remember the current total as the new baseline. + startedAt = tokensUsed; + await session.log( + "Token counter started. Run '/tokencount' later to " + + "see how many tokens you've used.", + { level: "info" }, + ); + return; + } + + if (startedAt === null) { + await session.log( + "The token counter has not been started. Start by " + + "entering '/tokencount start'.", + { level: "info" }, + ); + return; + } + + const used = tokensUsed - startedAt; + await session.log( + `You have used ${used} tokens since entering ` + + "'/tokencount start'.", + { level: "info" }, + ); + }, + }, + ], + }); + + // The CLI emits an "assistant.usage" event after each assistant turn. Add the + // tokens it reports to a running total kept in the extension's memory. + session.on("assistant.usage", (event) => { + const { inputTokens = 0, outputTokens = 0 } = event.data ?? {}; + tokensUsed += inputTokens + outputTokens; + }); + ``` + +### Step 2: Load the extension + +Start an interactive session from the same repository, with experimental features enabled: + +```shell copy +copilot --experimental +``` + +If you already had a session open, you can run `/clear` to start a fresh session, which reloads extensions from disk. + +### Step 3: Confirm the extension is running + +Run the `/extensions manage` command to open the extension manager. Your `token-counter` extension should be listed under the **Project** group with a status of **running**. Press Esc to close the manager. + +You can also run `/env` to see a summary of everything loaded into the session, including extensions. + +### Step 4: Try it out + +Unlike a tool, you invoke a slash command yourself. Start the counter: + +```copilot copy +/tokencount start +``` + +Send {% data variables.product.prodname_copilot_short %} a prompt or two so that it uses some tokens—for example, ask it to explain a file. Then check how many tokens you've used: + +```copilot copy +/tokencount +``` + +If you run `/tokencount start` again, the count restarts from zero. + +### How the example works + +The call to `joinSession` registers everything the extension adds to the CLI—in this case, a single slash command. + +A slash command is defined by three fields: + +* **`name`**—the command name, without the leading slash. Registering `tokencount` is what makes `/tokencount` available in the session. +* **`description`**—the text shown next to the command in the slash command picker. +* **`handler`**—an async function that runs when you invoke the command. It receives a context object whose `args` property holds the raw text typed after the command name. For `/tokencount start`, `ctx.args` is `"start"`; for a bare `/tokencount`, it is an empty string. + +The handler writes its output back to the session with `session.log(message, { level: "info" })`, which prints the message in the transcript. + +To know how many tokens have been used, the extension subscribes to a session event: + +```javascript +session.on("assistant.usage", (event) => { + const { inputTokens = 0, outputTokens = 0 } = event.data ?? {}; + tokensUsed += inputTokens + outputTokens; +}); +``` + +The CLI emits an `assistant.usage` event after each assistant turn, carrying the input and output token counts for that turn. The extension adds them to the running total in `tokensUsed`. When you run `/tokencount start`, the handler records the current total in `startedAt`. Entering `/tokencount` with no argument later in the session reports the difference. Because both variables live in the long-lived extension process, they persist for the whole session. + +> [!NOTE] +> The totals are held in memory, so they reset whenever the extension is reloaded or the session restarts (for example, after `/clear`). + +## Editing and reloading an extension + +As you develop an extension, you'll edit `extension.mjs` and want to see your changes. After saving the file, you can pick up the new version in any of these ways: + +* Ask {% data variables.product.prodname_copilot_short %} to reload extensions—for example, `Reload my extensions`. +* Run `/clear` to start a new session, which reloads extensions from disk. +* Restart the CLI. + +If an extension fails to start or behaves unexpectedly, run `/extensions manage` and inspect the extension to see its status and the path to its log file. Each extension writes a log under `~/.copilot/logs/extensions/`, which is the best place to look when something goes wrong. + +## Next steps + +* Adapt one of these examples to your own needs. In the CLI, ask {% data variables.product.prodname_copilot_short %} to modify the behavior of either of the example extensions. +* Share a user-level extension. For example, move the `tool-time` extension into a repository's `.github/extensions/` directory to share it with everyone who works in that repository. +* Ask {% data variables.product.prodname_copilot_short %} to create a new extension for you from scratch. {% data variables.copilot.copilot_cli_short %} extensions are powered by the {% data variables.copilot.copilot_sdk_short %}, so an extension can do anything the SDK makes possible, allowing you to add interactive views with buttons and forms, as well as new tools and commands. See [AUTOTITLE](/copilot/how-tos/copilot-sdk). + +## Further reading + +* [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-command-reference) diff --git a/content/copilot/tutorials/index.md b/content/copilot/tutorials/index.md index 0cc93395ab81..668bcfbc4d03 100644 --- a/content/copilot/tutorials/index.md +++ b/content/copilot/tutorials/index.md @@ -33,6 +33,7 @@ children: - /upgrade-projects - /copilot-cli-hooks - /use-an-ai-sme + - /create-an-extension redirect_from: - /copilot/using-github-copilot/guides-on-using-github-copilot contentType: tutorials diff --git a/content/copilot/tutorials/roll-out-at-scale/drive-downstream-impact/increase-test-coverage.md b/content/copilot/tutorials/roll-out-at-scale/drive-downstream-impact/increase-test-coverage.md index 483be8391fc5..2c690673dc23 100644 --- a/content/copilot/tutorials/roll-out-at-scale/drive-downstream-impact/increase-test-coverage.md +++ b/content/copilot/tutorials/roll-out-at-scale/drive-downstream-impact/increase-test-coverage.md @@ -135,7 +135,7 @@ Developers **should not**: * [{% data variables.copilot.copilot_chat_dotcom_short %}](/copilot/using-github-copilot/copilot-chat/asking-github-copilot-questions-in-github) * [{% data variables.product.prodname_copilot_short %} inline suggestions](/copilot/using-github-copilot/getting-code-suggestions-in-your-ide-with-github-copilot) * [{% data variables.copilot.copilot_chat_short %} in the IDE](/copilot/using-github-copilot/copilot-chat/asking-github-copilot-questions-in-your-ide) -* [{% data variables.copilot.copilot_cloud_agent %}](/copilot/concepts/about-copilot-cloud-agent) +* [{% data variables.copilot.copilot_cloud_agent %}](/copilot/concepts/agents/cloud-agent/about-cloud-agent) ## Metrics to watch diff --git a/content/copilot/tutorials/roll-out-at-scale/govern-at-scale/index.md b/content/copilot/tutorials/roll-out-at-scale/govern-at-scale/index.md index 291ea985af69..0e38dbcb6c77 100644 --- a/content/copilot/tutorials/roll-out-at-scale/govern-at-scale/index.md +++ b/content/copilot/tutorials/roll-out-at-scale/govern-at-scale/index.md @@ -8,6 +8,7 @@ children: - /resources-for-approval - /establish-ai-managers - /govern-for-adoption + - /pilot-a-feature-or-model - /maintain-codebase-standards contentType: tutorials --- diff --git a/content/copilot/tutorials/roll-out-at-scale/govern-at-scale/pilot-a-feature-or-model.md b/content/copilot/tutorials/roll-out-at-scale/govern-at-scale/pilot-a-feature-or-model.md new file mode 100644 index 000000000000..201791cdd8be --- /dev/null +++ b/content/copilot/tutorials/roll-out-at-scale/govern-at-scale/pilot-a-feature-or-model.md @@ -0,0 +1,170 @@ +--- +title: Pilot a new Copilot feature or model in your enterprise +shortTitle: Pilot a feature or model +intro: Pilot a new {% data variables.product.prodname_copilot_short %} feature or model to validate adoption, control costs, and gather evidence for a wider rollout. +permissions: Enterprise owners, organization owners, and billing managers +versions: + feature: copilot +category: + - Roll Copilot out at scale +contentType: tutorials +allowTitleToDifferFromFilename: true +--- + +This tutorial walks you through running a pilot end to end: setting a budget, enabling the feature for a contained group, monitoring results, and using the evidence to make and report a go or no-go decision. + +## Before you start your pilot + +Before you enable anything, make sure you've evaluated the feature or model and confirmed it meets your compliance requirements. See [AUTOTITLE](/copilot/concepts/preparing-for-new-features-and-models). + +If you're not sure whether a feature or model is safe to enable, check with security and compliance teams at your company before you start the pilot. + +### Define what success looks like + +Decide what a "go" looks like before you start, so you judge the pilot against a plan instead of after the fact. Write down a few concrete success criteria that cover adoption (for example, a minimum share of active users), cost (staying within the ceiling you set), and qualitative feedback (for example, most participants would recommend the feature). You'll check the pilot's actual results against these criteria in [Make a go or no-go decision](#make-a-go-or-no-go-decision). + +Also plan how long the pilot will run. Budgets and the shared pool of {% data variables.product.prodname_ai_credits_short %} reset every billing cycle, so plan for the pilot to span at least one full billing cycle, typically four to six weeks; a shorter run can leave you with too little cost and adoption data to draw a reliable conclusion. + +### Choose who to include in your pilot group + +Since features and models are enabled at the organization level, everyone who receives their {% data variables.product.prodname_copilot_short %} license through that organization will get access, not just a hand-picked subset, and not necessarily everyone who's a member of it. So instead of selecting individuals, look for an organization whose licensed population already reflects the criteria below: + +* **Developers doing real, meaningful work.** Pilot participants should use the feature on the languages, frameworks, and repositories that matter to your organization, not on throwaway projects. Usage and cost signals are only trustworthy if they come from genuine work. +* **A mix of skill and seniority levels.** Include both experienced engineers and people who are newer to your codebase or to {% data variables.product.prodname_copilot_short %}. Different levels of experience use the feature differently and uncover different value and different problems. +* **A range of teams and workflows.** A feature that helps one team's workflow may not help another's. Spanning a few teams gives you a more reliable picture of where the feature adds value and where it doesn't. +* **People willing to give feedback.** Much of a pilot's value comes from qualitative feedback, not just metrics. Choose participants who will engage, report what works and what doesn't, and respond when you ask for their input. + +If none of your organizations fit, for example, if the right mix of people is scattered across several organizations, or your organizations are too large to serve as a contained pilot, you can create a dedicated organization and add only your chosen users to it. This gives you precise control over who's included, but it means migrating or duplicating repository access for those users, and it may not work well for features like {% data variables.copilot.copilot_cloud_agent %} that depend on deep repository-level context. This tutorial assumes you're using an existing organization; if you go the dedicated-organization route, treat setting it up and adding members as an extra step before [Enable the feature for your pilot group](#enable-the-feature-for-your-pilot-group). + +### Estimate costs and set a budget ceiling + +Many of the features and models you'll want to pilot are billed through usage, so the spending isn't fixed by the number of licenses you assign. + +A handful of active developers using a frontier model or an agentic feature could consume more than you'd expect. Work out a rough estimate of what the pilot could cost, and decide on a ceiling you're willing to spend before you enable the feature. + +Start by understanding how the feature or model is billed. Usage-based features consume {% data variables.product.prodname_ai_credits %}, and the cost of each interaction depends on the model and the number of tokens consumed. To understand which features count toward usage, how included credits are pooled across your enterprise, and how overages are charged, see [AUTOTITLE](/copilot/concepts/billing/usage-based-billing-for-organizations-and-enterprises). + +To produce an estimate, combine three numbers: + +* **The number of people in your pilot group**, which you defined in the previous section. +* **How heavily you expect them to use the feature.** You won't know this until people are actually using it, so pick a deliberately high-end estimate rather than trying to predict it precisely. If you've already rolled out a comparable feature or model, use its actual usage as a reference point. If not, assume frequent, heavy use per developer, since agentic features and frontier models cost more per interaction than a quick chat question. A small group doing intensive work can cost more than a large group using the feature occasionally. +* **The included allowance you can draw on.** Each assigned license comes with a monthly amount of included {% data variables.product.prodname_ai_credits_short %} that are pooled at the enterprise level, so some of the pilot's usage may be covered before any overage is billed. + +Use these to set a ceiling: the maximum you're prepared to spend on the pilot before you'd want to pause and reassess. Choose a figure that gives the pilot room to generate meaningful usage data while capping your exposure if consumption runs higher than expected. If you adjust your pilot group size later, revisit this estimate. + +For example, suppose you pilot an agentic feature with 20 developers on {% data variables.copilot.copilot_business_short %}. Each license includes {% data variables.copilot.ai_credits_per_user_business %} {% data variables.product.prodname_ai_credits_short %} per month, so your pilot group draws on a shared pool of 20 × {% data variables.copilot.ai_credits_per_user_business %} included {% data variables.product.prodname_ai_credits_short %} before any metered charges begin. If you expect heavy use of roughly 3,000 {% data variables.product.prodname_ai_credits_short %} per developer that month, estimate total usage as 20 × 3,000. Subtract the included pool to estimate metered usage, then multiply by {% data variables.product.prodname_ai_credits_value %} to estimate overage cost and set your ceiling slightly above that. You'll create the budget that enforces this ceiling after you enable the feature, in [Set a budget to cap the pilot's costs](#set-a-budget-to-cap-the-pilots-costs). + +## Enable the feature for your pilot group + +Now that you've estimated costs and chosen who to include, you can turn the feature on in a contained way. The goal is to give your pilot group a real, correctly governed experience while keeping the rest of your enterprise unaffected. + +You'll do this in four steps: + +1. Confirm organization membership and license assignment. +1. Configure the policies that govern the feature. +1. Enable the feature for that organization only. +1. Set a budget to cap the pilot's costs. + +### Confirm organization membership and license assignment + +Membership alone isn't enough: most {% data variables.product.prodname_copilot_short %} policies apply based on which organization assigns a user's {% data variables.product.prodname_copilot_short %} license, not simply which organizations they belong to. If someone belongs to multiple organizations, confirm their license is assigned through the pilot organization specifically. If it's assigned through a different one, the pilot organization's policies won't govern their access, and the feature enablement in this tutorial won't apply to them. + +If any of your pilot users aren't yet members, or are members but receive their license elsewhere, add them to the pilot organization and assign their {% data variables.product.prodname_copilot_short %} license through it before you continue. + +### Configure policies before you enable the feature + +{% data variables.product.prodname_copilot_short %} policies control which features and models your users can access and how their data is handled. Configure these policies for the pilot organization *before* you turn the feature on, so that pilot users get a correctly governed experience from their very first interaction rather than a brief window where the feature is available without the guardrails you intend. + +Decide which features and models the pilot should allow, and set the policies at the organization level so the change only affects your pilot organization; policies set at the enterprise level apply to every organization in your enterprise. If your enterprise policy doesn't allow organizations to set their own value, an enterprise owner must change this before you can continue. To understand how policies cascade from the enterprise to organizations and who can override settings at each level, see [AUTOTITLE](/copilot/concepts/policies). + +### Enable the feature for a single organization + +As an organization owner, enable the specific feature or model for the pilot organization only, from the organization's {% data variables.product.prodname_copilot_short %} policy settings. For the full steps, see [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-organization/manage-policies). Confirm that the setting applies to the pilot organization and not to the enterprise as a whole before you save it. + +### Set a budget to cap the pilot's costs + +Earlier, you decided on a spending ceiling for the pilot. Now you'll create the budget that enforces it. + +First, understand what an organization budget does. It caps *metered* charges only, and it becomes active only after the shared pool of {% data variables.product.prodname_ai_credits_short %} is exhausted. It doesn't cap total spend, so set the limit to the amount of overage you're willing to pay beyond the included allowance, not to the pilot's total cost. Critically, a budget is **not a hard stop by default**: charges continue to accrue past the limit unless you enable **Stop usage when budget limit is reached** when you create it. + +As an organization owner or billing manager, create a budget scoped to the pilot organization, set its limit to the overage ceiling you chose, and enable **Stop usage when budget limit is reached**. + +If you want to prevent any single pilot user from running up consumption, also set a universal user-level budget. This is the only control that is always a hard stop, and it counts a user's consumption from both the shared pool and metered usage toward the same limit. Set it to a modest amount that lets participants do real work but stops any one user from consuming an outsized share of the pilot's budget. + +For how budgets meter and block usage at the user, organization, and enterprise levels, see [AUTOTITLE](/copilot/concepts/billing/budgets-for-usage-based-billing). + +## Monitor the pilot + +Throughout the pilot, track adoption, collect developer feedback, and watch cost and agentic activity against the success criteria you defined at the start. Together, these signals help you decide whether to expand the feature. + +### Track adoption and usage with {% data variables.product.prodname_copilot_short %} metrics + +Use {% data variables.product.prodname_copilot_short %} usage metrics to see how many of your pilot users are active, how often they use the feature, and how those numbers trend over the pilot period. Adoption that holds steady or grows is a strong signal; usage that spikes and then fades may mean the feature isn't fitting into developers' real workflows. + +* To understand what the metrics cover and how to interpret them, see [AUTOTITLE](/copilot/concepts/copilot-usage-metrics/copilot-metrics). +* To view the dashboard for your organization or enterprise, see [AUTOTITLE](/copilot/how-tos/administer-copilot/view-usage-and-adoption). + +### Collect developer feedback from your pilot group + +Metrics tell you *whether* people use the feature, but not *why* or how well it works for them. Gather qualitative feedback to fill that gap. Run short surveys, hold regular check-ins, or set up a dedicated channel where pilot users can report what's working and what isn't. + +This is where the participants you chose for their willingness to give feedback pay off. Ask specific questions: where the feature saved time, where it produced poor results, and whether it changed how they work. This feedback often surfaces problems and opportunities that the numbers alone won't reveal, and it gives you concrete examples to share with leadership later. + +### Watch for cost and agentic activity in budgets and the audit log + +Keep an eye on spending against the budget you created throughout the pilot, not just at the end. If usage approaches your ceiling sooner than expected, that's useful early evidence about what a wider rollout would cost. For the steps to monitor what you've spent against the budget, see [AUTOTITLE](/copilot/how-tos/manage-and-track-spending/monitor-ai-usage). For tracking spending over time, see [AUTOTITLE](/copilot/how-tos/manage-and-track-spending/manage-company-spending). + +Alongside cost, watch whether the feature is being used safely. This is the third question leadership will ask, and the pilot is your chance to answer it with evidence rather than assurances. For agentic features, review agent sessions to understand what the feature is actually doing on your developers' behalf and to catch any unexpected or unwanted behavior; the audit log records when the feature is enabled or disabled, but doesn't capture that level of detail. Confirm that the policies and budgets you set are behaving as intended, and note anything that would need tighter guardrails at a larger scale. See [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/monitor-agentic-activity). + +## Make a go or no-go decision + +At the end of the pilot, check your results against the success criteria you defined up front, and decide whether to expand the feature, hold and gather more data, or roll it back. Make the decision deliberately and document the evidence behind it. + +### Confirm you have enough evidence to decide + +Before you decide, check that the pilot ran for the duration you planned and produced enough data to support a defensible conclusion. + +Pilot usage draws from the shared pool first (at no extra cost) before metered billing begins, so $0 spend early in the pilot is expected and a shorter run can make cost data misleading. + +You're ready to decide when you have: + +* Adoption metrics that show a stable pattern rather than a single early burst. +* Actual cost data, spanning at least one billing cycle, that you can compare against your estimate. +* Feedback from a representative range of your pilot users. + +If any of these is thin, for example if usage is still climbing or only a few participants engaged, consider extending the pilot rather than deciding on incomplete evidence. A decision you can back with data is far easier to defend to leadership and far less likely to need reversing. + +## Report to leadership + +To justify your decision and build a case for a wider rollout, report the pilot's results to leadership. + +### Identify the data to share + +Pull together the signals you gathered during the pilot: + +* **Adoption metrics**, showing how many pilot users were active and how usage trended over time. +* **Actual cost against budget**, comparing what the pilot spent to the ceiling you set and to your original estimate. +* **Feedback themes**, summarizing where participants found value and where they ran into problems. +* **Safety and governance observations**, including anything you learned from the audit log or from how policies and budgets behaved. + +### Frame adoption, cost, and safety against ROI + +Present the data in business terms rather than as raw figures. Connect adoption and feedback to the value the feature delivered, such as time saved, work unblocked, or quality improved, and weigh that against its actual cost and any governance or risk considerations you observed. + +Close with a clear recommendation: expand, hold, or stop, and what it would cost to roll out more widely. Framing the pilot as a question of return on investment, predictable cost, and managed risk gives leadership what they need to approve a wider rollout with confidence. + +## Act on your decision + +Take the next step based on the evidence you gathered and the decision you made. + +### Expand the rollout across your enterprise + +If the evidence supports a go, widen the rollout in stages rather than all at once. Extend the feature policy to more organizations, and only then to the rest of your enterprise once each stage looks healthy. As you add more users, raise or add budgets so your spending controls scale with the larger population, and keep monitoring adoption and cost at each step. + +For broader guidance on managing a rollout across your enterprise, see [AUTOTITLE](/copilot/tutorials/roll-out-at-scale). + +### Roll back and disable the feature cleanly + +If the evidence points to a no-go, disable the feature cleanly so there's no lingering cost or confusion. Turn off the feature or model policy for the pilot organization, and tell your participants the pilot has ended and why, so they're not left wondering why the feature disappeared. + +Confirm there's no remaining usage that could continue to bill against your budget, and review or remove the pilot budget so it doesn't affect future reporting. Keep the data and feedback you gathered: even a no-go is a useful result to share with leadership, and it may inform a later re-evaluation when the feature matures. diff --git a/content/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets.md b/content/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets.md index ddfa3d8746de..d5003889eeb0 100644 --- a/content/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets.md +++ b/content/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets.md @@ -120,6 +120,8 @@ Optionally, you can choose to dismiss stale pull request approvals when commits Optionally, you can choose to require reviews from code owners. If you do, any pull request that modifies content with a code owner must be approved by that code owner before the pull request can be merged into the protected branch. Note that if code has multiple owners, an approval from _any_ of the code owners will be sufficient to meet this requirement. For more information, see [AUTOTITLE](/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners). +Optionally, you can restrict who can dismiss pull request reviews. If you enable this setting, select the users, teams, or {% data variables.product.prodname_github_apps %} that can dismiss reviews on branches targeted by the ruleset. You can configure this setting in the UI or through the REST API or GraphQL API. For more information, see [AUTOTITLE](/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/dismissing-a-pull-request-review). + Optionally, you can require an approval from someone other than the last person to push to a branch before a pull request can be merged. This means at least one other authorized reviewer has approved any changes. For example, the "last reviewer" can check that the latest set of changes incorporates feedback from other reviews, and does not add new, unreviewed content. For complex pull requests that require many reviews, requiring an approval from someone other than the last person to push can be a compromise that avoids the need to dismiss all stale reviews: with this option, "stale" reviews are not dismissed, and the pull request remains approved as long as someone other than the person who made the most recent changes approves it. Users who have already reviewed a pull request can reapprove after the most recent push to meet this requirement. If you are concerned about pull requests being "hijacked" (where unapproved content is added to approved pull requests), it is safer to dismiss stale reviews. diff --git a/contributing/README.md b/contributing/README.md index 848e3d1474e8..987a112b9b32 100644 --- a/contributing/README.md +++ b/contributing/README.md @@ -14,6 +14,6 @@ Here, you'll find additional information that might be helpful as you work on a - [troubleshooting](./troubleshooting.md) - some help for troubleshooting failed and stalled status checks - [Reusables](https://docs.github.com/en/contributing/writing-for-github-docs/creating-reusable-content#about-reusables) - We create reusables and call them from articles to help us keep content that is used in multiple places up to date. - [Variables](https://docs.github.com/en/contributing/writing-for-github-docs/creating-reusable-content#about-variables) - We use variables the same way we use reusables. Variables are for short strings of reusable text. -- [Tests](/tests/README.md) - We use tests to ensure content will render correctly on the site. Tests run automatically in your PR, and sometimes it's also helpful to run them locally. +- [Tests](/src/tests/README.md) - We use tests to ensure content will render correctly on the site. Tests run automatically in your PR, and sometimes it's also helpful to run them locally. You can also read the READMEs in the `src/` directory to learn more about the features of the docs site. diff --git a/contributing/development.md b/contributing/development.md index b7b181740bc4..913fb8df4838 100644 --- a/contributing/development.md +++ b/contributing/development.md @@ -32,7 +32,7 @@ Power users may want to read more about [debugging the docs application](./debug ### Using GitHub Codespaces -As an alternative, you can simply use [GitHub Codespaces](https://docs.github.com/en/codespaces/quickstart). For more information about using a codespace for working on GitHub documentation, see [Working in a codespace](https://github.com/github/docs/blob/main/contributing/codespace.md). +As an alternative, you can simply use [GitHub Codespaces](https://docs.github.com/en/codespaces/quickstart). For more information about using a codespace for working on GitHub documentation, see [Working on GitHub Docs in a codespace](https://docs.github.com/en/contributing/setting-up-your-environment-to-work-on-github-docs/working-on-github-docs-in-a-codespace). In a matter of minutes, you will be ready to edit, review and test your changes directly from the comfort of your browser. diff --git a/data/reusables/copilot/auto-model-discount.md b/data/reusables/copilot/auto-model-discount.md index 3649baa13f4a..407c30b920e2 100644 --- a/data/reusables/copilot/auto-model-discount.md +++ b/data/reusables/copilot/auto-model-discount.md @@ -1 +1 @@ -If you are on a paid {% data variables.product.prodname_copilot_short %} plan, you qualify for a 10% discount on model costs while using {% data variables.copilot.copilot_auto_model_selection_short %} in {% data variables.copilot.copilot_chat_short %}, {% data variables.copilot.copilot_cli_short %}, or {% data variables.copilot.copilot_cloud_agent %}. +If you are on a paid {% data variables.product.prodname_copilot_short %} plan, you qualify for a 10% discount on model costs while using {% data variables.copilot.copilot_auto_model_selection_short %} in {% data variables.copilot.copilot_chat_short %}, {% data variables.copilot.copilot_cli_short %}, {% data variables.copilot.github_copilot_app %}, or {% data variables.copilot.copilot_cloud_agent %}. diff --git a/data/reusables/copilot/copilot-cli/cli-extensions-experimental.md b/data/reusables/copilot/copilot-cli/cli-extensions-experimental.md new file mode 100644 index 000000000000..8ce3a4759aae --- /dev/null +++ b/data/reusables/copilot/copilot-cli/cli-extensions-experimental.md @@ -0,0 +1,2 @@ +> [!NOTE] +> {% data variables.copilot.copilot_cli %} extensions are currently an experimental feature and are subject to change. diff --git a/data/reusables/copilot/model-fable-disabled.md b/data/reusables/copilot/model-fable-disabled.md deleted file mode 100644 index c7feac2a8541..000000000000 --- a/data/reusables/copilot/model-fable-disabled.md +++ /dev/null @@ -1 +0,0 @@ -> [!NOTE] {% data variables.copilot.copilot_claude_fable_5 %} is currently unavailable. For more information, see [Anthropic's announcement](https://www.anthropic.com/news/fable-mythos-access). \ No newline at end of file diff --git a/data/reusables/elm/ghes-version-requirement.md b/data/reusables/elm/ghes-version-requirement.md deleted file mode 100644 index 8907fe89a3fa..000000000000 --- a/data/reusables/elm/ghes-version-requirement.md +++ /dev/null @@ -1,3 +0,0 @@ -To use ELM, upgrade to a supported {% data variables.product.prodname_ghe_server %} patch release. - -Minimum versions: **3.21.0**, **3.20.2**, **3.19.6**, **3.18.9**, **3.17.15**. diff --git a/data/tables/copilot/auto-model-selection.yml b/data/tables/copilot/auto-model-selection.yml index 2d1eb9bd2880..ef56247b0fa4 100644 --- a/data/tables/copilot/auto-model-selection.yml +++ b/data/tables/copilot/auto-model-selection.yml @@ -16,47 +16,56 @@ # - cloud_agent: Availability for Copilot cloud agent. # - chat: Availability for Copilot Chat. # - cli: Availability for Copilot CLI. +# - app: Availability for GitHub Copilot app. # OpenAI - name: GPT-5 mini cloud_agent: false chat: true cli: true + app: true - name: GPT-5.3-Codex cloud_agent: true chat: true cli: true + app: true - name: GPT-5.4 cloud_agent: true chat: true cli: true + app: true - name: GPT-5.4 mini cloud_agent: false chat: true cli: true + app: true # Anthropic - name: Claude Haiku 4.5 cloud_agent: false chat: true cli: true + app: true - name: Claude Sonnet 4.6 cloud_agent: true chat: true cli: true + app: true # Microsoft - name: MAI-Code-1-Flash cloud_agent: true chat: true cli: true + app: true # Fine-tuned OAI models - name: Raptor mini cloud_agent: false chat: true cli: false + app: false diff --git a/next.config.ts b/next.config.ts index a33c943d7eb8..6fb873c313ff 100644 --- a/next.config.ts +++ b/next.config.ts @@ -12,9 +12,10 @@ const { data } = frontmatter(fs.readFileSync(homepage, 'utf8')) const productIds = data.children as string[] const config: NextConfig = { - // Transpile @primer/react so Next's webpack can process its CSS and other assets - // This ensures CSS in node_modules/@primer/react is handled by the app's loaders. - transpilePackages: ['@primer/react'], + // Transpile @primer/react and @primer/react-brand so Next's webpack can process their + // CSS and other assets. This ensures CSS from these node_modules packages is handled by + // the app's loaders. + transpilePackages: ['@primer/react', '@primer/react-brand'], // Keep OTel packages out of the Next.js server bundle. // They must be loaded via native require() for auto-instrumentation to work. serverExternalPackages: [ diff --git a/package-lock.json b/package-lock.json index ff363cfcc6c5..b0f400c4d9ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "@primer/octicons-react": "^19.22.0", "@primer/primitives": "^11.3.1", "@primer/react": "^38.13.0", + "@primer/react-brand": "^0.70.0", "accept-language-parser": "^1.5.0", "ajv": "^8.18.0", "ajv-errors": "^3.0.0", @@ -505,13 +506,6 @@ "node": ">=18" } }, - "node_modules/@elastic/elasticsearch/src/search/lib/apache-arrow-stub": { - "extraneous": true - }, - "node_modules/@elastic/elasticsearch/src/search/vendor/apache-arrow-stub": {}, - "node_modules/@elastic/elasticsearch/vendor/apache-arrow-stub": { - "extraneous": true - }, "node_modules/@elastic/transport": { "version": "8.10.1", "resolved": "https://registry.npmjs.org/@elastic/transport/-/transport-8.10.1.tgz", @@ -2352,6 +2346,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", @@ -2652,6 +2647,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -4861,6 +4857,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4900,6 +4897,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4920,6 +4918,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4940,6 +4939,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4960,6 +4960,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4980,6 +4981,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5000,6 +5002,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5020,6 +5023,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5040,6 +5044,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5060,6 +5065,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5080,6 +5086,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5100,6 +5107,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5120,6 +5128,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5140,6 +5149,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5157,6 +5167,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, "license": "Apache-2.0", "optional": true, "bin": { @@ -5185,6 +5196,7 @@ "integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright": "1.60.0" }, @@ -5306,6 +5318,40 @@ } } }, + "node_modules/@primer/react-brand": { + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@primer/react-brand/-/react-brand-0.70.0.tgz", + "integrity": "sha512-6La/hIIbNp8OA3ZxUTzxRJmpr8Imr5NdMiSVjWJTGyEtIuEJ9d4VA9OL0qQkg5cdeq+GTr6qZuTHQQh8cAhG8g==", + "license": "MIT", + "dependencies": { + "@oddbird/popover-polyfill": "0.5.2", + "@primer/behaviors": "^1.8.2" + }, + "engines": { + "node": ">=24.0.0", + "npm": ">=8.0.0" + }, + "peerDependencies": { + "@types/react": ">=18 <20", + "@types/react-dom": ">=18 <20", + "react": ">=18 <20", + "react-dom": ">=18 <20" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@primer/react-brand/node_modules/@oddbird/popover-polyfill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@oddbird/popover-polyfill/-/popover-polyfill-0.5.2.tgz", + "integrity": "sha512-iFrvar5SOMtKFOSjYvs4z9UlLqDdJbMx0mgISLcPedv+g0ac5sgeETLGtipHCVIae6HJPclNEH5aCyD1RZaEHw==", + "license": "BSD-3-Clause" + }, "node_modules/@primer/react/node_modules/@github/tab-container-element": { "version": "4.8.2", "resolved": "https://registry.npmjs.org/@github/tab-container-element/-/tab-container-element-4.8.2.tgz", @@ -5934,6 +5980,7 @@ "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", @@ -6075,6 +6122,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -6085,6 +6133,7 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -6244,6 +6293,7 @@ "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/types": "8.58.0", @@ -6917,6 +6967,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6956,6 +7007,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -7046,7 +7098,7 @@ } }, "node_modules/apache-arrow": { - "resolved": "node_modules/@elastic/elasticsearch/src/search/vendor/apache-arrow-stub", + "resolved": "src/search/vendor/apache-arrow-stub", "link": true }, "node_modules/arg": { @@ -7476,6 +7528,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001733", "electron-to-chromium": "^1.5.199", @@ -8953,6 +9006,7 @@ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -9013,6 +9067,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -9277,6 +9332,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -10177,6 +10233,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -10420,6 +10477,7 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==", "dev": true, + "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -11823,6 +11881,7 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, + "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -14215,6 +14274,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, "license": "MIT", "optional": true }, @@ -14951,6 +15011,7 @@ "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "playwright-core": "cli.js" }, @@ -15011,6 +15072,7 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -15161,6 +15223,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -15179,6 +15242,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -15205,7 +15269,8 @@ "version": "19.2.4", "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/react-markdown": { "version": "10.1.0", @@ -15807,6 +15872,7 @@ "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -16866,6 +16932,7 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -17202,6 +17269,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -17541,6 +17609,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -17751,6 +17820,7 @@ "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -17859,6 +17929,7 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -18485,6 +18556,9 @@ "peerDependencies": { "eslint": "^8.0.0 || ^9.0.0" } + }, + "src/search/vendor/apache-arrow-stub": { + "version": "0.0.0" } } } diff --git a/package.json b/package.json index e388efd04537..28713053c6f0 100644 --- a/package.json +++ b/package.json @@ -191,6 +191,7 @@ "@primer/octicons-react": "^19.22.0", "@primer/primitives": "^11.3.1", "@primer/react": "^38.13.0", + "@primer/react-brand": "^0.70.0", "accept-language-parser": "^1.5.0", "ajv": "^8.18.0", "ajv-errors": "^3.0.0", diff --git a/src/frame/pages/app.tsx b/src/frame/pages/app.tsx index 979be3598c20..2f3f65495b24 100644 --- a/src/frame/pages/app.tsx +++ b/src/frame/pages/app.tsx @@ -3,6 +3,7 @@ import App from 'next/app' import type { AppProps, AppContext } from 'next/app' import Head from 'next/head' import { ThemeProvider } from '@primer/react' +import { ThemeProvider as BrandThemeProvider } from '@primer/react-brand' import { useRouter } from 'next/router' import { initializeEvents } from '@/events/components/events' @@ -111,12 +112,22 @@ const MyApp = ({ Component, pageProps, languagesContext, stagingName }: MyAppPro dayScheme={theme.component.dayScheme} nightScheme={theme.component.nightScheme} > - - - - - - + {/* + Primer Brand ThemeProvider, nested so migrated @primer/react-brand + components receive brand theme context during the Docs 2026 migration + (github/docs-engineering#5879). Runs alongside the @primer/react + ThemeProvider above while the component-by-component swap is in progress. + Brand expects a CSS color mode ('auto' | 'light' | 'dark'), so pass + theme.css.colorMode rather than the component ('auto' | 'day' | 'night') mode. + */} + + + + + + + + ) diff --git a/src/frame/stylesheets/index.scss b/src/frame/stylesheets/index.scss index 178fcc2d5834..537b338f0096 100644 --- a/src/frame/stylesheets/index.scss +++ b/src/frame/stylesheets/index.scss @@ -8,6 +8,10 @@ @import "@primer/css/alerts/index.scss"; @import "@primer/css/popover/index.scss"; +// Primer Brand design system (Docs 2026 migration — github/docs-engineering#5879). +// Loaded alongside @primer/css during the incremental component-by-component swap. +@import "@primer/react-brand/lib/css/main.css"; + @import "headings.scss"; @import "scroll-top.scss"; @import "utilities.scss"; diff --git a/src/graphql/data/fpt/category-map.json b/src/graphql/data/fpt/category-map.json index 048dcab89f50..103ffb6f1d4b 100644 --- a/src/graphql/data/fpt/category-map.json +++ b/src/graphql/data/fpt/category-map.json @@ -661,6 +661,7 @@ "issuefieldsingleselectvalue": "issues", "issuefieldtext": "issues", "issuefieldtextvalue": "issues", + "issuefieldtimelineoption": "issues", "issuefieldvalueconnection": "issues", "issuefieldvalueedge": "issues", "issuefieldsconnection": "issues", diff --git a/src/graphql/data/fpt/changelog.json b/src/graphql/data/fpt/changelog.json index 3054300cda4c..2dc883fa6fb3 100644 --- a/src/graphql/data/fpt/changelog.json +++ b/src/graphql/data/fpt/changelog.json @@ -1,4 +1,25 @@ [ + { + "schemaChanges": [ + { + "title": "The GraphQL schema includes these changes:", + "changes": [ + "

Type IssueFieldTimelineOption was added

", + "

Field color was added to object type IssueFieldTimelineOption

", + "

Field name was added to object type IssueFieldTimelineOption

", + "

Field options was added to object type IssueFieldAddedEvent

", + "

Field newOptions was added to object type IssueFieldChangedEvent

", + "

Field previousOptions was added to object type IssueFieldChangedEvent

", + "

Field options was added to object type IssueFieldRemovedEvent

", + "

Enum value 'LICENSE_COMPLIANCE_SCANNINGwas added to enumRepositoryRuleType'

", + "

Input field issueCreationPolicy of type IssueCreationPolicy was added to input object type UpdateRepositoryInput

" + ] + } + ], + "previewChanges": [], + "upcomingChanges": [], + "date": "2026-07-07" + }, { "schemaChanges": [ { diff --git a/src/graphql/data/fpt/schema-issues.json b/src/graphql/data/fpt/schema-issues.json index 849437f17483..21d82e7343f3 100644 --- a/src/graphql/data/fpt/schema-issues.json +++ b/src/graphql/data/fpt/schema-issues.json @@ -4769,6 +4769,13 @@ "id": "issuefields", "href": "/graphql/reference/issues#union-issuefields" }, + { + "name": "options", + "description": "

The selected options for option-backed fields; single-select returns one option and multi-select returns many.

", + "type": "[IssueFieldTimelineOption!]", + "id": "issuefieldtimelineoption", + "href": "/graphql/reference/issues#object-issuefieldtimelineoption" + }, { "name": "value", "description": "

The value of the added field.

", @@ -4828,6 +4835,13 @@ "id": "string", "href": "/graphql/reference/other#scalar-string" }, + { + "name": "newOptions", + "description": "

The new options for option-backed fields; single-select returns one option and multi-select returns many.

", + "type": "[IssueFieldTimelineOption!]", + "id": "issuefieldtimelineoption", + "href": "/graphql/reference/issues#object-issuefieldtimelineoption" + }, { "name": "newValue", "description": "

The new value of the field.

", @@ -4842,6 +4856,13 @@ "id": "string", "href": "/graphql/reference/other#scalar-string" }, + { + "name": "previousOptions", + "description": "

The previous options for option-backed fields; single-select returns one option and multi-select returns many.

", + "type": "[IssueFieldTimelineOption!]", + "id": "issuefieldtimelineoption", + "href": "/graphql/reference/issues#object-issuefieldtimelineoption" + }, { "name": "previousValue", "description": "

The previous value of the field.

", @@ -5249,6 +5270,13 @@ "type": "IssueFields", "id": "issuefields", "href": "/graphql/reference/issues#union-issuefields" + }, + { + "name": "options", + "description": "

The removed options for option-backed fields; single-select returns one option and multi-select returns many.

", + "type": "[IssueFieldTimelineOption!]", + "id": "issuefieldtimelineoption", + "href": "/graphql/reference/issues#object-issuefieldtimelineoption" } ], "category": "issues" @@ -5582,6 +5610,30 @@ ], "category": "issues" }, + { + "name": "IssueFieldTimelineOption", + "id": "issuefieldtimelineoption", + "href": "/graphql/reference/issues#object-issuefieldtimelineoption", + "description": "

Represents a selected option for a timeline issue field event.

", + "isDeprecated": false, + "fields": [ + { + "name": "color", + "description": "

The option color.

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

The option name.

", + "type": "String!", + "id": "string", + "href": "/graphql/reference/other#scalar-string" + } + ], + "category": "issues" + }, { "name": "IssueFieldValueConnection", "id": "issuefieldvalueconnection", diff --git a/src/graphql/data/fpt/schema-other.json b/src/graphql/data/fpt/schema-other.json index ad287af86698..81005b014ab7 100644 --- a/src/graphql/data/fpt/schema-other.json +++ b/src/graphql/data/fpt/schema-other.json @@ -643,6 +643,10 @@ "name": "FILE_PATH_RESTRICTION", "description": "

Prevent commits that include changes in specified file and folder paths from\nbeing pushed to the commit graph. This includes absolute paths that contain file names.

" }, + { + "name": "LICENSE_COMPLIANCE_SCANNING", + "description": "

Enforce any added or changed dependencies to comply with the organization's license policy.

" + }, { "name": "LOCK_BRANCH", "description": "

Branch is read-only. Users cannot push to the branch.

" diff --git a/src/graphql/data/fpt/schema-pulls.json b/src/graphql/data/fpt/schema-pulls.json index 8ae21384d4c1..7df86ded970d 100644 --- a/src/graphql/data/fpt/schema-pulls.json +++ b/src/graphql/data/fpt/schema-pulls.json @@ -4,7 +4,7 @@ "name": "addPullRequestCreationCapBypassUsers", "id": "addpullrequestcreationcapbypassusers", "href": "/graphql/reference/pulls#mutation-addpullrequestcreationcapbypassusers", - "description": "

Add users to the pull request creation cap bypass list. Bypassed users can\ncreate pull requests regardless of the configured cap. Only repository admins\ncan manage the bypass list. You can add a maximum of 100 users per request.\nThe bypass list can only hold a maximum of 100 users.

", + "description": "

Add users to the pull request creation cap bypass list. Bypassed users can\ncreate pull requests regardless of the configured cap. Only users with\nmaintainer permissions can manage the bypass list. You can add a maximum of\n100 users per request. The bypass list can only hold a maximum of 100 users.

", "isDeprecated": false, "inputFields": [ { @@ -654,7 +654,7 @@ "name": "removePullRequestCreationCapBypassUsers", "id": "removepullrequestcreationcapbypassusers", "href": "/graphql/reference/pulls#mutation-removepullrequestcreationcapbypassusers", - "description": "

Remove users from the pull request creation cap bypass list. Only repository admins can manage the bypass list.

", + "description": "

Remove users from the pull request creation cap bypass list. Only users with\nmaintainer permissions can manage the bypass list.

", "isDeprecated": false, "inputFields": [ { diff --git a/src/graphql/data/fpt/schema-repos.json b/src/graphql/data/fpt/schema-repos.json index 734c46aa8c2e..35a58dd51966 100644 --- a/src/graphql/data/fpt/schema-repos.json +++ b/src/graphql/data/fpt/schema-repos.json @@ -5340,7 +5340,7 @@ }, { "name": "pullRequestCreationCapConfig", - "description": "

A list of users who are exempt from the pull request creation cap on this repository. Only visible to repository admins.

", + "description": "

The pull request creation cap configuration for this repository. Only visible\nto repository maintainers and administrators.

", "type": "PullRequestCreationCapConfig", "id": "pullrequestcreationcapconfig", "href": "/graphql/reference/pulls#object-pullrequestcreationcapconfig" @@ -10835,6 +10835,13 @@ "id": "uri", "href": "/graphql/reference/other#scalar-uri" }, + { + "name": "issueCreationPolicy", + "description": "

The policy controlling who can create issues in this repository.

", + "type": "IssueCreationPolicy", + "id": "issuecreationpolicy", + "href": "/graphql/reference/issues#enum-issuecreationpolicy" + }, { "name": "name", "description": "

The new name of the repository.

", diff --git a/src/graphql/data/fpt/schema.docs.graphql b/src/graphql/data/fpt/schema.docs.graphql index d62d035bf8a0..0899afab7911 100644 --- a/src/graphql/data/fpt/schema.docs.graphql +++ b/src/graphql/data/fpt/schema.docs.graphql @@ -21145,6 +21145,11 @@ type IssueFieldAddedEvent implements Node @docsCategory(name: "issues") { """ issueField: IssueFields + """ + The selected options for option-backed fields; single-select returns one option and multi-select returns many. + """ + options: [IssueFieldTimelineOption!] + """ The value of the added field. """ @@ -21180,6 +21185,11 @@ type IssueFieldChangedEvent implements Node @docsCategory(name: "issues") { """ newColor: String + """ + The new options for option-backed fields; single-select returns one option and multi-select returns many. + """ + newOptions: [IssueFieldTimelineOption!] + """ The new value of the field. """ @@ -21190,6 +21200,11 @@ type IssueFieldChangedEvent implements Node @docsCategory(name: "issues") { """ previousColor: String + """ + The previous options for option-backed fields; single-select returns one option and multi-select returns many. + """ + previousOptions: [IssueFieldTimelineOption!] + """ The previous value of the field. """ @@ -21544,6 +21559,11 @@ type IssueFieldRemovedEvent implements Node @docsCategory(name: "issues") { The issue field removed. """ issueField: IssueFields + + """ + The removed options for option-backed fields; single-select returns one option and multi-select returns many. + """ + options: [IssueFieldTimelineOption!] } """ @@ -21801,6 +21821,21 @@ type IssueFieldTextValue implements IssueFieldValueCommon & Node @docsCategory(n value: String! } +""" +Represents a selected option for a timeline issue field event. +""" +type IssueFieldTimelineOption @docsCategory(name: "issues") { + """ + The option color. + """ + color: String + + """ + The option name. + """ + name: String! +} + """ Issue field values """ @@ -26547,9 +26582,9 @@ type Mutation @docsCategory(name: "meta") { """ Add users to the pull request creation cap bypass list. Bypassed users can - create pull requests regardless of the configured cap. Only repository admins - can manage the bypass list. You can add a maximum of 100 users per request. - The bypass list can only hold a maximum of 100 users. + create pull requests regardless of the configured cap. Only users with + maintainer permissions can manage the bypass list. You can add a maximum of + 100 users per request. The bypass list can only hold a maximum of 100 users. """ addPullRequestCreationCapBypassUsers( """ @@ -28000,7 +28035,8 @@ type Mutation @docsCategory(name: "meta") { ): RemoveOutsideCollaboratorPayload @docsCategory(name: "orgs") """ - Remove users from the pull request creation cap bypass list. Only repository admins can manage the bypass list. + Remove users from the pull request creation cap bypass list. Only users with + maintainer permissions can manage the bypass list. """ removePullRequestCreationCapBypassUsers( """ @@ -53779,7 +53815,8 @@ type Repository implements Node & PackageOwner & ProjectOwner & ProjectV2Recent ): PullRequest """ - A list of users who are exempt from the pull request creation cap on this repository. Only visible to repository admins. + The pull request creation cap configuration for this repository. Only visible + to repository maintainers and administrators. """ pullRequestCreationCapConfig: PullRequestCreationCapConfig @@ -56066,6 +56103,11 @@ enum RepositoryRuleType { """ FILE_PATH_RESTRICTION + """ + Enforce any added or changed dependencies to comply with the organization's license policy. + """ + LICENSE_COMPLIANCE_SCANNING + """ Branch is read-only. Users cannot push to the branch. """ @@ -69395,6 +69437,11 @@ input UpdateRepositoryInput { """ homepageUrl: URI + """ + The policy controlling who can create issues in this repository. + """ + issueCreationPolicy: IssueCreationPolicy + """ The new name of the repository. """ diff --git a/src/graphql/data/ghec/category-map.json b/src/graphql/data/ghec/category-map.json index 048dcab89f50..103ffb6f1d4b 100644 --- a/src/graphql/data/ghec/category-map.json +++ b/src/graphql/data/ghec/category-map.json @@ -661,6 +661,7 @@ "issuefieldsingleselectvalue": "issues", "issuefieldtext": "issues", "issuefieldtextvalue": "issues", + "issuefieldtimelineoption": "issues", "issuefieldvalueconnection": "issues", "issuefieldvalueedge": "issues", "issuefieldsconnection": "issues", diff --git a/src/graphql/data/ghec/schema-issues.json b/src/graphql/data/ghec/schema-issues.json index 849437f17483..21d82e7343f3 100644 --- a/src/graphql/data/ghec/schema-issues.json +++ b/src/graphql/data/ghec/schema-issues.json @@ -4769,6 +4769,13 @@ "id": "issuefields", "href": "/graphql/reference/issues#union-issuefields" }, + { + "name": "options", + "description": "

The selected options for option-backed fields; single-select returns one option and multi-select returns many.

", + "type": "[IssueFieldTimelineOption!]", + "id": "issuefieldtimelineoption", + "href": "/graphql/reference/issues#object-issuefieldtimelineoption" + }, { "name": "value", "description": "

The value of the added field.

", @@ -4828,6 +4835,13 @@ "id": "string", "href": "/graphql/reference/other#scalar-string" }, + { + "name": "newOptions", + "description": "

The new options for option-backed fields; single-select returns one option and multi-select returns many.

", + "type": "[IssueFieldTimelineOption!]", + "id": "issuefieldtimelineoption", + "href": "/graphql/reference/issues#object-issuefieldtimelineoption" + }, { "name": "newValue", "description": "

The new value of the field.

", @@ -4842,6 +4856,13 @@ "id": "string", "href": "/graphql/reference/other#scalar-string" }, + { + "name": "previousOptions", + "description": "

The previous options for option-backed fields; single-select returns one option and multi-select returns many.

", + "type": "[IssueFieldTimelineOption!]", + "id": "issuefieldtimelineoption", + "href": "/graphql/reference/issues#object-issuefieldtimelineoption" + }, { "name": "previousValue", "description": "

The previous value of the field.

", @@ -5249,6 +5270,13 @@ "type": "IssueFields", "id": "issuefields", "href": "/graphql/reference/issues#union-issuefields" + }, + { + "name": "options", + "description": "

The removed options for option-backed fields; single-select returns one option and multi-select returns many.

", + "type": "[IssueFieldTimelineOption!]", + "id": "issuefieldtimelineoption", + "href": "/graphql/reference/issues#object-issuefieldtimelineoption" } ], "category": "issues" @@ -5582,6 +5610,30 @@ ], "category": "issues" }, + { + "name": "IssueFieldTimelineOption", + "id": "issuefieldtimelineoption", + "href": "/graphql/reference/issues#object-issuefieldtimelineoption", + "description": "

Represents a selected option for a timeline issue field event.

", + "isDeprecated": false, + "fields": [ + { + "name": "color", + "description": "

The option color.

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

The option name.

", + "type": "String!", + "id": "string", + "href": "/graphql/reference/other#scalar-string" + } + ], + "category": "issues" + }, { "name": "IssueFieldValueConnection", "id": "issuefieldvalueconnection", diff --git a/src/graphql/data/ghec/schema-other.json b/src/graphql/data/ghec/schema-other.json index 5171bc4f35d6..3090b71a3d78 100644 --- a/src/graphql/data/ghec/schema-other.json +++ b/src/graphql/data/ghec/schema-other.json @@ -643,6 +643,10 @@ "name": "FILE_PATH_RESTRICTION", "description": "

Prevent commits that include changes in specified file and folder paths from\nbeing pushed to the commit graph. This includes absolute paths that contain file names.

" }, + { + "name": "LICENSE_COMPLIANCE_SCANNING", + "description": "

Enforce any added or changed dependencies to comply with the organization's license policy.

" + }, { "name": "LOCK_BRANCH", "description": "

Branch is read-only. Users cannot push to the branch.

" diff --git a/src/graphql/data/ghec/schema-pulls.json b/src/graphql/data/ghec/schema-pulls.json index 8ae21384d4c1..7df86ded970d 100644 --- a/src/graphql/data/ghec/schema-pulls.json +++ b/src/graphql/data/ghec/schema-pulls.json @@ -4,7 +4,7 @@ "name": "addPullRequestCreationCapBypassUsers", "id": "addpullrequestcreationcapbypassusers", "href": "/graphql/reference/pulls#mutation-addpullrequestcreationcapbypassusers", - "description": "

Add users to the pull request creation cap bypass list. Bypassed users can\ncreate pull requests regardless of the configured cap. Only repository admins\ncan manage the bypass list. You can add a maximum of 100 users per request.\nThe bypass list can only hold a maximum of 100 users.

", + "description": "

Add users to the pull request creation cap bypass list. Bypassed users can\ncreate pull requests regardless of the configured cap. Only users with\nmaintainer permissions can manage the bypass list. You can add a maximum of\n100 users per request. The bypass list can only hold a maximum of 100 users.

", "isDeprecated": false, "inputFields": [ { @@ -654,7 +654,7 @@ "name": "removePullRequestCreationCapBypassUsers", "id": "removepullrequestcreationcapbypassusers", "href": "/graphql/reference/pulls#mutation-removepullrequestcreationcapbypassusers", - "description": "

Remove users from the pull request creation cap bypass list. Only repository admins can manage the bypass list.

", + "description": "

Remove users from the pull request creation cap bypass list. Only users with\nmaintainer permissions can manage the bypass list.

", "isDeprecated": false, "inputFields": [ { diff --git a/src/graphql/data/ghec/schema-repos.json b/src/graphql/data/ghec/schema-repos.json index 734c46aa8c2e..35a58dd51966 100644 --- a/src/graphql/data/ghec/schema-repos.json +++ b/src/graphql/data/ghec/schema-repos.json @@ -5340,7 +5340,7 @@ }, { "name": "pullRequestCreationCapConfig", - "description": "

A list of users who are exempt from the pull request creation cap on this repository. Only visible to repository admins.

", + "description": "

The pull request creation cap configuration for this repository. Only visible\nto repository maintainers and administrators.

", "type": "PullRequestCreationCapConfig", "id": "pullrequestcreationcapconfig", "href": "/graphql/reference/pulls#object-pullrequestcreationcapconfig" @@ -10835,6 +10835,13 @@ "id": "uri", "href": "/graphql/reference/other#scalar-uri" }, + { + "name": "issueCreationPolicy", + "description": "

The policy controlling who can create issues in this repository.

", + "type": "IssueCreationPolicy", + "id": "issuecreationpolicy", + "href": "/graphql/reference/issues#enum-issuecreationpolicy" + }, { "name": "name", "description": "

The new name of the repository.

", diff --git a/src/graphql/data/ghec/schema.docs.graphql b/src/graphql/data/ghec/schema.docs.graphql index d62d035bf8a0..0899afab7911 100644 --- a/src/graphql/data/ghec/schema.docs.graphql +++ b/src/graphql/data/ghec/schema.docs.graphql @@ -21145,6 +21145,11 @@ type IssueFieldAddedEvent implements Node @docsCategory(name: "issues") { """ issueField: IssueFields + """ + The selected options for option-backed fields; single-select returns one option and multi-select returns many. + """ + options: [IssueFieldTimelineOption!] + """ The value of the added field. """ @@ -21180,6 +21185,11 @@ type IssueFieldChangedEvent implements Node @docsCategory(name: "issues") { """ newColor: String + """ + The new options for option-backed fields; single-select returns one option and multi-select returns many. + """ + newOptions: [IssueFieldTimelineOption!] + """ The new value of the field. """ @@ -21190,6 +21200,11 @@ type IssueFieldChangedEvent implements Node @docsCategory(name: "issues") { """ previousColor: String + """ + The previous options for option-backed fields; single-select returns one option and multi-select returns many. + """ + previousOptions: [IssueFieldTimelineOption!] + """ The previous value of the field. """ @@ -21544,6 +21559,11 @@ type IssueFieldRemovedEvent implements Node @docsCategory(name: "issues") { The issue field removed. """ issueField: IssueFields + + """ + The removed options for option-backed fields; single-select returns one option and multi-select returns many. + """ + options: [IssueFieldTimelineOption!] } """ @@ -21801,6 +21821,21 @@ type IssueFieldTextValue implements IssueFieldValueCommon & Node @docsCategory(n value: String! } +""" +Represents a selected option for a timeline issue field event. +""" +type IssueFieldTimelineOption @docsCategory(name: "issues") { + """ + The option color. + """ + color: String + + """ + The option name. + """ + name: String! +} + """ Issue field values """ @@ -26547,9 +26582,9 @@ type Mutation @docsCategory(name: "meta") { """ Add users to the pull request creation cap bypass list. Bypassed users can - create pull requests regardless of the configured cap. Only repository admins - can manage the bypass list. You can add a maximum of 100 users per request. - The bypass list can only hold a maximum of 100 users. + create pull requests regardless of the configured cap. Only users with + maintainer permissions can manage the bypass list. You can add a maximum of + 100 users per request. The bypass list can only hold a maximum of 100 users. """ addPullRequestCreationCapBypassUsers( """ @@ -28000,7 +28035,8 @@ type Mutation @docsCategory(name: "meta") { ): RemoveOutsideCollaboratorPayload @docsCategory(name: "orgs") """ - Remove users from the pull request creation cap bypass list. Only repository admins can manage the bypass list. + Remove users from the pull request creation cap bypass list. Only users with + maintainer permissions can manage the bypass list. """ removePullRequestCreationCapBypassUsers( """ @@ -53779,7 +53815,8 @@ type Repository implements Node & PackageOwner & ProjectOwner & ProjectV2Recent ): PullRequest """ - A list of users who are exempt from the pull request creation cap on this repository. Only visible to repository admins. + The pull request creation cap configuration for this repository. Only visible + to repository maintainers and administrators. """ pullRequestCreationCapConfig: PullRequestCreationCapConfig @@ -56066,6 +56103,11 @@ enum RepositoryRuleType { """ FILE_PATH_RESTRICTION + """ + Enforce any added or changed dependencies to comply with the organization's license policy. + """ + LICENSE_COMPLIANCE_SCANNING + """ Branch is read-only. Users cannot push to the branch. """ @@ -69395,6 +69437,11 @@ input UpdateRepositoryInput { """ homepageUrl: URI + """ + The policy controlling who can create issues in this repository. + """ + issueCreationPolicy: IssueCreationPolicy + """ The new name of the repository. """ diff --git a/src/landings/components/shared/LandingArticleGridWithFilter.tsx b/src/landings/components/shared/LandingArticleGridWithFilter.tsx index acdea3ef78fb..2e65957e5cf6 100644 --- a/src/landings/components/shared/LandingArticleGridWithFilter.tsx +++ b/src/landings/components/shared/LandingArticleGridWithFilter.tsx @@ -1,5 +1,6 @@ import React, { useState, useRef, useEffect, useMemo } from 'react' -import { TextInput, ActionMenu, ActionList, Token, Pagination } from '@primer/react' +import { TextInput, ActionMenu, ActionList, Token } from '@primer/react' +import { Pagination } from '@primer/react-brand' import { SearchIcon } from '@primer/octicons-react' import { announce } from '@primer/live-region-element' import cx from 'classnames' diff --git a/src/languages/lib/correct-translation-content.ts b/src/languages/lib/correct-translation-content.ts index 2b05a40af3f9..53774987d7ea 100644 --- a/src/languages/lib/correct-translation-content.ts +++ b/src/languages/lib/correct-translation-content.ts @@ -882,6 +882,42 @@ export function correctTranslatedContentStrings( '在{% data variables.product.prodname_dotcom_the_website %}或{% data variables.enterprise.data_residency_site %}{% endif %}上的企业{% ifversion ghec %}进行通信。', '的企业{% ifversion ghec %}在{% data variables.product.prodname_dotcom_the_website %}或{% data variables.enterprise.data_residency_site %}{% endif %}进行通信。', ) + + // [per-file] actions/azure-vnet-creating-network-configuration-prereqs.md: + // `{% ifversion ghec%}` closes prematurely with `{% endif %}` before the + // `{% else %}` branch, leaving `{% else %}` as an orphan. Reorder to: + // `{% ifversion ghec %}...{% else %}...{% endif %}`. + content = content.replaceAll( + '可以{% ifversion ghec%}在企业或组织级别{% endif %}在组织级别{% else %}创建网络配置,从而将 Azure 虚拟网络 (VNET) 用于专用网络。', + '可以{% ifversion ghec %}在企业或组织级别{% else %}在组织级别{% endif %}创建网络配置,从而将 Azure 虚拟网络 (VNET) 用于专用网络。', + ) + + // [per-file] gated-features/ghas-ghec.md: `prodname_team` and `prodname_ghe_cloud` + // escaped outside the `{% ifversion fpt or ghec %}` block, and the branches are + // swapped. `{% endif %}` appears before `{% elsif ghes %}`. Restore structure: + // `{% ifversion fpt or ghec %}...team...ghe_cloud{% elsif ghes %}...ghe_server{% endif %}`. + content = content.replaceAll( + '适用于{% data variables.product.prodname_team %}上的{% ifversion fpt or ghec %}账户以及{% data variables.product.prodname_ghe_server %}{% endif %}上的{% data variables.product.prodname_ghe_cloud %}{% elsif ghes %}账户。', + '适用于{% ifversion fpt or ghec %}{% data variables.product.prodname_team %}和{% data variables.product.prodname_ghe_cloud %}上的账户{% elsif ghes %}{% data variables.product.prodname_ghe_server %}上的账户{% endif %}。', + ) + + // [per-file] scim/after-you-configure-saml.md: `{% ifversion fpt or ghec %}` opener + // was dropped before `{% data variables.product.github %}`, leaving `{% else %}` as + // an orphan. The `{% ifversion %}` token was then misplaced after `{% endif %}`. + content = content.replaceAll( + '{% data variables.product.github %}{% else %}{% data variables.location.product_location_enterprise %}{% endif %} 上的{% ifversion fpt or ghec %}企业资源', + '{% ifversion fpt or ghec %}{% data variables.product.github %} 上的企业资源{% else %}{% data variables.location.product_location_enterprise %}{% endif %}', + ) + + // [per-file] enterprise_user_management/consider-usernames-for-external-authentication.md: + // The second `{% ifversion ghec %}` opener (before `product.github`) was dropped, + // leaving an orphan `{% elsif ghes %}` and a dangling `{% ifversion ghec %}` at end. + // `企业中` ("in your enterprise") is GHEC-only in the source, so it belongs inside the + // `{% ifversion ghec %}` branch, not before it. + content = content.replaceAll( + '企业中 {% data variables.product.github %}{% elsif ghes %} 上 {% data variables.location.product_location %}{% endif %} 上每个新个人帐户 {% ifversion ghec %} 的用户名。', + '{% ifversion ghec %}企业中 {% data variables.product.github %}{% elsif ghes %} 上 {% data variables.location.product_location %}{% endif %} 上每个新个人帐户的用户名。', + ) } if (context.code === 'ru') { @@ -1104,6 +1140,14 @@ export function correctTranslatedContentStrings( return match.replace(/(\d)\s*о/g, '$10').replace(/о\s*(\d)/g, '0$1') }) + // `{% PLAN PLAN ifversion %}` — plan name duplicated before `ifversion`; word-order swap. + // The universal whitespace fix converts `{ % ghes ghes ifversion %}` to this form first. + // Collapse the duplicate plan name and swap to canonical `{% ifversion PLAN %}`. + content = content.replace( + /\{%(-?)\s*(fpt|ghec|ghes|ghae|ghecom)\s+\2\s+ifversion\s*(-?)%\}/g, + '{%$1 ifversion $2 $3%}', + ) + // Word-order swap: translator placed plan name BEFORE `ifversion`, e.g. // `{% ghes ifversion %}` → `{% ifversion ghes %}`, // `{% ghes ifversion < 3,14 %}` → `{% ifversion ghes < 3.14 %}` @@ -1207,6 +1251,19 @@ export function correctTranslatedContentStrings( '{% ifversion ghec %}аутентификации и{% endif %} провизионирования SCIM{% else %}с помощью Okta', '{% ifversion ghec %}SCIM{% else %}аутентификации и{% endif %} провизионирования с помощью Okta', ) + + // [SCRAPE-6732] admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/viewing-and-managing-a-users-saml-access-to-your-enterprise.md + // (intro): translator scrambled `{% ifversion ghec %}...{% else %}...{% endif %}` + // so the `{% else %}` ended up before any opening `{% ifversion %}` (an orphan) + // and the `{% ifversion ghec %}` moved into the else branch. This breaks the + // admin landing page render (`tag "else" not found`). Reconstruct to match + // English: view and revoke an enterprise member's {% ifversion ghec %}linked + // identity, active sessions, and authorized credentials{% else %}active SAML + // sessions{% endif %}. The corrector runs on the PARSED intro value. + content = content.replaceAll( + 'связанную личность, активные сессии и авторизованные учетные{% else %}данные {% ifversion ghec %}SAML{% endif %}', + '{% ifversion ghec %}связанную личность, активные сессии и авторизованные учетные данные{% else %}активные сессии SAML{% endif %}', + ) } if (context.code === 'fr') { @@ -1777,6 +1834,13 @@ export function correctTranslatedContentStrings( 'auf selbst-gehosteten Runnern ausführen.{% data variables.product.prodname_dependabot %}', 'auf selbst-gehosteten Runnern ausführen.{% endif %}', ) + // [per-file] enterprise_installation/hardware-considerations-all-platforms.md: + // `{% ifversion ghes %}` opener was stripped before "200 GB", leaving `{% else %}` + // as an orphan. Restore the opener immediately before the "200 GB" text. + content = content.replaceAll( + 'werden 200 GB auf dem Stammdateisystem verfügbar sein. Die verbleibenden 200GB{% else %}', + 'werden {% ifversion ghes %}200 GB auf dem Stammdateisystem verfügbar sein. Die verbleibenden 200GB{% else %}', + ) } // --- Generic fixes (all languages) --- diff --git a/src/languages/tests/correct-translation-content.ts b/src/languages/tests/correct-translation-content.ts index 0295d2980da9..acd373748f98 100644 --- a/src/languages/tests/correct-translation-content.ts +++ b/src/languages/tests/correct-translation-content.ts @@ -577,6 +577,76 @@ describe('correctTranslatedContentStrings', () => { expect(fix('{%- 捕获 myvar -%}', 'zh')).toBe('{%- capture myvar -%}') expect(fix('{%- 捕获myvar %}', 'zh')).toBe('{%- capture myvar %}') }) + + test('[per-file] azure-vnet: premature endif leaves orphan else', () => { + function fixAt(content: string, code: string, relativePath: string) { + return correctTranslatedContentStrings(content, '', { + code, + relativePath, + skipOrphanStripping: true, + }) + } + const path = 'data/reusables/actions/azure-vnet-creating-network-configuration-prereqs.md' + const broken = + '可以{% ifversion ghec%}在企业或组织级别{% endif %}在组织级别{% else %}创建网络配置,从而将 Azure 虚拟网络 (VNET) 用于专用网络。' + const fixed = + '可以{% ifversion ghec %}在企业或组织级别{% else %}在组织级别{% endif %}创建网络配置,从而将 Azure 虚拟网络 (VNET) 用于专用网络。' + expect(fixAt(broken, 'zh', path)).toBe(fixed) + // unchanged if already correct + expect(fixAt(fixed, 'zh', path)).toBe(fixed) + }) + + test('[per-file] ghas-ghec: scrambled ifversion with stray elsif after endif', () => { + function fixAt(content: string, code: string, relativePath: string) { + return correctTranslatedContentStrings(content, '', { + code, + relativePath, + skipOrphanStripping: true, + }) + } + const path = 'data/reusables/gated-features/ghas-ghec.md' + const broken = + '适用于{% data variables.product.prodname_team %}上的{% ifversion fpt or ghec %}账户以及{% data variables.product.prodname_ghe_server %}{% endif %}上的{% data variables.product.prodname_ghe_cloud %}{% elsif ghes %}账户。' + const fixed = + '适用于{% ifversion fpt or ghec %}{% data variables.product.prodname_team %}和{% data variables.product.prodname_ghe_cloud %}上的账户{% elsif ghes %}{% data variables.product.prodname_ghe_server %}上的账户{% endif %}。' + expect(fixAt(broken, 'zh', path)).toBe(fixed) + expect(fixAt(fixed, 'zh', path)).toBe(fixed) + }) + + test('[per-file] scim/after-you-configure-saml: ifversion opener dropped leaving orphan else', () => { + function fixAt(content: string, code: string, relativePath: string) { + return correctTranslatedContentStrings(content, '', { + code, + relativePath, + skipOrphanStripping: true, + }) + } + const path = 'data/reusables/scim/after-you-configure-saml.md' + const broken = + '{% data variables.product.github %}{% else %}{% data variables.location.product_location_enterprise %}{% endif %} 上的{% ifversion fpt or ghec %}企业资源' + const fixed = + '{% ifversion fpt or ghec %}{% data variables.product.github %} 上的企业资源{% else %}{% data variables.location.product_location_enterprise %}{% endif %}' + expect(fixAt(broken, 'zh', path)).toBe(fixed) + expect(fixAt(fixed, 'zh', path)).toBe(fixed) + }) + + test('[per-file] consider-usernames: ifversion ghec opener dropped leaving orphan elsif', () => { + function fixAt(content: string, code: string, relativePath: string) { + return correctTranslatedContentStrings(content, '', { + code, + relativePath, + skipOrphanStripping: true, + }) + } + const path = + 'data/reusables/enterprise_user_management/consider-usernames-for-external-authentication.md' + const broken = + '企业中 {% data variables.product.github %}{% elsif ghes %} 上 {% data variables.location.product_location %}{% endif %} 上每个新个人帐户 {% ifversion ghec %} 的用户名。' + const fixed = + '{% ifversion ghec %}企业中 {% data variables.product.github %}{% elsif ghes %} 上 {% data variables.location.product_location %}{% endif %} 上每个新个人帐户的用户名。' + expect(fixAt(broken, 'zh', path)).toBe(fixed) + expect(fixAt(fixed, 'zh', path)).toBe(fixed) + }) }) // ─── RUSSIAN (ru) ────────────────────────────────────────────────── @@ -836,6 +906,17 @@ describe('correctTranslatedContentStrings', () => { '{% ifversion enterprise-licensing-language %}licenses{% else %}licensed seats{% endif %}', ) }) + + test('fixes doubled plan name before ifversion (ghes ghes ifversion → ifversion ghes)', () => { + // `{% ghes ghes ifversion %}` — plan name appears twice before `ifversion`; + // collapses the duplicate and swaps to canonical `{% ifversion PLAN %}`. + expect(fix('{% ghes ghes ifversion %}', 'ru')).toBe('{% ifversion ghes %}') + expect(fix('{%- ghec ghec ifversion %}', 'ru')).toBe('{%- ifversion ghec %}') + // Does not affect normal word-order swap (single plan name) + expect(fix('{% ghes ifversion %}', 'ru')).toBe('{% ifversion ghes %}') + // Unchanged when already correct + expect(fix('{% ifversion ghes %}', 'ru')).toBe('{% ifversion ghes %}') + }) }) // ─── FRENCH (fr) ─────────────────────────────────────────────────── @@ -1399,6 +1480,24 @@ describe('correctTranslatedContentStrings', () => { '{%- data variables.product.github %}', ) }) + + test('[per-file] hardware-considerations-all-platforms: ifversion ghes opener stripped', () => { + function fixAt(content: string, code: string, relativePath: string) { + return correctTranslatedContentStrings(content, '', { + code, + relativePath, + skipOrphanStripping: true, + }) + } + const path = 'data/reusables/enterprise_installation/hardware-considerations-all-platforms.md' + const broken = + 'werden 200 GB auf dem Stammdateisystem verfügbar sein. Die verbleibenden 200GB{% else %}100GB sind auf dem Stammdateisystem verfügbar.' + const fixed = + 'werden {% ifversion ghes %}200 GB auf dem Stammdateisystem verfügbar sein. Die verbleibenden 200GB{% else %}100GB sind auf dem Stammdateisystem verfügbar.' + expect(fixAt(broken, 'de', path)).toBe(fixed) + // unchanged if already correct + expect(fixAt(fixed, 'de', path)).toBe(fixed) + }) }) describe('Generic fixes (all languages)', () => { @@ -2338,4 +2437,21 @@ Para más información, consulta "[AUTOTITLE](/path)". expect(fix(broken, 'de')).toBe('auf selbst-gehosteten Runnern ausführen.{% endif %}') }) }) + + // ─── SCRAPE-6732: search-scrape failures ───────────────────────────── + // The ru admin landing page failed to scrape with `tag "else" not found` + // because the intro of viewing-and-managing-a-users-saml-access-to-your-enterprise.md + // had an orphaned `{% else %}` before any opening `{% ifversion %}` + // (github/docs-engineering#6732). The corrector runs on the PARSED intro value. + describe('SCRAPE-6732 per-file fixes', () => { + test('ru: viewing-and-managing-a-users-saml-access intro reorders orphaned else', () => { + const broken = + 'Вы можете просматривать и отзывать связанную личность, активные сессии и авторизованные учетные{% else %}данные {% ifversion ghec %}SAML{% endif %} участника предприятия.' + const fixed = + 'Вы можете просматривать и отзывать {% ifversion ghec %}связанную личность, активные сессии и авторизованные учетные данные{% else %}активные сессии SAML{% endif %} участника предприятия.' + expect(fix(broken, 'ru')).toBe(fixed) + // idempotent: the fix only matches the broken form + expect(fix(fixed, 'ru')).toBe(fixed) + }) + }) }) diff --git a/src/links/lib/extract-links.ts b/src/links/lib/extract-links.ts index 2cf850669b12..adc468a18793 100644 --- a/src/links/lib/extract-links.ts +++ b/src/links/lib/extract-links.ts @@ -12,6 +12,8 @@ import { createLogger } from '@/observability/logger' import { allVersions } from '@/versions/lib/all-versions' import { latestStable } from '@/versions/lib/enterprise-server-releases' import { getDataByLanguage } from '@/data-directory/lib/get-data' +import getRedirect from '@/redirects/lib/get-redirect' +import { isArchivedVersionByPath } from '@/archives/lib/is-archived-version' import type { Context, Page } from '@/types' const logger = createLogger(import.meta.url) @@ -447,8 +449,10 @@ export function checkInternalLink( } } - // Strip language prefix and check redirects (which are stored without it) - const langPrefixMatch = resolved.match(/^\/[a-z]{2}\//) + // Strip language prefix and check redirects (which are stored without it). + // Match hyphenated locales too (e.g. /pt-br/, /zh-cn/) so we don't later + // double-prefix them with /en. + const langPrefixMatch = resolved.match(/^\/[a-z]{2}(-[a-z]{2})?\//) if (langPrefixMatch) { const withoutLang = resolved.slice(langPrefixMatch[0].length - 1) if (redirects[withoutLang]) { @@ -460,6 +464,49 @@ export function checkInternalLink( } } + // The path in language-prefixed form, used by the runtime resolvers below. + // Avoid double-prefixing when the link already carried a language code. + const withEn = langPrefixMatch ? resolved : withLang + + // Links into deprecated/archived Enterprise Server versions (e.g. + // /enterprise-server@3.7/... or the legacy /enterprise/2.1/... format) are + // served by the archived enterprise versions system, which isn't loaded into + // pageMap. They resolve fine at runtime, so treat them as valid rather than + // broken. + if (isArchivedVersionByPath(withEn).isArchived) { + return { exists: true, isRedirect: false } + } + + // Fall back to the runtime redirect resolver. It handles algorithmic + // corrections (version-prefix normalization, /admin, /desktop/guides, etc.) + // that the flat redirects map doesn't contain as literal keys. This mirrors + // what the production server actually does, so a link that redirects in + // production is reported as a redirect here instead of a false broken link. + try { + // Only redirects, userLanguage, and pages are read by getRedirect (and the + // resolvers it delegates to), so type the object to those fields rather than + // casting an arbitrary shape to the full Context. + const context: Pick = { + redirects, + userLanguage: 'en', + pages: pageMap, + } + const redirect = getRedirect(withEn, context as unknown as Context) + if (redirect) { + // getRedirect returns a language-prefixed path (e.g. /en/...); strip any + // locale prefix to match the format used by the flat-map branches above, + // and normalize a bare language root (e.g. /en) to /. + return { + exists: true, + isRedirect: true, + redirectTarget: redirect.replace(/^\/[a-z]{2}(-[a-z]{2})?(?=\/|$)/, '') || '/', + } + } + } catch { + // getRedirect throws on a few fully-deprecated shapes (e.g. github-ae). + // Treat those as unresolvable rather than crashing the whole check. + } + return { exists: false, isRedirect: false } } diff --git a/src/links/tests/extract-links.ts b/src/links/tests/extract-links.ts index d660763f287e..7e2858be6e80 100644 --- a/src/links/tests/extract-links.ts +++ b/src/links/tests/extract-links.ts @@ -434,6 +434,63 @@ describe('checkInternalLink', () => { expect(result.isRedirect).toBe(true) expect(result.redirectTarget).toBe('/actions/current-path') }) + + test('treats archived Enterprise Server versions as valid', () => { + // Deprecated GHES versions are served by the archived enterprise versions + // system, which isn't loaded into pageMap. They must not be reported broken. + const result = checkInternalLink( + '/enterprise-server@3.7/admin/release-notes', + pageMap, + redirects, + ) + expect(result.exists).toBe(true) + expect(result.isRedirect).toBe(false) + }) + + test('treats legacy /enterprise// archived paths as valid', () => { + const result = checkInternalLink( + '/enterprise/2.1/admin/guides/installation/provisioning-and-installation/', + pageMap, + redirects, + ) + expect(result.exists).toBe(true) + expect(result.isRedirect).toBe(false) + }) + + test('resolves free-pro-team@latest prefixed links via the redirect resolver', () => { + // The flat redirects map has no literal key for this; getRedirect computes + // the correction (strip the version prefix) the same way production does. + const result = checkInternalLink('/free-pro-team@latest/actions/guides', pageMap, redirects) + expect(result.exists).toBe(true) + expect(result.isRedirect).toBe(true) + expect(result.redirectTarget).toBe('/actions/guides') + }) + + test('resolves versionless /enterprise-server/ links via the redirect resolver', () => { + const result = checkInternalLink(`/enterprise-server/admin/overview`, pageMap, redirects) + expect(result.exists).toBe(true) + expect(result.isRedirect).toBe(true) + // Normalized to the latest stable Enterprise Server version. + expect(result.redirectTarget).toBe(`/enterprise-server@${latestStable}/admin/overview`) + }) + + test('strips hyphenated locale prefixes without double-prefixing', () => { + // /pt-br/ is a hyphenated locale; it must be stripped (not turned into + // /en/pt-br/...) so the underlying path resolves against the redirects map. + const result = checkInternalLink('/pt-br/actions/legacy-path', pageMap, redirects) + expect(result.exists).toBe(true) + expect(result.isRedirect).toBe(true) + expect(result.redirectTarget).toBe('/actions/current-path') + }) + + test('normalizes a bare language-root redirect target to /', () => { + // getRedirect collapses '/free-pro-team@latest' to the language root ('/en'); + // after stripping the locale that would be empty, so it must normalize to '/'. + const result = checkInternalLink('/free-pro-team@latest', pageMap, redirects) + expect(result.exists).toBe(true) + expect(result.isRedirect).toBe(true) + expect(result.redirectTarget).toBe('/') + }) }) describe('isAssetLink', () => { diff --git a/src/search/components/results/SearchResults.tsx b/src/search/components/results/SearchResults.tsx index 4e8e2803cf2a..cbcfc8f07332 100644 --- a/src/search/components/results/SearchResults.tsx +++ b/src/search/components/results/SearchResults.tsx @@ -1,4 +1,4 @@ -import { Pagination } from '@primer/react' +import { Pagination } from '@primer/react-brand' import { SearchIcon } from '@primer/octicons-react' import { useRouter } from 'next/router' import React, { useEffect, useRef, useState } from 'react' diff --git a/src/secret-scanning/components/SecretScanningTable.tsx b/src/secret-scanning/components/SecretScanningTable.tsx index b8818e93ad91..6395fcb3c6e6 100644 --- a/src/secret-scanning/components/SecretScanningTable.tsx +++ b/src/secret-scanning/components/SecretScanningTable.tsx @@ -1,6 +1,7 @@ import React, { useState, useMemo, useEffect, useRef, useCallback } from 'react' import { DataTable, Table } from '@primer/react/experimental' -import { TextInput, ActionMenu, ActionList, Pagination, Button } from '@primer/react' +import { TextInput, ActionMenu, ActionList, Button } from '@primer/react' +import { Pagination } from '@primer/react-brand' import debounce from 'lodash/debounce' import { useTranslation } from '@/languages/components/useTranslation' import { sendEvent } from '@/events/components/events' 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 75f7acf17aa8..dbf024ef4068 100644 --- a/src/secret-scanning/data/pattern-docs/fpt/public-docs.yml +++ b/src/secret-scanning/data/pattern-docs/fpt/public-docs.yml @@ -297,7 +297,7 @@ isPrivateWithGhas: true hasPushProtection: false hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Asana @@ -307,7 +307,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: true - provider: Atlassian @@ -1157,7 +1157,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: true isduplicate: true - provider: Brevo @@ -1377,7 +1377,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: CircleCI @@ -1777,7 +1777,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: DigitalOcean @@ -1787,7 +1787,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: DigitalOcean @@ -1867,7 +1867,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Doppler @@ -1877,7 +1877,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Doppler @@ -1887,7 +1887,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Doppler @@ -1897,7 +1897,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Doppler @@ -1907,7 +1907,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Doppler @@ -1917,7 +1917,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Dropbox @@ -2047,7 +2047,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Fastly @@ -2387,7 +2387,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Google @@ -3251,7 +3251,7 @@ isPrivateWithGhas: true hasPushProtection: false hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: MongoDB @@ -3581,7 +3581,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: OpenVSX @@ -4291,7 +4291,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Sentry @@ -4471,7 +4471,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Slack 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 75f7acf17aa8..dbf024ef4068 100644 --- a/src/secret-scanning/data/pattern-docs/ghec/public-docs.yml +++ b/src/secret-scanning/data/pattern-docs/ghec/public-docs.yml @@ -297,7 +297,7 @@ isPrivateWithGhas: true hasPushProtection: false hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Asana @@ -307,7 +307,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: true - provider: Atlassian @@ -1157,7 +1157,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: true isduplicate: true - provider: Brevo @@ -1377,7 +1377,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: CircleCI @@ -1777,7 +1777,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: DigitalOcean @@ -1787,7 +1787,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: DigitalOcean @@ -1867,7 +1867,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Doppler @@ -1877,7 +1877,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Doppler @@ -1887,7 +1887,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Doppler @@ -1897,7 +1897,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Doppler @@ -1907,7 +1907,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Doppler @@ -1917,7 +1917,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Dropbox @@ -2047,7 +2047,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Fastly @@ -2387,7 +2387,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Google @@ -3251,7 +3251,7 @@ isPrivateWithGhas: true hasPushProtection: false hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: MongoDB @@ -3581,7 +3581,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: OpenVSX @@ -4291,7 +4291,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Sentry @@ -4471,7 +4471,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: false - provider: Slack