diff --git a/.github/actions/retry-command/action.yml b/.github/actions/retry-command/action.yml index 5b70e2a3d627..6bbf45f9797e 100644 --- a/.github/actions/retry-command/action.yml +++ b/.github/actions/retry-command/action.yml @@ -18,13 +18,17 @@ runs: steps: - name: Retry command shell: bash + env: + INPUT_MAX_ATTEMPTS: ${{ inputs.max_attempts }} + INPUT_DELAY: ${{ inputs.delay }} + INPUT_COMMAND: ${{ inputs.command }} run: | # Generic retry function: configurable attempts and delay retry_command() { - local max_attempts=${{ inputs.max_attempts }} - local delay=${{ inputs.delay }} + local max_attempts=${INPUT_MAX_ATTEMPTS} + local delay=${INPUT_DELAY} local attempt=1 - local command="${{ inputs.command }}" + local command="${INPUT_COMMAND}" while [ $attempt -le $max_attempts ]; do echo "Attempt $attempt/$max_attempts: Running command..." diff --git a/.github/actions/setup-elasticsearch/action.yml b/.github/actions/setup-elasticsearch/action.yml index 60d3fb07216e..813e46bc11d8 100644 --- a/.github/actions/setup-elasticsearch/action.yml +++ b/.github/actions/setup-elasticsearch/action.yml @@ -34,14 +34,18 @@ runs: - name: Pull Docker image shell: bash if: steps.cache-docker-layers.outputs.cache-hit != 'true' - run: docker pull elasticsearch:${{ inputs.elasticsearch_version }} + env: + ES_VERSION: ${{ inputs.elasticsearch_version }} + run: docker pull elasticsearch:${ES_VERSION} - name: Save Docker image to cache shell: bash if: steps.cache-docker-layers.outputs.cache-hit != 'true' + env: + ES_VERSION: ${{ inputs.elasticsearch_version }} run: | mkdir -p /tmp/docker-cache - docker save -o /tmp/docker-cache/elasticsearch.tar elasticsearch:${{ inputs.elasticsearch_version }} + docker save -o /tmp/docker-cache/elasticsearch.tar elasticsearch:${ES_VERSION} # Setups the Elasticsearch container # Derived from https://github.com/getong/elasticsearch-action diff --git a/.github/workflows/auto-close-dependencies.yml b/.github/workflows/auto-close-dependencies.yml index 8ed6848c3645..280ef8f07f79 100644 --- a/.github/workflows/auto-close-dependencies.yml +++ b/.github/workflows/auto-close-dependencies.yml @@ -19,7 +19,7 @@ on: - submitted permissions: - contents: read + contents: write pull-requests: write jobs: @@ -34,12 +34,12 @@ jobs: }} runs-on: ubuntu-latest steps: - - name: Close pull request + - name: Close pull request and delete branch env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_URL: ${{ github.event.pull_request.html_url }} run: | - gh pr close "$PR_URL" + gh pr close "$PR_URL" --delete-branch - name: Comment on the pull request env: diff --git a/.github/workflows/close-on-invalid-label.yaml b/.github/workflows/close-on-invalid-label.yaml index cf68f22a396b..ec54378401d8 100644 --- a/.github/workflows/close-on-invalid-label.yaml +++ b/.github/workflows/close-on-invalid-label.yaml @@ -27,13 +27,15 @@ jobs: if: ${{ github.event_name == 'issues' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh issue close ${{ github.event.issue.html_url }} + ISSUE_URL: ${{ github.event.issue.html_url }} + run: gh issue close "$ISSUE_URL" - name: Close PR if: ${{ github.event_name == 'pull_request_target' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh pr close ${{ github.event.pull_request.html_url }} + PR_URL: ${{ github.event.pull_request.html_url }} + run: gh pr close "$PR_URL" - name: Check out repo if: ${{ failure() && github.event_name != 'pull_request_target' }} diff --git a/.github/workflows/content-pipelines.yml b/.github/workflows/content-pipelines.yml index ef723035be15..c0b82400916b 100644 --- a/.github/workflows/content-pipelines.yml +++ b/.github/workflows/content-pipelines.yml @@ -22,7 +22,6 @@ on: permissions: contents: write pull-requests: write - copilot-requests: write env: HUSKY: 0 @@ -71,11 +70,11 @@ jobs: git config user.email "github-actions[bot]@users.noreply.github.com" if git ls-remote --exit-code --heads origin "$UPDATE_BRANCH" > /dev/null 2>&1; then - git fetch origin "$UPDATE_BRANCH" + git fetch origin "$UPDATE_BRANCH" main git checkout "$UPDATE_BRANCH" git merge origin/main --no-edit || { echo "Merge conflict with main — resetting branch to main" - git merge --abort + git merge --abort 2>/dev/null || true git checkout main git branch -D "$UPDATE_BRANCH" git push origin --delete "$UPDATE_BRANCH" || true @@ -89,6 +88,7 @@ jobs: env: GH_TOKEN: ${{ secrets.DOCS_BOT_PAT_BASE }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COPILOT_GITHUB_TOKEN: ${{ secrets.DOCS_BOT_PAT_COPILOT }} run: npx tsx src/content-pipelines/scripts/update.ts --id "${{ matrix.id }}" - name: Commit changes @@ -131,6 +131,7 @@ jobs: PR_BODY+="Runs the \`content-pipeline-update\` agent (${PIPELINE_ID}) against the latest source docs and updates official articles under \`content/\` that have fallen out of sync."$'\n\n' PR_BODY+="## Review"$'\n\n' PR_BODY+="* Review each commit for accuracy — the agent uses AI, so spot-check important changes"$'\n' + PR_BODY+="* To adjust agent behavior, see [Modifying results](${{ github.server_url }}/${{ github.repository }}/blob/main/src/content-pipelines/README.md#modifying-results)"$'\n' PR_BODY+="* Once satisfied, merge to keep docs up to date"$'\n' PR_BODY+="* A new PR will be created on the next run if there are further changes" diff --git a/.github/workflows/generate-code-scanning-query-lists.yml b/.github/workflows/generate-code-scanning-query-lists.yml index b3309211e0ae..2b965f65dc25 100644 --- a/.github/workflows/generate-code-scanning-query-lists.yml +++ b/.github/workflows/generate-code-scanning-query-lists.yml @@ -24,8 +24,7 @@ on: - .github/actions/install-cocofix/action.yml permissions: - contents: write - pull-requests: write + contents: read jobs: generate-security-query-lists: @@ -159,6 +158,9 @@ jobs: create-pull-request: if: github.repository == 'github/docs-internal' runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write needs: [generate-security-query-lists, generate-quality-query-lists] steps: - name: Checkout repository code diff --git a/.github/workflows/link-check-internal.yml b/.github/workflows/link-check-internal.yml index 369681f638ea..89bc0d207f67 100644 --- a/.github/workflows/link-check-internal.yml +++ b/.github/workflows/link-check-internal.yml @@ -17,7 +17,6 @@ on: permissions: contents: read - issues: write jobs: # Determine which version/language combos to run @@ -35,14 +34,18 @@ jobs: - name: Set matrix id: set-matrix run: | - if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + if [[ "${EVENT_NAME}" == "workflow_dispatch" ]]; then # Manual run: use the provided version and language - echo 'matrix={"include":[{"version":"${{ inputs.version }}","language":"${{ inputs.language }}"}]}' >> $GITHUB_OUTPUT + echo "matrix={\"include\":[{\"version\":\"${INPUT_VERSION}\",\"language\":\"${INPUT_LANGUAGE}\"}]}" >> $GITHUB_OUTPUT else # Scheduled run: English free-pro-team + English latest enterprise-server LATEST_GHES=$(npx tsx -e "import { latest } from './src/versions/lib/enterprise-server-releases'; console.log(latest)") echo "matrix={\"include\":[{\"version\":\"free-pro-team@latest\",\"language\":\"en\"},{\"version\":\"enterprise-server@${LATEST_GHES}\",\"language\":\"en\"}]}" >> $GITHUB_OUTPUT fi + env: + EVENT_NAME: ${{ github.event_name }} + INPUT_VERSION: ${{ inputs.version }} + INPUT_LANGUAGE: ${{ inputs.language }} - uses: ./.github/actions/slack-alert if: ${{ failure() && github.event_name != 'workflow_dispatch' }} @@ -104,6 +107,8 @@ jobs: if: always() && github.repository == 'github/docs-internal' needs: [setup-matrix, check-internal-links] runs-on: ubuntu-latest + permissions: + issues: write steps: - name: Checkout uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 diff --git a/.github/workflows/needs-sme-workflow.yml b/.github/workflows/needs-sme-workflow.yml index 82a9d32c1ecb..284ece93107a 100644 --- a/.github/workflows/needs-sme-workflow.yml +++ b/.github/workflows/needs-sme-workflow.yml @@ -13,13 +13,13 @@ on: permissions: contents: read - issues: write - pull-requests: write jobs: add-issue-comment: if: ${{ github.repository == 'github/docs' && (github.event.label.name == 'needs SME' && github.event_name == 'issues') }} runs-on: ubuntu-latest + permissions: + issues: write steps: - name: Check out repo uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 @@ -39,6 +39,8 @@ jobs: add-pr-comment: if: ${{ github.repository == 'github/docs' && (github.event.label.name == 'needs SME' && github.event_name == 'pull_request_target') }} runs-on: ubuntu-latest + permissions: + pull-requests: write steps: - name: Check out repo uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 diff --git a/.github/workflows/ready-for-doc-review.yml b/.github/workflows/ready-for-doc-review.yml index 152490b8b801..534402a63740 100644 --- a/.github/workflows/ready-for-doc-review.yml +++ b/.github/workflows/ready-for-doc-review.yml @@ -37,11 +37,15 @@ jobs: - name: Set AUTHOR_LOGIN run: | - if [[ "${{ github.event.pull_request.assignee.login && github.event.pull_request.user.login == 'docs-bot' }}" ]]; then - echo "AUTHOR_LOGIN=${{ github.event.pull_request.assignee.login }}" >> $GITHUB_ENV + if [[ "${IS_DOCS_BOT_ASSIGNEE}" == "true" ]]; then + echo "AUTHOR_LOGIN=${ASSIGNEE_LOGIN}" >> $GITHUB_ENV else - echo "AUTHOR_LOGIN=${{ github.event.pull_request.user.login }}" >> $GITHUB_ENV + echo "AUTHOR_LOGIN=${USER_LOGIN}" >> $GITHUB_ENV fi + env: + IS_DOCS_BOT_ASSIGNEE: ${{ github.event.pull_request.assignee.login && github.event.pull_request.user.login == 'docs-bot' }} + ASSIGNEE_LOGIN: ${{ github.event.pull_request.assignee.login }} + USER_LOGIN: ${{ github.event.pull_request.user.login }} - name: Run script run: | diff --git a/.github/workflows/sync-graphql.yml b/.github/workflows/sync-graphql.yml index b2992859f7d6..57b8f6e54998 100644 --- a/.github/workflows/sync-graphql.yml +++ b/.github/workflows/sync-graphql.yml @@ -10,13 +10,15 @@ on: - cron: '20 16 * * 1-5' # Run Mon-Fri at 16:20 UTC / 8:20 PST permissions: - contents: write - pull-requests: write + contents: read jobs: update_graphql_files: if: github.repository == 'github/docs-internal' runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write outputs: ignored-changes: ${{ steps.sync.outputs.ignored-changes }} ignored-count: ${{ steps.sync.outputs.ignored-count }} diff --git a/.github/workflows/triage-stale-check.yml b/.github/workflows/triage-stale-check.yml index 58f71fe8e68b..e154f9132bbe 100644 --- a/.github/workflows/triage-stale-check.yml +++ b/.github/workflows/triage-stale-check.yml @@ -10,14 +10,15 @@ on: permissions: contents: read - issues: write - pull-requests: write jobs: stale_contributor: name: Identify and close stale issues and PRs if: github.repository == 'github/docs' runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write steps: - uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 @@ -55,6 +56,9 @@ jobs: name: Remind staff about PRs waiting for review if: github.repository == 'github/docs' runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write steps: - uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 with: diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 000000000000..63a7e6ffe6f8 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,33 @@ +name: Workflow security lint + +# **What it does**: Runs zizmor to detect security issues in GitHub Actions workflows. +# **Why we have it**: To catch injection vulnerabilities and other security misconfigurations before they ship. +# **Who does it impact**: Docs engineering. + +on: + pull_request: + paths: + - '.github/workflows/**' + - '.github/actions/**' + - '.github/zizmor.yml' + +permissions: + contents: read + +jobs: + zizmor: + if: github.repository == 'github/docs-internal' + name: zizmor + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - uses: zizmorcore/zizmor-action@0dce2577a4760a2749d8cfb7a84b7d5585ebcb7d # v0.5.0 + with: + online-audits: 'false' + advanced-security: 'false' + annotations: 'true' + min-severity: 'high' diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 000000000000..95639824c31c --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,22 @@ +rules: + # pull_request_target is required for workflows that need write access + # on PRs from forks (e.g. labeling, commenting). We audit these manually. + dangerous-triggers: + disable: true + + # moda-ci uses reusable workflows (uses:) which don't support job-level + # permissions. id-token:write and attestations:write are needed by docker-image + # for attestation but can't be scoped to that job alone. + excessive-permissions: + ignore: + - moda-ci.yaml + + # actions/* has immutable tags, so ref-pinning is sufficient. + # github/internal-actions is a private GitHub org repo, ref-pin is fine. + # Everything else must be hash-pinned. + unpinned-uses: + config: + policies: + 'actions/*': ref-pin + 'github/internal-actions/*': ref-pin + '*': hash-pin diff --git a/config/moda/secrets/production/secrets.yml b/config/moda/secrets/production/secrets.yml index ccdb3af5732d..20b7ce0d3c0a 100644 --- a/config/moda/secrets/production/secrets.yml +++ b/config/moda/secrets/production/secrets.yml @@ -1,8 +1,66 @@ ---- secrets: COOKIE_SECRET: kind: latest_at_deployment_start - key: COOKIE_SECRET type: salt owner: '@github/docs-engineering' externally_usable: true + CSE_COPILOT_ENDPOINT: + kind: latest_at_deployment_start + type: salt + owner: '@github/docs-engineering' + externally_usable: true + CSE_COPILOT_SECRET: + kind: latest_at_deployment_start + type: salt + owner: '@github/docs-engineering' + externally_usable: true + DOCS_BOT_PAT_BASE: + kind: latest_at_deployment_start + type: github_pat + owner: '@github/docs-engineering' + externally_usable: true + DOCS_BOT_PAT_COPILOT: + kind: latest_at_deployment_start + type: github_pat + owner: '@github/docs-engineering' + externally_usable: true + DOCS_BOT_PAT_REPO_SYNC: + kind: latest_at_deployment_start + type: github_pat + owner: '@github/docs-engineering' + externally_usable: true + ELASTICSEARCH_URL: + kind: latest_at_deployment_start + type: elasticsearch + owner: '@github/docs-engineering' + externally_usable: true + FASTLY_API_TOKEN: + kind: latest_at_deployment_start + type: fastly + owner: '@github/docs-engineering' + externally_usable: true + FASTLY_SERVICE_ID: + kind: latest_at_deployment_start + type: fastly + owner: '@github/docs-engineering' + externally_usable: true + FASTLY_TOKEN: + kind: latest_at_deployment_start + type: fastly + owner: '@github/docs-engineering' + externally_usable: true + HAYSTACK_URL: + kind: latest_at_deployment_start + type: failbot + owner: '@github/docs-engineering' + externally_usable: true + HYDRO_ENDPOINT: + kind: latest_at_deployment_start + type: hydro_api_key + owner: '@github/docs-engineering' + externally_usable: true + HYDRO_SECRET: + kind: latest_at_deployment_start + type: hydro_api_key + owner: '@github/docs-engineering' + externally_usable: true diff --git a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/about-the-monitor-dashboards.md b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/about-the-monitor-dashboards.md index 0a73adc0c44e..1a052e679a8c 100644 --- a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/about-the-monitor-dashboards.md +++ b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/about-the-monitor-dashboards.md @@ -61,6 +61,8 @@ On this more detailed dashboard you can get further insights into all aspects of Starting with {% data variables.product.prodname_ghe_server %} 3.18, you can create custom Grafana dashboards and alerts directly inside the {% data variables.enterprise.management_console %}. These custom dashboards and alerts will persist over upgrades, making it easier to maintain your monitoring configuration. +> [!IMPORTANT] Pre-built dashboards can be opened and edited for inspection, but changes cannot be saved. Create a copy of a dashboard to apply and retain any customizations. + You can use OpenTelemetry and collectd metrics concurrently for external monitoring. During the {% data variables.release-phases.public_preview %}, collectd remains enabled and cannot be disabled. {% else %} diff --git a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/configuring-opentelemetry-for-your-instance.md b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/configuring-opentelemetry-for-your-instance.md index da5ead6627b8..1e138a8411b6 100644 --- a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/configuring-opentelemetry-for-your-instance.md +++ b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/configuring-opentelemetry-for-your-instance.md @@ -170,4 +170,5 @@ sudo journalctl -u victoriametrics -f ## Next steps * To enable advanced monitoring dashboards, see [AUTOTITLE](/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/advanced-dashboards) +* Exploring OpenTelemetry metrics, see [AUTOTITLE](/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/using-grafana-to-analyze-opentelemetry-metrics). * To set up external monitoring, see [AUTOTITLE](/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/setting-up-external-monitoring-with-opentelemetry). diff --git a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/index.md b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/index.md index 332ff735c973..3dd58e4139ed 100644 --- a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/index.md +++ b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/index.md @@ -1,6 +1,6 @@ --- title: OpenTelemetry metrics -intro: 'You can configure monitoring and alerting using OpenTelemetry so that you''re aware of potential issues before they negatively impact application performance or availability.' +intro: You can configure monitoring and alerting using OpenTelemetry so that you're aware of potential issues before they negatively impact application performance or availability. versions: feature: ghes-opentelemetry topics: @@ -9,6 +9,7 @@ children: - /about-opentelemetry-metrics - /configuring-opentelemetry-for-your-instance - /advanced-dashboards + - /using-grafana-to-analyze-opentelemetry-metrics - /setting-up-external-monitoring-with-opentelemetry shortTitle: OpenTelemetry metrics --- diff --git a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/using-grafana-to-analyze-opentelemetry-metrics.md b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/using-grafana-to-analyze-opentelemetry-metrics.md new file mode 100644 index 000000000000..1c5f8995394c --- /dev/null +++ b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/using-grafana-to-analyze-opentelemetry-metrics.md @@ -0,0 +1,75 @@ +--- +title: Using Grafana to analyze OpenTelemetry metrics +intro: Monitor the health and performance of your instance using dashboards and metrics provided by the OpenTelemetry metrics stack. +versions: + feature: ghes-opentelemetry +topics: + - Enterprise + - Fundamentals + - Infrastructure + - Monitoring + - Performance +type: how_to +shortTitle: Using Grafana to analyze metrics +contentType: other +--- + +## Prerequisites + +OpenTelemetry metrics must be enabled on the instance. For configuration instructions, see [AUTOTITLE](/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/configuring-opentelemetry-for-your-instance). + +## Navigating to the monitor dashboards + +{% data reusables.enterprise_site_admin_settings.access-settings %} +{% data reusables.enterprise_site_admin_settings.management-console %} +{% data reusables.enterprise_management_console.type-management-console-password %} +1. Click the **Monitor** tab. + + +### Metric labels + +The OpenTelemetry stack exporters automatically attach labels to metrics. Common labels are: + +* `job` — the name of the exporter that produced the metric +* `instance` — the instance name from which the metric originated + +For some exporters (like `node_exporter` or `haproxy_exporter`), open-source documentation may be available by searching for the `job` label value. + +Label availability varies by exporter and metric. Some GitHub application-level metrics may provide fewer labels than infrastructure/exporter metrics, and labels are subject to change from version to version. + + +## Inspecting panel queries + +> [!IMPORTANT] Pre-built dashboards can be opened and edited for inspection, but changes cannot be saved. Create a copy of a dashboard to apply and retain any customizations. + +To understand which metrics and queries power a given panel, use the graph panel menu *(3 vertical dots)* when viewing a Grafana dashboard: + +* **Edit**: Opens the panel editor. This is the most direct way to review and adjust the queries that power the visualization. +* **Explore**: Opens Grafana Explore with the panel’s queries pre-populated, allowing interactive analysis. +* **Metrics Drilldown**: Opens a side panel for discovering related metrics and breaking down the metric by label values. + +## Exploring metrics in Grafana + +Grafana provides multiple ways to discover and analyze the metrics available on your instance. + +### Grafana Drilldown + +Grafana Drilldown provides a guided workflow to discover metrics by filtering on metric prefixes, labels, and label values. After selecting a metric, Grafana can break down the series by available label values. For example, `haproxy_backend_response_errors_total` can be grouped by proxy/back-end label values. + +### Grafana Explore + +Grafana Explore enables interactive analysis using PromQL (Prometheus Query Language), including a query builder that can help users discover available metrics and labels. + +Tips for working in Grafana Explore: + +* Use **Code mode's Metric browser** to discover metrics and labels. Selecting labels and label values can reveal associated metrics, and selecting a metric can reveal associated labels and label values. +* After selecting a metric, switch to **Builder mode** for suggestions to refine the query. +* Access additional operations via the **+ Operations** button. +* Customize legend labels in the **Options** panel using the **Legend** setting, for example `{{}}`. By default, Grafana assigns legend labels based on unique label sets. + +## Further reading + +* [AUTOTITLE](/admin/monitoring-and-managing-your-instance/monitoring-your-instance/opentelemetry-metrics/advanced-dashboards) +* [AUTOTITLE](/admin/monitoring-and-managing-your-instance/monitoring-your-instance/about-the-monitor-dashboards#creating-new-dashboards) +* [Get Started with Explore](https://grafana.com/docs/grafana/latest/visualizations/explore/get-started-with-explore/) in the Grafana documentation +* [Promql - Querying Basics](https://prometheus.io/docs/prometheus/latest/querying/basics/) in the Prometheus documentation diff --git a/content/code-security/how-tos/report-and-fix-vulnerabilities/fix-reported-vulnerabilities/adding-a-collaborator-to-a-repository-security-advisory.md b/content/code-security/how-tos/report-and-fix-vulnerabilities/fix-reported-vulnerabilities/adding-a-collaborator-to-a-repository-security-advisory.md index b6c55c1a6f22..13c2df1e81c5 100644 --- a/content/code-security/how-tos/report-and-fix-vulnerabilities/fix-reported-vulnerabilities/adding-a-collaborator-to-a-repository-security-advisory.md +++ b/content/code-security/how-tos/report-and-fix-vulnerabilities/fix-reported-vulnerabilities/adding-a-collaborator-to-a-repository-security-advisory.md @@ -1,6 +1,6 @@ --- title: Adding a collaborator to a repository security advisory -intro: You can add other users or teams to collaborate on a security advisory with you. +intro: Add other users or teams to collaborate on a security advisory with you. permissions: '{% data reusables.permissions.security-repo-enable %}' redirect_from: - /articles/adding-a-collaborator-to-a-maintainer-security-advisory @@ -21,9 +21,7 @@ topics: shortTitle: Add collaborators --- -{% data reusables.security-advisory.repository-level-advisory-note %} - -## Adding a collaborator to a security advisory +This article applies to repository-level security advisories in a public repository. To edit a global advisory in the {% data variables.product.prodname_advisory_database %}, see [AUTOTITLE](/code-security/security-advisories/working-with-global-security-advisories-from-the-github-advisory-database/editing-security-advisories-in-the-github-advisory-database). Collaborators have write permissions to the security advisory. For more information, see [AUTOTITLE](/code-security/security-advisories/working-with-repository-security-advisories/permission-levels-for-repository-security-advisories). @@ -40,6 +38,4 @@ Collaborators have write permissions to the security advisory. For more informat ## Further reading -* [AUTOTITLE](/code-security/security-advisories/working-with-repository-security-advisories/permission-levels-for-repository-security-advisories) * [AUTOTITLE](/code-security/security-advisories/working-with-repository-security-advisories/collaborating-in-a-temporary-private-fork-to-resolve-a-repository-security-vulnerability) -* [AUTOTITLE](/code-security/security-advisories/working-with-repository-security-advisories/removing-a-collaborator-from-a-repository-security-advisory). diff --git a/content/code-security/how-tos/report-and-fix-vulnerabilities/fix-reported-vulnerabilities/managing-privately-reported-security-vulnerabilities.md b/content/code-security/how-tos/report-and-fix-vulnerabilities/fix-reported-vulnerabilities/managing-privately-reported-security-vulnerabilities.md index 031338f2123c..3b828572304c 100644 --- a/content/code-security/how-tos/report-and-fix-vulnerabilities/fix-reported-vulnerabilities/managing-privately-reported-security-vulnerabilities.md +++ b/content/code-security/how-tos/report-and-fix-vulnerabilities/fix-reported-vulnerabilities/managing-privately-reported-security-vulnerabilities.md @@ -15,16 +15,8 @@ redirect_from: - /code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/managing-privately-reported-security-vulnerabilities --- -{% data reusables.security-advisory.private-vulnerability-reporting-enable %} - -## About privately reporting a security vulnerability - -Private vulnerability reporting makes it easy for security researchers to report vulnerabilities directly to you using a simple form. - When a security researcher reports a vulnerability privately, you are notified and can choose to either accept it, ask more questions, or reject it. If you accept the report, you're ready to collaborate on a fix for the vulnerability in private with the security researcher. -## Managing security vulnerabilities that are privately reported - {% data reusables.security-advisory.private-vulnerability-reporting-configure-notifications %} For more information about configuring notification preferences, see [AUTOTITLE](/code-security/security-advisories/working-with-repository-security-advisories/configuring-private-vulnerability-reporting-for-a-repository#configuring-notifications-for-private-vulnerability-reporting). diff --git a/content/code-security/tutorials/manage-security-alerts/best-practices-for-participating-in-a-security-campaign.md b/content/code-security/tutorials/manage-security-alerts/best-practices-for-participating-in-a-security-campaign.md index 548fb5cfd15d..eaa314dc4d55 100644 --- a/content/code-security/tutorials/manage-security-alerts/best-practices-for-participating-in-a-security-campaign.md +++ b/content/code-security/tutorials/manage-security-alerts/best-practices-for-participating-in-a-security-campaign.md @@ -1,7 +1,7 @@ --- -title: Best practices for participating in a code security campaign -shortTitle: Best practices for campaigns -intro: Learn how you can successfully take part in a security campaign for {% data variables.product.prodname_code_scanning %} alerts and how it can benefit your career as well as your code. +title: Participating in a code security campaign +shortTitle: Participate in campaigns +intro: If you’ve been assigned alerts as part of a security campaign, this guide explains what campaigns are, what to expect, and how to resolve alerts effectively. allowTitleToDifferFromFilename: true permissions: '{% data reusables.permissions.code-scanning-all-alerts %}' product: '{% data reusables.gated-features.security-campaigns %}' @@ -17,15 +17,15 @@ redirect_from: - /code-security/code-scanning/managing-code-scanning-alerts/best-practices-for-participating-in-a-security-campaign --- -## What is a code security campaign +## What is a code security campaign? -A security campaign is a group of {% data variables.product.prodname_code_scanning %} alerts, detected in the default branches of repositories, chosen by an organization owner or security manager for remediation. +A code security campaign is a focused effort to remediate a defined group of {% data variables.product.prodname_code_scanning %} alerts across one or more repositories. -You can take part in a security campaign by fixing one or more of the alerts included in the campaign. +Campaigns are created by organization owners or security managers and typically target alerts detected in the default branches of repositories. If you’re participating in a campaign, you’ve been asked to help resolve some of these alerts. -## What are the benefits of participating in a campaign +## What are the benefits of participating in a campaign? -In addition to the benefit of removing an important security problem from your organization's codebase, alerts in a security campaign have several other benefits compared with fixing another alert in your repository. +In addition to reducing risk in your organization’s codebase, alerts in a security campaign have several other benefits compared with fixing another alert in your repository. * You have a campaign manager on the security team to collaborate with and a specific contact link for discussing campaign activities. * You know that you are fixing a security alert that is important to the company. @@ -34,9 +34,11 @@ In addition to the benefit of removing an important security problem from your o * If you have access to {% data variables.copilot.copilot_chat %}, you can ask questions about the alert and the suggested fix.{% endif %} * You are improving and demonstrating your knowledge of secure coding. -Adopting a few key best practices can help you participate successfully in a campaign. +Participating in a campaign helps reduce risk in your organization’s codebase while strengthening your secure coding skills. -## Stay informed +## 1. Learn about campaigns + +Start by reviewing campaign updates and deadlines so you can plan your work effectively. ### Notification settings @@ -50,15 +52,15 @@ When you open the **Security** tab for a repository with one or more campaign al ### Campaign-generated {% data variables.product.prodname_github_issues %} -Some campaigns automatically create {% data variables.product.prodname_github_issues %} for each repository which details the campaign managers, contact URL, and due date. +Some campaigns automatically create {% data variables.product.prodname_github_issues %} for each repository that detail the campaign managers, contact URL, and due date. -You can use this issue to plan and track campaign work as part of your usual workflows, such as: +Use this issue to coordinate work, track progress, and keep stakeholders aligned. For example, you might use the issue to: -* Adding the issue to project boards -* Adding assignees -* Creating sub-issues or tasklists +* Add the issue to project boards +* Add assignees +* Create sub-issues or tasklists -## Seek context +## 2. Build context before applying fixes Your security team may provide you with specific training ahead of participating in a campaign, so that you feel equipped to address the alerts included in the campaign. @@ -73,13 +75,25 @@ In addition, there are external resources for understanding common security issu * The **OWASP Foundation** provides many resources for learning about the most common vulnerabilities, see [About the OWASP Foundation](https://owasp.org/about/). * The **MITRE Corporation** maintains a detailed list of common weaknesses, see [About CWE](https://cwe.mitre.org/about/index.html). -## Group similar alerts +## 3. Collaborate early and often + +A security campaign will generally include a contact URL, which might link you to the campaign manager, an open forum (such as a {% data variables.product.github %} Discussion), or a website of resources. You should use this space to ask questions about the campaign or specific alerts, find useful resources, and share knowledge. + +To find the contact URL: + +1. Open the **Security** tab for your repository. +1. On the left sidebar, click the name of the campaign you are participating in. +1. On the campaign tracking page, to the right of the campaign manager's name, click **{% octicon "comment" aria-hidden="true" aria-label="comment" %}**. + +## 4. Group alerts strategically -When fixing security alerts as part of a campaign, it may be helpful to group and fix similar alerts together. By doing so, you can develop a deeper understanding of the underlying issue. As you gain confidence and efficiency in resolving a specific type of alert, it makes it easier and faster for you to resolve subsequent alerts. +Tackle similar alerts together to build momentum, reduce context switching, and develop a deeper understanding of the underlying issue. As you gain confidence and efficiency in resolving a specific type of alert, it makes it easier and faster for you to resolve subsequent alerts. {% ifversion copilot %} -## Leverage {% data variables.product.prodname_copilot_short %} +## 5. Resolve alerts with the help of {% data variables.product.prodname_copilot_short %} + +You can leverage {% data variables.product.prodname_copilot_short %} to help resolve alerts in a security campaign. Depending on the features enabled in your repository, you may have access to {% data variables.copilot.copilot_autofix_short %} suggestions and {% data variables.copilot.copilot_chat_short %}. {% ifversion code-scanning-autofix %} @@ -111,20 +125,10 @@ For example: ``` -If you don't already have access to {% data variables.copilot.copilot_chat_short %} through your organization{% ifversion ghec %} or enterprise{% endif %}, you can sign up to {% data variables.copilot.copilot_free %}. For more information, see [AUTOTITLE](/copilot/managing-copilot/managing-copilot-as-an-individual-subscriber/managing-copilot-free/accessing-github-copilot-free). +If you don't already have access to {% data variables.copilot.copilot_chat_short %} through your organization{% ifversion ghec %} or enterprise{% endif %}, you can sign up to {% data variables.copilot.copilot_free %}. See [AUTOTITLE](/copilot/managing-copilot/managing-copilot-as-an-individual-subscriber/managing-copilot-free/accessing-github-copilot-free). {% endif %} -## Ask questions - -A security campaign will generally include a contact URL, which might link you to the campaign manager, an open forum (such as a {% data variables.product.github %} Discussion), or a website of resources. You should use this space to ask questions about the campaign or specific alerts, find useful resources, and share knowledge. - -To find the contact URL: - -1. Open the **Security** tab for your repository. -1. On the left sidebar, click the name of the campaign you are participating in. -1. On the campaign tracking page, to the right of the campaign manager's name, click **{% octicon "comment" aria-hidden="true" aria-label="comment" %}**. - ## Next steps * [AUTOTITLE](/code-security/code-scanning/managing-code-scanning-alerts/fixing-alerts-in-security-campaign) diff --git a/content/code-security/tutorials/trialing-github-advanced-security/explore-trial-code-scanning.md b/content/code-security/tutorials/trialing-github-advanced-security/explore-trial-code-scanning.md index b2181850b424..cc7439086e34 100644 --- a/content/code-security/tutorials/trialing-github-advanced-security/explore-trial-code-scanning.md +++ b/content/code-security/tutorials/trialing-github-advanced-security/explore-trial-code-scanning.md @@ -84,11 +84,11 @@ Most enterprises put automatic checks in place to block the use of dependencies This allows you to update the configuration in a single location, but use the workflow in many repositories. You may want to use this central repository to maintain other workflows. For more information, see [AUTOTITLE](/actions/sharing-automations/reusing-workflows). -### {% data variables.product.prodname_copilot_short %} review +### {% data variables.copilot.copilot_code-review_short %} > [!NOTE] > -> * If you get a {% data variables.product.prodname_copilot_short %} subscription from an organization, you will only be able to participate in the {% data variables.release-phases.public_preview %} on the {% data variables.product.github %} website if an owner of your organization or enterprise has enabled **Copilot in GitHub.com > Opt in to preview features** in the **{% data variables.product.prodname_copilot %} policies** page of the organization or enterprise settings. See [AUTOTITLE](/enterprise-cloud@latest/copilot/managing-copilot/managing-github-copilot-in-your-organization/managing-policies-for-copilot-in-your-organization#enabling-copilot-features-in-your-organization) and [AUTOTITLE](/enterprise-cloud@latest/copilot/managing-copilot/managing-copilot-for-your-enterprise/managing-policies-and-features-for-copilot-in-your-enterprise#copilot-in-githubcom){% ifversion fpt or ghes %} in the {% data variables.product.prodname_ghe_cloud %} documentation{% endif %}. +> * If you get a {% data variables.product.prodname_copilot_short %} subscription from an organization, you will only be able to participate in the {% data variables.release-phases.public_preview %} on the {% data variables.product.github %} website if an owner of your organization or enterprise has enabled {% data variables.copilot.copilot_code-review_short %}. See [AUTOTITLE](/enterprise-cloud@latest/copilot/how-tos/administer-copilot/manage-for-organization/manage-policies#opting-in-to-previews-or-feedback) and [AUTOTITLE](/enterprise-cloud@latest/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-enterprise-policies){% ifversion fpt or ghes %} in the {% data variables.product.prodname_ghe_cloud %} documentation{% endif %}. By default, users request a review from {% data variables.product.prodname_copilot_short %} in the same way as they do from human reviewers. However, you can update or create an organization-level branch ruleset to automatically add {% data variables.product.prodname_copilot_short %} as a reviewer to all pull requests made to selected branches in all or selected repositories. See [AUTOTITLE](/enterprise-cloud@latest/copilot/how-tos/agents/copilot-code-review/automatic-code-review){% ifversion fpt or ghes %} in the {% data variables.product.prodname_ghe_cloud %} documentation{% endif %}. diff --git a/content/copilot/concepts/agents/coding-agent/about-coding-agent.md b/content/copilot/concepts/agents/coding-agent/about-coding-agent.md index 931470f67c7d..e5b62e140c6c 100644 --- a/content/copilot/concepts/agents/coding-agent/about-coding-agent.md +++ b/content/copilot/concepts/agents/coding-agent/about-coding-agent.md @@ -69,9 +69,9 @@ You can create specialized {% data variables.copilot.custom_agents_short %} for ## Measuring pull request outcomes for {% data variables.copilot.copilot_coding_agent %} -Enterprise administrators can use {% data variables.product.prodname_copilot_short %} usage metrics to analyze pull request outcomes for pull requests created by {% data variables.copilot.copilot_coding_agent %}. +Enterprise administrators and organization owners can use {% data variables.product.prodname_copilot_short %} usage metrics to analyze pull request outcomes for pull requests created by {% data variables.copilot.copilot_coding_agent %}. -The enterprise-level {% data variables.product.prodname_copilot_short %} usage metrics API includes pull request lifecycle metrics such as: +The {% data variables.product.prodname_copilot_short %} usage metrics APIs include pull request lifecycle metrics such as: * The total number of pull requests created and merged * The number of pull requests created by {% data variables.copilot.copilot_coding_agent %} that have been merged diff --git a/content/copilot/concepts/agents/enterprise-management.md b/content/copilot/concepts/agents/enterprise-management.md index 9e0334a5e522..a2cb43759e6c 100644 --- a/content/copilot/concepts/agents/enterprise-management.md +++ b/content/copilot/concepts/agents/enterprise-management.md @@ -11,21 +11,23 @@ category: - Learn about Copilot --- -{% data reusables.enterprise-accounts.ai-controls-preview-note %} - ## Overview -The AI Controls view provides a centralized platform where you can manage and monitor agents across your enterprise. From this agent control plane, you can: +The AI Controls view provides a centralized platform where you can manage and monitor AI policies and agents across your enterprise. From the "Agents" page, you can: * Manage the enterprise-level availability of agents like {% data variables.copilot.copilot_coding_agent %}, {% data variables.copilot.copilot_code-review_short %}, and {% data variables.copilot.copilot_custom_agents %} * Control who can manage your enterprise-level {% data variables.copilot.custom_agents_short %} -* View a list of agent sessions in your enterprise over the last 24 hours +* View and filter a list of agent sessions in your enterprise over the last 24 hours * Find a detailed record of agentic audit log events ## {% data variables.copilot.copilot_custom_agents %} {% data variables.copilot.copilot_custom_agents %} are specialized versions of {% data variables.copilot.copilot_coding_agent %} that you can configure with tailored prompts, tools, and context, making them excel at specific tasks. {% data variables.copilot.custom_agents_caps_short %} can be defined and managed at the enterprise level for greater control and compliance, or at the organization and repository levels to allow teams the flexibility to build for their specific needs. -Before you start configuring agentic settings for your enterprise, you should have a strong understanding of {% data variables.copilot.custom_agents_short %}. To learn more, see [AUTOTITLE](/copilot/concepts/agents/coding-agent/about-custom-agents). +You can manage your enterprise-level {% data variables.copilot.custom_agents_short %}: +* From the AI Controls view +* Using the REST API. See [AUTOTITLE](/rest/copilot/copilot-custom-agents). + +For more detailed information on {% data variables.copilot.custom_agents_short %}, see [AUTOTITLE](/copilot/concepts/agents/coding-agent/about-custom-agents). ## Agent sessions @@ -39,3 +41,7 @@ For billing information on agent sessions, see [AUTOTITLE](/billing/concepts/pro ## Next steps To get started with {% data variables.copilot.custom_agents_short %}, see [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/prepare-for-custom-agents). + +## Further reading + +* [AUTOTITLE](/copilot/concepts/agents/about-third-party-agents) diff --git a/content/copilot/concepts/context/mcp.md b/content/copilot/concepts/context/mcp.md index 5a1715bb5f49..729c06663143 100644 --- a/content/copilot/concepts/context/mcp.md +++ b/content/copilot/concepts/context/mcp.md @@ -99,6 +99,7 @@ The {% data variables.product.github %} MCP Registry is a curated list of MCP se ## Next steps +* [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers) * [AUTOTITLE](/copilot/how-tos/context/model-context-protocol/extending-copilot-chat-with-mcp) * [AUTOTITLE](/copilot/how-tos/context/model-context-protocol/using-the-github-mcp-server) * [AUTOTITLE](/copilot/tutorials/enhancing-copilot-agent-mode-with-mcp) diff --git a/content/copilot/concepts/copilot-usage-metrics/copilot-metrics.md b/content/copilot/concepts/copilot-usage-metrics/copilot-metrics.md index b624da9e6f47..0119438efb88 100644 --- a/content/copilot/concepts/copilot-usage-metrics/copilot-metrics.md +++ b/content/copilot/concepts/copilot-usage-metrics/copilot-metrics.md @@ -115,7 +115,15 @@ For example, all usage data for a Monday (which closes at midnight UTC) will be **Lines of Code (LoC) metrics** measure the number of lines {% data variables.product.prodname_copilot_short %} suggested, added, or deleted in the editor, providing a directional view of {% data variables.product.prodname_copilot_short %}’s tangible output. For example, "Lines added" shows how much code was actually accepted and inserted into the editor. -**Pull request lifecycle metrics** measure how {% data variables.product.prodname_copilot_short %} activity relates to pull request outcomes and delivery flow. These metrics include pull request creation and merge counts, median time to merge, and review suggestion activity. By comparing overall pull request activity with pull requests created by {% data variables.product.prodname_copilot_short %}, you can evaluate how AI-assisted workflows influence throughput and cycle time across your enterprise. +**Pull request lifecycle metrics** measure how {% data variables.product.prodname_copilot_short %} activity relates to pull request outcomes and delivery flow. These metrics include pull request creation and merge counts, median time to merge, and review suggestion activity. By comparing overall pull request activity with pull requests created by {% data variables.product.prodname_copilot_short %}, you can evaluate how AI-assisted workflows influence throughput and cycle time at the organization or enterprise level. + +### Interpreting pull request lifecycle metrics across scopes + +Pull request lifecycle metrics are available at both the organization and enterprise level. When comparing reports, keep the following in mind: + +* **Deduplication**: Enterprise-level reports deduplicate users across organizations. Organization-level reports do not. +* **Pull request-only data**: Pull request lifecycle metrics may appear even if IDE usage metrics are absent, since pull request data is derived from repository activity. +* **Attribution timing**: If a repository or organization is transferred between owners, pull request creation, review, and merge events may be attributed to different entities depending on when each event occurred. ## How can I use these metrics? diff --git a/content/copilot/concepts/prompting/response-customization.md b/content/copilot/concepts/prompting/response-customization.md index 6f35bf58988b..ef832a98b163 100644 --- a/content/copilot/concepts/prompting/response-customization.md +++ b/content/copilot/concepts/prompting/response-customization.md @@ -93,6 +93,8 @@ Here are some common use cases and examples for each type of custom instructions * Preferred language for a company which exclusively speaks a single language: `Always respond in Portuguese.` * Organization-wide preferences: `Do not generate code blocks in responses.` +{% data reusables.copilot.code-review.custom-instructions-limit %} + {% data reusables.copilot.custom-instructions-effective %} ## Next steps diff --git a/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/manage-copilot-code-review.md b/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/manage-copilot-code-review.md index 6aa4a43b1407..f2ae584f323d 100644 --- a/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/manage-copilot-code-review.md +++ b/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/manage-copilot-code-review.md @@ -11,7 +11,7 @@ shortTitle: 'Manage {% data variables.copilot.copilot_code-review_short %}' redirect_from: - /copilot/how-tos/administer-copilot/manage-for-enterprise/manage-copilot-code-review contentType: how-tos -category: +category: - Manage Copilot for a team --- @@ -28,7 +28,6 @@ You can allow your members to use {% data variables.copilot.copilot_code-review_ > [!NOTE] > > {% data reusables.copilot.code-review.preview-note %} -> * To participate in the {% data variables.release-phases.public_preview %}, you must first enable **{% data variables.product.prodname_copilot_short %} in {% data variables.product.prodname_dotcom_the_website %} > Opt in to preview features** from the "{% data variables.product.prodname_copilot_short %}" page of your enterprise's AI Controls. {% data reusables.enterprise-accounts.policy-enablement-next-steps %} diff --git a/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/monitor-agentic-activity.md b/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/monitor-agentic-activity.md index 51b6229be007..1fc18c9de922 100644 --- a/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/monitor-agentic-activity.md +++ b/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/monitor-agentic-activity.md @@ -12,13 +12,12 @@ category: - Manage Copilot for a team --- -{% data reusables.enterprise-accounts.ai-controls-preview-note %} - ## Viewing active and recent agentic sessions in your enterprise {% data reusables.enterprise-accounts.access-enterprise %} {% data reusables.enterprise-accounts.ai-controls-tab %} 1. Towards the top of the page, in the "Agent sessions" section, you will see the three most recent agent sessions in your enterprise. To view all agent sessions from the last 24 hours, click **View all**. +1. To see available filters for agent sessions, click the search bar at the top of the list, then press Space. Use the dropdown menu that appears to create your filter criteria. ## Tracking agentic activity in your enterprise through the audit log diff --git a/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/prepare-for-custom-agents.md b/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/prepare-for-custom-agents.md index 9caafaae8f38..2886842b7e0b 100644 --- a/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/prepare-for-custom-agents.md +++ b/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/prepare-for-custom-agents.md @@ -13,8 +13,6 @@ category: - Manage Copilot for a team --- -{% data reusables.enterprise-accounts.ai-controls-preview-note %} - Enterprise-level {% data variables.copilot.custom_agents_short %} are defined in a specific repository within an organization in your enterprise. Before you can create and use {% data variables.copilot.custom_agents_short %}, you need to create this repository and configure the relevant enterprise settings. ## Creating a repository for your {% data variables.copilot.custom_agents_short %} diff --git a/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-enterprise-policies.md b/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-enterprise-policies.md index 7dc4b4802652..a4f5d98baa7c 100644 --- a/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-enterprise-policies.md +++ b/content/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-enterprise-policies.md @@ -16,7 +16,7 @@ redirect_from: - /copilot/how-tos/administer/enterprises/manage-enterprise-policies - /copilot/how-tos/administer/manage-for-enterprise/manage-enterprise-policies contentType: how-tos -category: +category: - Manage Copilot for a team --- @@ -41,11 +41,9 @@ Enterprise owners can define a policy for the whole enterprise, or delegate the {% data reusables.copilot.mcp-servers-policy-note %} -## Opting in to previews or feedback +## Opting in to feedback collection -If your enterprise has a {% data variables.copilot.copilot_business_short %} or {% data variables.copilot.copilot_enterprise_short %} plan and you enable "{% data variables.product.prodname_copilot_short %} in {% data variables.product.prodname_dotcom_the_website %}" from the "{% data variables.product.prodname_copilot_short %}" page of the "AI Controls" tab, two additional options are displayed: - - {% data reusables.copilot.policies-for-dotcom %} +If you enable "{% data variables.product.prodname_copilot_short %} in {% data variables.product.prodname_dotcom_the_website %}" from the "{% data variables.product.prodname_copilot_short %}" page of the "AI Controls" tab, you can also opt in to user feedback collection to help {% data variables.product.github %} improve {% data variables.product.prodname_copilot_short %} features. ## Further reading diff --git a/content/copilot/how-tos/configure-content-exclusion/exclude-content-from-copilot.md b/content/copilot/how-tos/configure-content-exclusion/exclude-content-from-copilot.md index ad47be253bd7..82574e04c2fa 100644 --- a/content/copilot/how-tos/configure-content-exclusion/exclude-content-from-copilot.md +++ b/content/copilot/how-tos/configure-content-exclusion/exclude-content-from-copilot.md @@ -24,7 +24,7 @@ redirect_from: topics: - Copilot contentType: how-tos -category: +category: - Configure Copilot --- @@ -194,6 +194,12 @@ You can verify that content exclusions you have configured are working as expect If the file is excluded, {% data variables.copilot.copilot_chat_short %} will not be able to use the file to generate a response. The file will not be listed as a reference in {% data variables.product.prodname_copilot_short %}'s response. +## Manage content exclusions with the REST API + +Organization owners and enterprise owners can also manage content exclusions programmatically using the {% data variables.product.github %} REST API. + +For more information, see [AUTOTITLE](/rest/copilot/copilot-content-exclusion-management). + ## Further reading * [AUTOTITLE](/copilot/concepts/content-exclusion-for-github-copilot) diff --git a/content/copilot/how-tos/copilot-cli/cli-best-practices.md b/content/copilot/how-tos/copilot-cli/cli-best-practices.md index 862bfafad1e7..cca8e55f2fae 100644 --- a/content/copilot/how-tos/copilot-cli/cli-best-practices.md +++ b/content/copilot/how-tos/copilot-cli/cli-best-practices.md @@ -483,6 +483,9 @@ To submit private feedback to {% data variables.product.github %} about {% data /feedback ``` +## Hands-on practice +Try the [Creating applications with {% data variables.copilot.copilot_cli_short %}](https://github.com/skills/create-applications-with-the-copilot-cli) Skills exercise for practical experience building an application with {% data variables.copilot.copilot_cli_short %}. + ## Further reading * [AUTOTITLE](/copilot/concepts/agents/about-copilot-cli) diff --git a/content/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers.md b/content/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers.md new file mode 100644 index 000000000000..58e616312205 --- /dev/null +++ b/content/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers.md @@ -0,0 +1,105 @@ +--- +title: Adding MCP servers for {% data variables.copilot.copilot_cli %} +shortTitle: Add MCP servers +allowTitleToDifferFromFilename: true +intro: 'Extend {% data variables.product.prodname_copilot_short %}''s capabilities by connecting Model Context Protocol (MCP) servers to provide additional tools and context.' +versions: + feature: copilot +topics: + - Copilot +contentType: how-tos +category: + - Configure Copilot CLI # Copilot CLI bespoke page + - Author and optimize with Copilot # Copilot discovery page +--- + +The Model Context Protocol (MCP) is an open standard that defines how applications share context with large language models (LLMs). You can connect MCP servers to {% data variables.copilot.copilot_cli %} to give {% data variables.product.prodname_copilot_short %} access to external tools, data sources, and services. For an overview of MCP, see [AUTOTITLE](/copilot/concepts/about-mcp). + +## Adding an MCP server + +> [!NOTE] +> The {% data variables.product.github %} MCP server is built into {% data variables.copilot.copilot_cli_short %} and is already available without any additional configuration. The steps below are for adding other MCP servers. + +You can add MCP servers using the interactive `/mcp add` command within the CLI, or by editing the configuration file directly. + +For installation instructions, available tools, and URLs for specific MCP servers, see the [{% data variables.product.github %} MCP Registry](https://github.com/mcp). + +### Using the `/mcp add` command + +1. In interactive mode, enter `/mcp add`. A configuration form is displayed. Use Tab to navigate between fields. +1. Next to **Server Name**, enter a unique name for the MCP server. This is the name you will use to refer to the server. +1. Next to **Server Type**, select a type by pressing the corresponding number. The following types are available: + + * **Local** or **STDIO**: starts a local process and communicates over standard input/output (`stdin`/`stdout`). Both options work the same way. **STDIO** is the standard MCP protocol type name, so choose this if you want your configuration to be compatible with {% data variables.product.prodname_vscode_shortname %}, the {% data variables.copilot.copilot_coding_agent %}, and other MCP clients. + * **HTTP** or **SSE**: connects to a remote MCP server. **HTTP** uses the Streamable HTTP transport. **SSE** uses the legacy HTTP with Server-Sent Events transport, which is deprecated in the MCP specification but still supported for backwards compatibility. + +1. The remaining fields depend on the server type you selected: + + * If you chose **Local** or **STDIO**: + + * Next to **Command**, enter the command to start the server, including any arguments. For example, `npx @playwright/mcp@latest`. This corresponds to both the `command` and `args` properties in the JSON configuration file. + * Next to **Environment Variables**, optionally specify environment variables the server needs, such as API keys or tokens, as JSON key-value pairs. For example, `{"API_KEY": "YOUR-API-KEY"}`. The `PATH` variable is automatically inherited from your environment. All other environment variables must be configured here. + + * If you chose **HTTP** or **SSE**: + + * Next to **URL**, paste the remote server URL. For example, `https://mcp.context7.com/mcp`. + * Next to **HTTP Headers**, optionally specify HTTP headers as JSON. For example, `{"CONTEXT7_API_KEY": "YOUR-API-KEY"}`. + +1. Next to **Tools**, specify which tools from the server should be available. Enter `*` to include all tools, or provide a comma-separated list of tool names (no quotes needed). The default is `*`. +1. Press Ctrl+S to save the configuration. The MCP server is added and available immediately without restarting the CLI. + +### Editing the configuration file + +You can also add MCP servers by editing the configuration file at `~/.copilot/mcp-config.json`. This is useful if you want to share configurations or add multiple servers at once. + +The following example shows a configuration file with a local server and a remote HTTP server: + +```json copy +{ + "mcpServers": { + "playwright": { + "type": "local", + "command": "npx", + "args": ["@playwright/mcp@latest"], + "env": {}, + "tools": ["*"] + }, + "context7": { + "type": "http", + "url": "https://mcp.context7.com/mcp", + "headers": { + "CONTEXT7_API_KEY": "YOUR-API-KEY" + }, + "tools": ["*"] + } + } +} +``` + +For more information on MCP server configuration, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/coding-agent/extend-coding-agent-with-mcp#writing-a-json-configuration-for-mcp-servers). + +## Managing MCP servers + +You can manage your configured MCP servers using the following `/mcp` commands in {% data variables.copilot.copilot_cli_short %}. + +* **List configured MCP servers:** Use the command `/mcp show`. This displays all configured MCP servers and their current status. + +* **View details about a specific server:** Use the command `/mcp show SERVER-NAME`. This displays the status of the specified server and the list of tools it provides. + +* **Edit a server's configuration:** Use the command `/mcp edit SERVER-NAME`. + +* **Delete a server:** Use the command `/mcp delete SERVER-NAME`. + +* **Disable a server:** Use the command `/mcp disable SERVER-NAME`. A disabled server remains configured but is not used by {% data variables.product.prodname_copilot_short %} for the current session. + +* **Enable a previously disabled server:** Use the command `/mcp enable SERVER-NAME`. + +## Using MCP servers + +Once you have added an MCP server, {% data variables.product.prodname_copilot_short %} can automatically use the tools it provides when relevant to your prompt. You can also directly reference an MCP server and specific tools in a prompt to ensure they are used. + +## Further reading + +* [AUTOTITLE](/copilot/concepts/about-mcp) +* [AUTOTITLE](/copilot/how-tos/provide-context/use-mcp/extend-copilot-chat-with-mcp) +* [AUTOTITLE](/copilot/how-tos/use-copilot-agents/coding-agent/extend-coding-agent-with-mcp) 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 afbb56ff5f98..04488fa17204 100644 --- a/content/copilot/how-tos/copilot-cli/customize-copilot/index.md +++ b/content/copilot/how-tos/copilot-cli/customize-copilot/index.md @@ -10,6 +10,7 @@ children: - /add-custom-instructions - /use-hooks - /create-skills + - /add-mcp-servers - /create-custom-agents-for-cli - /plugins-finding-installing - /plugins-creating diff --git a/content/copilot/how-tos/copilot-cli/use-copilot-cli.md b/content/copilot/how-tos/copilot-cli/use-copilot-cli.md index 6b2d9a164d60..a510109336be 100644 --- a/content/copilot/how-tos/copilot-cli/use-copilot-cli.md +++ b/content/copilot/how-tos/copilot-cli/use-copilot-cli.md @@ -270,6 +270,8 @@ To extend the functionality available to you in {% data variables.copilot.copilo Details of your configured MCP servers are stored in the `mcp-config.json` file, which is located, by default, in the `~/.copilot` directory. This location can be changed by setting the `XDG_CONFIG_HOME` environment variable. For information about the JSON structure of a server definition, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/coding-agent/extend-coding-agent-with-mcp#writing-a-json-configuration-for-mcp-servers). +For more detailed information on adding and managing MCP servers in {% data variables.copilot.copilot_cli_short %}, see [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers). + ### Context management {% data variables.copilot.copilot_cli_short %} provides several slash commands to help you monitor and manage your context window: diff --git a/content/copilot/how-tos/provide-context/use-mcp/extend-copilot-chat-with-mcp.md b/content/copilot/how-tos/provide-context/use-mcp/extend-copilot-chat-with-mcp.md index ab61b4e01d9b..622bc1fdd25c 100644 --- a/content/copilot/how-tos/provide-context/use-mcp/extend-copilot-chat-with-mcp.md +++ b/content/copilot/how-tos/provide-context/use-mcp/extend-copilot-chat-with-mcp.md @@ -323,6 +323,7 @@ For more information on creating and configuring your own MCP servers, see [the ## Further reading +* [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers) * [AUTOTITLE](/copilot/using-github-copilot/coding-agent/extending-copilot-coding-agent-with-mcp) * [AUTOTITLE](/copilot/customizing-copilot/using-model-context-protocol/using-the-github-mcp-server) * [AUTOTITLE](/copilot/tutorials/enhancing-copilot-agent-mode-with-mcp) diff --git a/content/copilot/how-tos/set-up/set-up-for-enterprise.md b/content/copilot/how-tos/set-up/set-up-for-enterprise.md index 2e864c7a25d9..9da8809e3eb9 100644 --- a/content/copilot/how-tos/set-up/set-up-for-enterprise.md +++ b/content/copilot/how-tos/set-up/set-up-for-enterprise.md @@ -16,7 +16,7 @@ redirect_from: - /copilot/get-started/setting-up-github-copilot/setting-up-github-copilot-for-your-enterprise - /copilot/how-tos/set-up/setting-up-github-copilot-for-your-enterprise contentType: how-tos -category: +category: - Configure Copilot --- @@ -27,7 +27,7 @@ category: 1. Click the **{% octicon "check-circle" aria-hidden="true" aria-label="check-circle" %} Getting Started** tab. 1. Under "Next steps", click **Verify your payment method**. This will enable {% data variables.product.prodname_copilot %} in your enterprise. -After you've completed these steps, you will be able to confirm that {% data variables.product.prodname_copilot %} is enabled in your enterprise's **{% octicon "law" aria-hidden="true" aria-label="law" %} Policies** tab. +After you've completed these steps, you will be able to confirm that {% data variables.product.prodname_copilot %} is enabled in your enterprise's **{% octicon "law" aria-hidden="true" aria-label="law" %} AI Controls** tab. ## 2. Set policies diff --git a/content/copilot/how-tos/use-copilot-agents/coding-agent/extend-coding-agent-with-mcp.md b/content/copilot/how-tos/use-copilot-agents/coding-agent/extend-coding-agent-with-mcp.md index 1c14f67c3778..7584a92c9699 100644 --- a/content/copilot/how-tos/use-copilot-agents/coding-agent/extend-coding-agent-with-mcp.md +++ b/content/copilot/how-tos/use-copilot-agents/coding-agent/extend-coding-agent-with-mcp.md @@ -339,6 +339,7 @@ For information on using the {% data variables.product.github %} MCP server in o ## Next steps +* [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers) * [AUTOTITLE](/copilot/how-tos/use-copilot-agents/coding-agent/create-custom-agents) * [AUTOTITLE](/copilot/customizing-copilot/customizing-the-development-environment-for-copilot-coding-agent) * [AUTOTITLE](/copilot/customizing-copilot/extending-copilot-chat-with-mcp) diff --git a/content/copilot/reference/copilot-allowlist-reference.md b/content/copilot/reference/copilot-allowlist-reference.md index 72adb64467c3..958b773d746d 100644 --- a/content/copilot/reference/copilot-allowlist-reference.md +++ b/content/copilot/reference/copilot-allowlist-reference.md @@ -37,6 +37,7 @@ If your company employs security measures like a firewall or proxy server, you s | `https://*.enterprise.githubcopilot.com`[^4] | API service for {% data variables.product.prodname_copilot_short %} suggestions | | `https://*.SUBDOMAIN.ghe.com` | For {% data variables.product.prodname_copilot_short %} users on {% data variables.enterprise.data_residency_site %} | | `https://SUBDOMAIN.ghe.com` | For {% data variables.product.prodname_copilot_short %} users on {% data variables.enterprise.data_residency_site %} | +| `https://copilot-reports-*.b01.azurefd.net` | {% data variables.product.prodname_copilot_short %} usage metrics report downloads | Depending on the security policies and editors your organization uses, you may need to allowlist additional domains and URLs. For more information on specific editors, see [Further reading](#further-reading). diff --git a/content/copilot/reference/copilot-usage-metrics/copilot-usage-metrics.md b/content/copilot/reference/copilot-usage-metrics/copilot-usage-metrics.md index 8f8c0087f1e6..804842c11ab2 100644 --- a/content/copilot/reference/copilot-usage-metrics/copilot-usage-metrics.md +++ b/content/copilot/reference/copilot-usage-metrics/copilot-usage-metrics.md @@ -97,18 +97,21 @@ These fields appear in the exported NDJSON reports and in the {% data variables. ### Pull request activity fields (API only) -These fields capture daily pull request creation, review, merge, and suggestion activity across the enterprise, including activity performed by {% data variables.product.prodname_copilot_short %}. +> [!IMPORTANT] +> Organization- and enterprise-level reports may show different totals due to differences in user deduplication and attribution timing. For guidance on interpreting pull request metrics across scopes, see [AUTOTITLE](/copilot/concepts/copilot-usage-metrics/copilot-metrics#interpreting-pull-request-lifecycle-metrics-across-scopes). + +These fields capture daily pull request creation, review, merge, and suggestion activity at the enterprise or organization scope, including activity performed by {% data variables.product.prodname_copilot_short %}. | Field | Description | |:--|:--| -| `pull_requests.total_created` | Total number of pull requests created across the enterprise on this specific day.

Creation is a one-time event. Each pull request is counted only on the day it is created. | -| `pull_requests.total_reviewed` | Total number of pull requests reviewed across the enterprise on this specific day.

The same pull request may be counted on multiple days if it receives reviews on multiple days. Within a single day, each pull request is counted once, even if multiple review actions occur. | -| `pull_requests.total_merged` | Total number of pull requests merged across the enterprise on this specific day.

Merging is a one-time event. Each pull request is counted only on the day it is merged. | +| `pull_requests.total_created` | Total number of pull requests created on this specific day.

Creation is a one-time event. Each pull request is counted only on the day it is created. | +| `pull_requests.total_reviewed` | Total number of pull requests reviewed on this specific day.

The same pull request may be counted on multiple days if it receives reviews on multiple days. Within a single day, each pull request is counted once, even if multiple review actions occur. | +| `pull_requests.total_merged` | Total number of pull requests merged on this specific day.

Merging is a one-time event. Each pull request is counted only on the day it is merged. | | `pull_requests.median_minutes_to_merge` | Median time, in minutes, between pull request creation and merge for pull requests merged on this specific day.

Median is used to reduce the impact of outliers from unusually long-running pull requests. | | `pull_requests.total_suggestions` | Total number of pull request review suggestions generated on this specific day, regardless of author. | | `pull_requests.total_applied_suggestions` | Total number of pull request review suggestions that were applied on this specific day, regardless of author. | -| `pull_requests.total_created_by_copilot` | Number of pull requests created by {% data variables.product.prodname_copilot_short %} across the enterprise on this specific day. | -| `pull_requests.total_reviewed_by_copilot` | Number of pull requests reviewed by {% data variables.product.prodname_copilot_short %} across the enterprise on this specific day.

A pull request may be counted on multiple days if {% data variables.product.prodname_copilot_short %} reviews it on multiple days. | +| `pull_requests.total_created_by_copilot` | Number of pull requests created by {% data variables.product.prodname_copilot_short %} on this specific day. | +| `pull_requests.total_reviewed_by_copilot` | Number of pull requests reviewed by {% data variables.product.prodname_copilot_short %} on this specific day.

A pull request may be counted on multiple days if {% data variables.product.prodname_copilot_short %} reviews it on multiple days. | | `pull_requests.total_merged_created_by_copilot` | Number of pull requests created by {% data variables.product.prodname_copilot_short %} that were merged on this specific day. Each pull request is counted only on the day it is merged. | | `pull_requests.median_minutes_to_merge_copilot_authored` | Median time, in minutes, between pull request creation and merge for pull requests created by {% data variables.product.prodname_copilot_short %} and merged on this specific day. | | `pull_requests.total_copilot_suggestions` | Number of pull request review suggestions generated by {% data variables.product.prodname_copilot_short %} on this specific day. | diff --git a/content/copilot/tutorials/roll-out-at-scale/establish-ai-managers.md b/content/copilot/tutorials/roll-out-at-scale/establish-ai-managers.md index 76770af801fa..730d6c8628c8 100644 --- a/content/copilot/tutorials/roll-out-at-scale/establish-ai-managers.md +++ b/content/copilot/tutorials/roll-out-at-scale/establish-ai-managers.md @@ -11,7 +11,7 @@ contentType: tutorials --- > [!NOTE] -> Enterprise custom roles, enterprise teams, and the AI Controls view are in public preview and subject to change. +> Enterprise custom roles and enterprise teams are in public preview and subject to change. ## Overview diff --git a/content/copilot/tutorials/use-custom-instructions.md b/content/copilot/tutorials/use-custom-instructions.md index be148e75dc5a..6a9b10bca99c 100644 --- a/content/copilot/tutorials/use-custom-instructions.md +++ b/content/copilot/tutorials/use-custom-instructions.md @@ -45,6 +45,8 @@ Before writing custom instructions, it's helpful to understand how {% data varia * **Context limits**: Very long instruction files may result in some instructions being overlooked. * **Specificity matters**: Clear, specific instructions work better than vague directives. +{% data reusables.copilot.code-review.custom-instructions-limit %} + Keep these factors in mind as you write your instructions—they'll help you set realistic expectations and write more effective guidance. ## Writing effective custom instructions diff --git a/content/desktop/index.md b/content/desktop/index.md index 2ff745461def..b1a4170abe50 100644 --- a/content/desktop/index.md +++ b/content/desktop/index.md @@ -28,9 +28,6 @@ changelog: layout: product-landing versions: feature: desktop -topics: - - Desktop - - Repositories children: - /overview - /installing-and-authenticating-to-github-desktop diff --git a/content/discussions/managing-discussions-for-your-community/viewing-insights-for-your-discussions.md b/content/discussions/managing-discussions-for-your-community/viewing-insights-for-your-discussions.md index 07aea8acd308..ed0ceef59ebf 100644 --- a/content/discussions/managing-discussions-for-your-community/viewing-insights-for-your-discussions.md +++ b/content/discussions/managing-discussions-for-your-community/viewing-insights-for-your-discussions.md @@ -6,8 +6,6 @@ versions: fpt: '*' ghes: '*' ghec: '*' -topics: - - Discussions shortTitle: View discussions insights --- diff --git a/content/education/manage-coursework-with-github-classroom/get-started-with-github-classroom/about-github-classroom.md b/content/education/manage-coursework-with-github-classroom/get-started-with-github-classroom/about-github-classroom.md index c30db7a5cc95..bd5b29d96f39 100644 --- a/content/education/manage-coursework-with-github-classroom/get-started-with-github-classroom/about-github-classroom.md +++ b/content/education/manage-coursework-with-github-classroom/get-started-with-github-classroom/about-github-classroom.md @@ -2,8 +2,6 @@ title: About GitHub Classroom intro: 'Teachers and school administrators can use {% data variables.product.prodname_classroom %} to create virtual classrooms, make and edit assignments, automatically grade assignment submissions, and more.' type: overview -topics: - - Fundamentals versions: fpt: '*' --- diff --git a/content/education/manage-coursework-with-github-classroom/teach-with-github-classroom/about-assignments.md b/content/education/manage-coursework-with-github-classroom/teach-with-github-classroom/about-assignments.md index a55d481dbebd..39ca39c45809 100644 --- a/content/education/manage-coursework-with-github-classroom/teach-with-github-classroom/about-assignments.md +++ b/content/education/manage-coursework-with-github-classroom/teach-with-github-classroom/about-assignments.md @@ -3,8 +3,6 @@ title: About assignments intro: 'You can use individual or group assignments in {% data variables.product.prodname_classroom %} to teach students and grade their work.' shortTitle: About assignments type: overview -topics: - - Fundamentals versions: fpt: '*' --- diff --git a/content/education/manage-coursework-with-github-classroom/teach-with-github-classroom/monitor-students-progress-with-the-assignment-overview-page.md b/content/education/manage-coursework-with-github-classroom/teach-with-github-classroom/monitor-students-progress-with-the-assignment-overview-page.md index d16ae6deeeda..a3c6c934e0c5 100644 --- a/content/education/manage-coursework-with-github-classroom/teach-with-github-classroom/monitor-students-progress-with-the-assignment-overview-page.md +++ b/content/education/manage-coursework-with-github-classroom/teach-with-github-classroom/monitor-students-progress-with-the-assignment-overview-page.md @@ -5,8 +5,6 @@ intro: You can use the assignment overview page to track the progress of each st versions: fpt: '*' permissions: 'Organization owners who are admins for a classroom can create and manage group assignments for a classroom. {% data reusables.classroom.classroom-admins-link %}' -topics: - - Education --- ## About the assignment overview page diff --git a/content/enterprise-onboarding/getting-started-with-your-enterprise/about-enterprise-billing.md b/content/enterprise-onboarding/getting-started-with-your-enterprise/about-enterprise-billing.md index 80687f95d8cd..3274efb0eb06 100644 --- a/content/enterprise-onboarding/getting-started-with-your-enterprise/about-enterprise-billing.md +++ b/content/enterprise-onboarding/getting-started-with-your-enterprise/about-enterprise-billing.md @@ -4,9 +4,6 @@ intro: 'Understand billing for {% data variables.product.prodname_ghe_cloud %}.' versions: ghec: '*' type: overview -topics: - - Accounts - - Enterprise shortTitle: Billing --- diff --git a/content/enterprise-onboarding/getting-started-with-your-enterprise/about-migrating-to-github-enterprise-cloud.md b/content/enterprise-onboarding/getting-started-with-your-enterprise/about-migrating-to-github-enterprise-cloud.md index 6c7736739782..0007a5fdc52a 100644 --- a/content/enterprise-onboarding/getting-started-with-your-enterprise/about-migrating-to-github-enterprise-cloud.md +++ b/content/enterprise-onboarding/getting-started-with-your-enterprise/about-migrating-to-github-enterprise-cloud.md @@ -4,9 +4,6 @@ intro: Explore migrating your current solution into {% data variables.product.pr versions: ghec: '*' type: overview -topics: - - Accounts - - Enterprise shortTitle: Migrations contentType: other --- diff --git a/content/enterprise-onboarding/getting-started-with-your-enterprise/choose-an-enterprise-type.md b/content/enterprise-onboarding/getting-started-with-your-enterprise/choose-an-enterprise-type.md index f05f838dbc96..75167550ac40 100644 --- a/content/enterprise-onboarding/getting-started-with-your-enterprise/choose-an-enterprise-type.md +++ b/content/enterprise-onboarding/getting-started-with-your-enterprise/choose-an-enterprise-type.md @@ -4,12 +4,6 @@ shortTitle: Choose an enterprise type intro: Understand the types of enterprises available in {% data variables.product.prodname_ghe_cloud %} and decide whether {% data variables.product.prodname_emus %} is right for you by asking yourself some questions. versions: ghec: '*' -topics: - - Accounts - - Authentication - - Enterprise - - Identity - - SSO redirect_from: - /admin/identity-and-access-management/understanding-iam-for-enterprises/identifying-the-best-authentication-method-for-your-enterprise - /admin/identity-and-access-management/managing-iam-for-your-enterprise/identifying-the-best-authentication-method-for-your-enterprise diff --git a/content/enterprise-onboarding/getting-started-with-your-enterprise/index.md b/content/enterprise-onboarding/getting-started-with-your-enterprise/index.md index 93a66ff1a5a9..e677f83eae06 100644 --- a/content/enterprise-onboarding/getting-started-with-your-enterprise/index.md +++ b/content/enterprise-onboarding/getting-started-with-your-enterprise/index.md @@ -3,10 +3,6 @@ title: Getting started with your enterprise intro: Start a trial of {% data variables.product.prodname_enterprise %}, add users, and discover billing and migrations. versions: ghec: '*' -topics: - - Accounts - - Enterprise - - Set up shortTitle: Get started children: - /choose-an-enterprise-type diff --git a/content/enterprise-onboarding/getting-started-with-your-enterprise/setting-up-a-trial-of-github-enterprise.md b/content/enterprise-onboarding/getting-started-with-your-enterprise/setting-up-a-trial-of-github-enterprise.md index 64beccb2b8b9..9ac2af88a5f3 100644 --- a/content/enterprise-onboarding/getting-started-with-your-enterprise/setting-up-a-trial-of-github-enterprise.md +++ b/content/enterprise-onboarding/getting-started-with-your-enterprise/setting-up-a-trial-of-github-enterprise.md @@ -4,9 +4,6 @@ intro: 'Understand what is included in the {% data variables.product.prodname_gh versions: ghec: '*' type: how_to -topics: - - Accounts - - Enterprise shortTitle: Start a trial redirect_from: - /enterprise-onboarding/getting-started-with-your-enterprise/ending-a-trial-of-github-enterprise diff --git a/content/enterprise-onboarding/github-actions-for-your-enterprise/about-github-actions-for-enterprises.md b/content/enterprise-onboarding/github-actions-for-your-enterprise/about-github-actions-for-enterprises.md index 7b3030cf6dfd..7af7afceac46 100644 --- a/content/enterprise-onboarding/github-actions-for-your-enterprise/about-github-actions-for-enterprises.md +++ b/content/enterprise-onboarding/github-actions-for-your-enterprise/about-github-actions-for-enterprises.md @@ -5,9 +5,6 @@ intro: '{% data variables.product.prodname_actions %} can improve developer prod versions: ghec: '*' type: overview -topics: - - Actions - - Enterprise allowTitleToDifferFromFilename: true --- diff --git a/content/enterprise-onboarding/github-actions-for-your-enterprise/actions-components.md b/content/enterprise-onboarding/github-actions-for-your-enterprise/actions-components.md index 0b161636906c..3a15021e5918 100644 --- a/content/enterprise-onboarding/github-actions-for-your-enterprise/actions-components.md +++ b/content/enterprise-onboarding/github-actions-for-your-enterprise/actions-components.md @@ -5,9 +5,6 @@ intro: Understand the basics of {% data variables.product.prodname_actions %}, i versions: ghec: '*' type: overview -topics: - - Fundamentals - - Actions allowTitleToDifferFromFilename: true redirect_from: - /enterprise-onboarding/github-actions-for-your-enterprise/understanding-github-actions diff --git a/content/enterprise-onboarding/github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-cloud.md b/content/enterprise-onboarding/github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-cloud.md index f859974fd12a..10a607715726 100644 --- a/content/enterprise-onboarding/github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-cloud.md +++ b/content/enterprise-onboarding/github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-cloud.md @@ -5,9 +5,6 @@ intro: 'Configure {% data variables.product.prodname_actions %} on {% data varia versions: ghec: '*' type: how_to -topics: - - Actions - - Enterprise allowTitleToDifferFromFilename: true --- diff --git a/content/enterprise-onboarding/github-actions-for-your-enterprise/index.md b/content/enterprise-onboarding/github-actions-for-your-enterprise/index.md index 2b4d70a9f9cc..a7c8bf06f17f 100644 --- a/content/enterprise-onboarding/github-actions-for-your-enterprise/index.md +++ b/content/enterprise-onboarding/github-actions-for-your-enterprise/index.md @@ -4,11 +4,6 @@ intro: Plan and implement a rollout of {% data variables.product.prodname_action shortTitle: Set up CI/CD with Actions versions: ghec: '*' -topics: - - Administrator - - Enterprise - - Set up - - Actions children: - /about-github-actions-for-enterprises - /actions-components diff --git a/content/enterprise-onboarding/github-actions-for-your-enterprise/migrating-your-enterprise-to-github-actions.md b/content/enterprise-onboarding/github-actions-for-your-enterprise/migrating-your-enterprise-to-github-actions.md index 8a4074fe9413..9546fb22e00f 100644 --- a/content/enterprise-onboarding/github-actions-for-your-enterprise/migrating-your-enterprise-to-github-actions.md +++ b/content/enterprise-onboarding/github-actions-for-your-enterprise/migrating-your-enterprise-to-github-actions.md @@ -5,10 +5,6 @@ intro: 'Plan a migration to {% data variables.product.prodname_actions %} for yo versions: ghec: '*' type: how_to -topics: - - Actions - - Enterprise - - Migration allowTitleToDifferFromFilename: true --- diff --git a/content/enterprise-onboarding/github-actions-for-your-enterprise/planning-a-rollout-of-github-actions.md b/content/enterprise-onboarding/github-actions-for-your-enterprise/planning-a-rollout-of-github-actions.md index 49170ef5f372..9a8190e8ac39 100644 --- a/content/enterprise-onboarding/github-actions-for-your-enterprise/planning-a-rollout-of-github-actions.md +++ b/content/enterprise-onboarding/github-actions-for-your-enterprise/planning-a-rollout-of-github-actions.md @@ -5,9 +5,6 @@ intro: You can plan how to roll out {% data variables.product.prodname_actions % versions: ghec: '*' type: how_to -topics: - - Actions - - Enterprise allowTitleToDifferFromFilename: true redirect_from: - /enterprise-onboarding/github-actions-for-your-enterprise/security-hardening-for-github-actions diff --git a/content/enterprise-onboarding/github-apps/automations-in-your-enterprise.md b/content/enterprise-onboarding/github-apps/automations-in-your-enterprise.md index a97dcb3943b0..cb6a03da8b04 100644 --- a/content/enterprise-onboarding/github-apps/automations-in-your-enterprise.md +++ b/content/enterprise-onboarding/github-apps/automations-in-your-enterprise.md @@ -5,9 +5,6 @@ intro: 'Learn how {% data variables.product.prodname_github_apps %}, external se versions: ghec: '*' type: overview -topics: - - Enterprise - - Fundamentals --- {% data reusables.enterprise-onboarding.automations %} diff --git a/content/enterprise-onboarding/github-apps/create-enterprise-apps.md b/content/enterprise-onboarding/github-apps/create-enterprise-apps.md index 410bc96b1c93..cb09a383caf2 100644 --- a/content/enterprise-onboarding/github-apps/create-enterprise-apps.md +++ b/content/enterprise-onboarding/github-apps/create-enterprise-apps.md @@ -5,9 +5,6 @@ intro: 'Create GitHub Apps that can access enterprise-level resources and automa versions: ghec: '*' type: how_to -topics: - - Enterprise - - GitHub Apps --- {% data reusables.enterprise-onboarding.create-enterprise-apps %} diff --git a/content/enterprise-onboarding/github-apps/index.md b/content/enterprise-onboarding/github-apps/index.md index 4a6dc10b394b..5b090bc52566 100644 --- a/content/enterprise-onboarding/github-apps/index.md +++ b/content/enterprise-onboarding/github-apps/index.md @@ -5,9 +5,6 @@ intro: 'Automate processes across your enterprise.' versions: ghec: '*' type: overview -topics: - - Enterprise - - GitHub Apps children: - /automations-in-your-enterprise - /create-enterprise-apps diff --git a/content/enterprise-onboarding/github-apps/install-enterprise-apps.md b/content/enterprise-onboarding/github-apps/install-enterprise-apps.md index f403a299cf14..e46ed389fbed 100644 --- a/content/enterprise-onboarding/github-apps/install-enterprise-apps.md +++ b/content/enterprise-onboarding/github-apps/install-enterprise-apps.md @@ -5,9 +5,6 @@ intro: 'Install GitHub Apps to build automations for the enterprise account.' versions: ghec: '*' type: how_to -topics: - - Enterprise - - GitHub Apps --- {% data reusables.enterprise-onboarding.install-enterprise-apps %} diff --git a/content/enterprise-onboarding/govern-people-and-repositories/about-enterprise-policies.md b/content/enterprise-onboarding/govern-people-and-repositories/about-enterprise-policies.md index 6cf524483274..0b2e53dda838 100644 --- a/content/enterprise-onboarding/govern-people-and-repositories/about-enterprise-policies.md +++ b/content/enterprise-onboarding/govern-people-and-repositories/about-enterprise-policies.md @@ -5,9 +5,6 @@ intro: 'Understand what policies are and why they are useful.' versions: ghec: '*' type: overview -topics: - - Enterprise - - Policies --- {% data reusables.enterprise-onboarding.about-policies %} diff --git a/content/enterprise-onboarding/govern-people-and-repositories/about-enterprise-security.md b/content/enterprise-onboarding/govern-people-and-repositories/about-enterprise-security.md index a27b6d71de74..6bfe948a21fe 100644 --- a/content/enterprise-onboarding/govern-people-and-repositories/about-enterprise-security.md +++ b/content/enterprise-onboarding/govern-people-and-repositories/about-enterprise-security.md @@ -9,10 +9,6 @@ redirect_from: - /enterprise-onboarding/feature-enhancements/about-code-security-for-your-enterprise - /enterprise-onboarding/feature-enhancements/about-enterprise-security type: overview -topics: - - Enterprise - - Set up - - Security contentType: other --- diff --git a/content/enterprise-onboarding/govern-people-and-repositories/create-custom-properties.md b/content/enterprise-onboarding/govern-people-and-repositories/create-custom-properties.md index 122b8b55e48a..b0cb89ca364c 100644 --- a/content/enterprise-onboarding/govern-people-and-repositories/create-custom-properties.md +++ b/content/enterprise-onboarding/govern-people-and-repositories/create-custom-properties.md @@ -5,8 +5,6 @@ intro: 'Create and apply custom properties to categorize and manage repositories versions: ghec: '*' type: how_to -topics: - - Enterprise --- {% data reusables.enterprise-onboarding.creating-custom-properties %} diff --git a/content/enterprise-onboarding/govern-people-and-repositories/create-repository-policies.md b/content/enterprise-onboarding/govern-people-and-repositories/create-repository-policies.md index f66416ef9621..c49f8709fa38 100644 --- a/content/enterprise-onboarding/govern-people-and-repositories/create-repository-policies.md +++ b/content/enterprise-onboarding/govern-people-and-repositories/create-repository-policies.md @@ -5,10 +5,6 @@ intro: 'Enforce repository policies across your enterprise using custom properti versions: ghec: '*' type: how_to -topics: - - Enterprise - - Policies - - Repositories --- {% data reusables.enterprise.repo-policy-rules-preview %} diff --git a/content/enterprise-onboarding/govern-people-and-repositories/index.md b/content/enterprise-onboarding/govern-people-and-repositories/index.md index db8046ae6e8f..564121d1801f 100644 --- a/content/enterprise-onboarding/govern-people-and-repositories/index.md +++ b/content/enterprise-onboarding/govern-people-and-repositories/index.md @@ -5,9 +5,6 @@ intro: 'Implement policies, custom properties, and rulesets to govern users and versions: ghec: '*' type: overview -topics: - - Enterprise - - Policies children: - /about-enterprise-policies - /create-custom-properties diff --git a/content/enterprise-onboarding/govern-people-and-repositories/protect-branches.md b/content/enterprise-onboarding/govern-people-and-repositories/protect-branches.md index c76e99c20ec6..8c075b1633d6 100644 --- a/content/enterprise-onboarding/govern-people-and-repositories/protect-branches.md +++ b/content/enterprise-onboarding/govern-people-and-repositories/protect-branches.md @@ -5,8 +5,6 @@ intro: 'Create rulesets at the enterprise level to enforce code governance polic versions: ghec: '*' type: how_to -topics: - - Enterprise redirect_from: - /enterprise-onboarding/feature-enhancements/about-rulesets --- diff --git a/content/enterprise-onboarding/govern-people-and-repositories/using-the-audit-log-for-your-enterprise.md b/content/enterprise-onboarding/govern-people-and-repositories/using-the-audit-log-for-your-enterprise.md index ae7af309b9ca..3950444223e3 100644 --- a/content/enterprise-onboarding/govern-people-and-repositories/using-the-audit-log-for-your-enterprise.md +++ b/content/enterprise-onboarding/govern-people-and-repositories/using-the-audit-log-for-your-enterprise.md @@ -4,8 +4,6 @@ intro: Use the audit log to monitor activity in your enterprise. versions: ghec: '*' type: overview -topics: - - Enterprise shortTitle: Use the audit log redirect_from: - /enterprise-onboarding/feature-enhancements/about-the-audit-log-for-your-enterprise diff --git a/content/enterprise-onboarding/index.md b/content/enterprise-onboarding/index.md index 535e14c947c5..a3410738f72c 100644 --- a/content/enterprise-onboarding/index.md +++ b/content/enterprise-onboarding/index.md @@ -59,10 +59,6 @@ journeyTracks: - href: '/enterprise-onboarding/github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-cloud' versions: ghec: '*' -topics: - - Administrator - - Enterprise - - Set up children: - /getting-started-with-your-enterprise - /setting-up-organizations-and-teams diff --git a/content/enterprise-onboarding/setting-up-organizations-and-teams/about-roles-in-an-enterprise.md b/content/enterprise-onboarding/setting-up-organizations-and-teams/about-roles-in-an-enterprise.md index a745e9a431c3..c9e95ee190a6 100644 --- a/content/enterprise-onboarding/setting-up-organizations-and-teams/about-roles-in-an-enterprise.md +++ b/content/enterprise-onboarding/setting-up-organizations-and-teams/about-roles-in-an-enterprise.md @@ -5,9 +5,6 @@ intro: 'Understand the different role types in an enterprise and how they contro versions: ghec: '*' type: overview -topics: - - Enterprise - - Permissions redirect_from: - /enterprise-onboarding/feature-enhancements/about-access-permissions-on-github --- diff --git a/content/enterprise-onboarding/setting-up-organizations-and-teams/about-teams-in-an-enterprise.md b/content/enterprise-onboarding/setting-up-organizations-and-teams/about-teams-in-an-enterprise.md index d03fcf3388b3..120d835e37c4 100644 --- a/content/enterprise-onboarding/setting-up-organizations-and-teams/about-teams-in-an-enterprise.md +++ b/content/enterprise-onboarding/setting-up-organizations-and-teams/about-teams-in-an-enterprise.md @@ -5,9 +5,6 @@ intro: 'Discover how teams help organize users, manage repository access, and st versions: ghec: '*' type: overview -topics: - - Enterprise - - Teams redirect_from: - /enterprise-onboarding/setting-up-organizations-and-teams/managing-organization-members --- diff --git a/content/enterprise-onboarding/setting-up-organizations-and-teams/assigning-roles-to-teams-and-users.md b/content/enterprise-onboarding/setting-up-organizations-and-teams/assigning-roles-to-teams-and-users.md index f93b6e9a6e50..e50c92f3812f 100644 --- a/content/enterprise-onboarding/setting-up-organizations-and-teams/assigning-roles-to-teams-and-users.md +++ b/content/enterprise-onboarding/setting-up-organizations-and-teams/assigning-roles-to-teams-and-users.md @@ -5,9 +5,6 @@ intro: 'Assign enterprise roles to manage access and permissions.' versions: ghec: '*' type: how_to -topics: - - Enterprise - - Teams --- {% data reusables.enterprise-onboarding.assign-roles %} diff --git a/content/enterprise-onboarding/setting-up-organizations-and-teams/best-practices.md b/content/enterprise-onboarding/setting-up-organizations-and-teams/best-practices.md index 62a049c3fe3a..5a965b60d54f 100644 --- a/content/enterprise-onboarding/setting-up-organizations-and-teams/best-practices.md +++ b/content/enterprise-onboarding/setting-up-organizations-and-teams/best-practices.md @@ -4,9 +4,6 @@ intro: Structure your enterprise and organizations for the best developer experi versions: ghec: '*' type: how_to -topics: - - Organizations - - Enterprise shortTitle: Best practices redirect_from: - /enterprise-onboarding/setting-up-organizations-and-teams/best-practices-for-organizations-in-your-enterprise diff --git a/content/enterprise-onboarding/setting-up-organizations-and-teams/creating-custom-roles.md b/content/enterprise-onboarding/setting-up-organizations-and-teams/creating-custom-roles.md index a95e8a844215..07740d2d0fd9 100644 --- a/content/enterprise-onboarding/setting-up-organizations-and-teams/creating-custom-roles.md +++ b/content/enterprise-onboarding/setting-up-organizations-and-teams/creating-custom-roles.md @@ -5,9 +5,6 @@ intro: 'Create custom organization roles with fine-grained permissions tailored versions: ghec: '*' type: how_to -topics: - - Enterprise - - Permissions --- {% data reusables.enterprise-onboarding.create-custom-roles %} diff --git a/content/enterprise-onboarding/setting-up-organizations-and-teams/creating-teams.md b/content/enterprise-onboarding/setting-up-organizations-and-teams/creating-teams.md index 2e06bd563eb3..23c2dcbfd2ad 100644 --- a/content/enterprise-onboarding/setting-up-organizations-and-teams/creating-teams.md +++ b/content/enterprise-onboarding/setting-up-organizations-and-teams/creating-teams.md @@ -6,9 +6,6 @@ versions: shortTitle: Create teams type: how_to permissions: Enterprise owners or organization owners -topics: - - Organizations - - Enterprise allowTitleToDifferFromFilename: true --- diff --git a/content/enterprise-onboarding/setting-up-organizations-and-teams/identify-role-requirements.md b/content/enterprise-onboarding/setting-up-organizations-and-teams/identify-role-requirements.md index 7472d740db57..30a418aef4c1 100644 --- a/content/enterprise-onboarding/setting-up-organizations-and-teams/identify-role-requirements.md +++ b/content/enterprise-onboarding/setting-up-organizations-and-teams/identify-role-requirements.md @@ -5,9 +5,6 @@ versions: ghec: '*' shortTitle: Identify role requirements type: how_to -topics: - - Organizations - - Enterprise --- {% data reusables.enterprise-onboarding.identify-role-requirements %} diff --git a/content/enterprise-onboarding/setting-up-organizations-and-teams/index.md b/content/enterprise-onboarding/setting-up-organizations-and-teams/index.md index 31379229d62a..ddb201bd6673 100644 --- a/content/enterprise-onboarding/setting-up-organizations-and-teams/index.md +++ b/content/enterprise-onboarding/setting-up-organizations-and-teams/index.md @@ -3,10 +3,6 @@ title: Setting up organizations and teams in your enterprise intro: Add and manage organizations and teams in your enterprise. versions: ghec: '*' -topics: - - Accounts - - Enterprise - - Set up shortTitle: Set up organizations and teams children: - /best-practices diff --git a/content/enterprise-onboarding/setting-up-organizations-and-teams/setting-up-an-organization.md b/content/enterprise-onboarding/setting-up-organizations-and-teams/setting-up-an-organization.md index 0e3102743750..52910cdeaf24 100644 --- a/content/enterprise-onboarding/setting-up-organizations-and-teams/setting-up-an-organization.md +++ b/content/enterprise-onboarding/setting-up-organizations-and-teams/setting-up-an-organization.md @@ -4,9 +4,6 @@ intro: 'Set up an organization in your enterprise.' versions: ghec: '*' type: how_to -topics: - - Organizations - - Enterprise shortTitle: Set up an organization redirect_from: - /enterprise-onboarding/setting-up-organizations-and-teams/managing-your-organizations diff --git a/content/enterprise-onboarding/setting-up-organizations-and-teams/use-innersource.md b/content/enterprise-onboarding/setting-up-organizations-and-teams/use-innersource.md index f52b22374034..0d6f2da21a2c 100644 --- a/content/enterprise-onboarding/setting-up-organizations-and-teams/use-innersource.md +++ b/content/enterprise-onboarding/setting-up-organizations-and-teams/use-innersource.md @@ -5,10 +5,6 @@ intro: Help dispersed teams to collaborate by setting up open source–style wor versions: ghec: '*' contentType: concepts -topics: - - Accounts - - Enterprise - - Fundamentals allowTitleToDifferFromFilename: true --- diff --git a/content/enterprise-onboarding/support-for-your-enterprise/index.md b/content/enterprise-onboarding/support-for-your-enterprise/index.md index 7a5801c0413d..0ea0bec28896 100644 --- a/content/enterprise-onboarding/support-for-your-enterprise/index.md +++ b/content/enterprise-onboarding/support-for-your-enterprise/index.md @@ -4,10 +4,6 @@ intro: 'Discover {% data variables.product.github %} support and set up a suppor allowTitleToDifferFromFilename: true versions: ghec: '*' -topics: - - Enterprise - - Set up - - Support shortTitle: Create a support model children: - /understanding-support diff --git a/content/enterprise-onboarding/support-for-your-enterprise/managing-support-entitlements.md b/content/enterprise-onboarding/support-for-your-enterprise/managing-support-entitlements.md index f1e8517be537..e34cf4a58219 100644 --- a/content/enterprise-onboarding/support-for-your-enterprise/managing-support-entitlements.md +++ b/content/enterprise-onboarding/support-for-your-enterprise/managing-support-entitlements.md @@ -5,10 +5,6 @@ versions: ghec: '*' allowTitleToDifferFromFilename: true type: how_to -topics: - - Enterprise - - Set up - - Support shortTitle: Manage support entitlements --- diff --git a/content/enterprise-onboarding/support-for-your-enterprise/understanding-support.md b/content/enterprise-onboarding/support-for-your-enterprise/understanding-support.md index 1158e3e63d06..fd97f60211bf 100644 --- a/content/enterprise-onboarding/support-for-your-enterprise/understanding-support.md +++ b/content/enterprise-onboarding/support-for-your-enterprise/understanding-support.md @@ -6,10 +6,6 @@ versions: ghec: '*' allowTitleToDifferFromFilename: true type: overview -topics: - - Enterprise - - Set up - - Support --- ## About {% data variables.contact.github_support %} diff --git a/content/enterprise-onboarding/support-for-your-enterprise/using-the-support-portal.md b/content/enterprise-onboarding/support-for-your-enterprise/using-the-support-portal.md index 99689f59bad5..1fe365766385 100644 --- a/content/enterprise-onboarding/support-for-your-enterprise/using-the-support-portal.md +++ b/content/enterprise-onboarding/support-for-your-enterprise/using-the-support-portal.md @@ -6,10 +6,6 @@ allowTitleToDifferFromFilename: true versions: ghec: '*' type: how_to -topics: - - Enterprise - - Set up - - Support --- ## About the {% data variables.contact.enterprise_portal %} for enterprises diff --git a/content/rest/copilot/copilot-content-exclusion-management.md b/content/rest/copilot/copilot-content-exclusion-management.md new file mode 100644 index 000000000000..ad5d3af89d19 --- /dev/null +++ b/content/rest/copilot/copilot-content-exclusion-management.md @@ -0,0 +1,14 @@ +--- +title: REST API endpoints for Copilot content exclusion management +shortTitle: Copilot content exclusion management +intro: 'Use the REST API to manage {% data variables.product.prodname_copilot_short %} content exclusion rules.' +versions: # DO NOT MANUALLY EDIT. CHANGES WILL BE OVERWRITTEN BY A 🤖 + fpt: '*' + ghec: '*' +topics: + - API +autogenerated: rest +allowTitleToDifferFromFilename: true +--- + + diff --git a/content/rest/copilot/copilot-custom-agents.md b/content/rest/copilot/copilot-custom-agents.md new file mode 100644 index 000000000000..94efea67009f --- /dev/null +++ b/content/rest/copilot/copilot-custom-agents.md @@ -0,0 +1,13 @@ +--- +title: REST API endpoints for Copilot custom agents +shortTitle: Copilot custom agents +intro: 'Use the REST API to manage {% data variables.copilot.copilot_custom_agents %} for your enterprise.' +versions: # DO NOT MANUALLY EDIT. CHANGES WILL BE OVERWRITTEN BY A 🤖 + ghec: '*' +topics: + - API +autogenerated: rest +allowTitleToDifferFromFilename: true +--- + + diff --git a/content/rest/copilot/copilot-usage-metrics.md b/content/rest/copilot/copilot-usage-metrics.md index d8caefa952e1..2839fbff374e 100644 --- a/content/rest/copilot/copilot-usage-metrics.md +++ b/content/rest/copilot/copilot-usage-metrics.md @@ -19,4 +19,6 @@ allowTitleToDifferFromFilename: true To enable these endpoints, the "{% data variables.product.prodname_copilot_short %} usage metrics" policy must be set to **Enabled everywhere** for the enterprise. See [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-enterprise-policies#defining-policies-for-your-enterprise). +For more information on the metrics returned by these endpoints, see [AUTOTITLE](/copilot/reference/copilot-usage-metrics). + diff --git a/content/rest/copilot/index.md b/content/rest/copilot/index.md index f6629a016cc8..2816bc36e647 100644 --- a/content/rest/copilot/index.md +++ b/content/rest/copilot/index.md @@ -9,6 +9,8 @@ topics: autogenerated: rest allowTitleToDifferFromFilename: true children: + - /copilot-content-exclusion-management + - /copilot-custom-agents - /copilot-metrics - /copilot-usage-metrics - /copilot-user-management diff --git a/data/reusables/copilot/code-review/custom-instructions-information.md b/data/reusables/copilot/code-review/custom-instructions-information.md index e35af5103f25..87537de05272 100644 --- a/data/reusables/copilot/code-review/custom-instructions-information.md +++ b/data/reusables/copilot/code-review/custom-instructions-information.md @@ -6,6 +6,8 @@ You can also write instructions that {% data variables.product.prodname_copilot_ For more information, see [AUTOTITLE](/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot). +{% data reusables.copilot.code-review.custom-instructions-limit %} + ### Example This example of a `.github/copilot-instructions.md` file contains three instructions that will be applied to all {% data variables.copilot.copilot_code-review_short %}s in the repository. diff --git a/data/reusables/copilot/code-review/custom-instructions-limit.md b/data/reusables/copilot/code-review/custom-instructions-limit.md new file mode 100644 index 000000000000..183daf978c36 --- /dev/null +++ b/data/reusables/copilot/code-review/custom-instructions-limit.md @@ -0,0 +1,2 @@ +> [!NOTE] +> {% data variables.copilot.copilot_code-review_short %} only reads the first 4,000 characters of any custom instruction file. Any instructions beyond this limit will not affect the reviews generated by {% data variables.copilot.copilot_code-review_short %}. This limit does not apply to {% data variables.copilot.copilot_chat_short %} or {% data variables.copilot.copilot_coding_agent %}. diff --git a/data/reusables/security-advisory/private-vulnerability-reporting-configure-notifications.md b/data/reusables/security-advisory/private-vulnerability-reporting-configure-notifications.md index 76c114aed806..581606345512 100644 --- a/data/reusables/security-advisory/private-vulnerability-reporting-configure-notifications.md +++ b/data/reusables/security-advisory/private-vulnerability-reporting-configure-notifications.md @@ -1,4 +1,4 @@ -When a new vulnerability is privately reported on a repository where private vulnerability reporting is enabled, {% data variables.product.github %} notifies repository maintainers and security managers if: +When a new vulnerability is privately reported in a repository, {% data variables.product.github %} notifies repository maintainers and security managers if: * They're watching the repository for all activity. * They have notifications enabled for the repository. diff --git a/data/reusables/security-advisory/repository-level-advisory-note.md b/data/reusables/security-advisory/repository-level-advisory-note.md index fb7f927ad71a..b1080399a812 100644 --- a/data/reusables/security-advisory/repository-level-advisory-note.md +++ b/data/reusables/security-advisory/repository-level-advisory-note.md @@ -1,4 +1,3 @@ > [!NOTE] -> This article applies to editing repository-level advisories as an owner of a public repository. -> -> Users who are not repository owners can contribute to global security advisories in the {% data variables.product.prodname_advisory_database %} at [github.com/advisories](https://github.com/advisories). Edits to global advisories will not change or affect how the advisory appears on the repository. For more information, see [AUTOTITLE](/code-security/security-advisories/working-with-global-security-advisories-from-the-github-advisory-database/editing-security-advisories-in-the-github-advisory-database). +> This article applies to repository-level security advisories in a public repository. +> To edit a global advisory in the {% data variables.product.prodname_advisory_database %}, see [AUTOTITLE](/code-security/security-advisories/working-with-global-security-advisories-from-the-github-advisory-database/editing-security-advisories-in-the-github-advisory-database). diff --git a/data/tables/copilot/model-supported-clients.yml b/data/tables/copilot/model-supported-clients.yml index 13d535e736b8..a5cdba4266b6 100644 --- a/data/tables/copilot/model-supported-clients.yml +++ b/data/tables/copilot/model-supported-clients.yml @@ -168,7 +168,7 @@ - name: GPT-5.3-Codex dotcom: true vscode: true - vs: false + vs: true eclipse: false xcode: false jetbrains: false diff --git a/eslint.config.ts b/eslint.config.ts index 8d367feaa749..bc4f7d6fd7a2 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -193,7 +193,6 @@ export default [ 'src/article-api/scripts/generate-api-docs.ts', 'src/article-api/transformers/audit-logs-transformer.ts', 'src/article-api/transformers/rest-transformer.ts', - 'src/assets/scripts/validate-asset-images.ts', 'src/automated-pipelines/tests/rendering.ts', 'src/codeql-cli/scripts/convert-markdown-for-docs.ts', 'src/content-linter/lib/helpers/get-lintable-yml.ts', @@ -226,27 +225,18 @@ export default [ 'src/content-render/scripts/move-content.ts', 'src/content-render/tests/data.ts', 'src/content-render/tests/link-error-line-numbers.ts', - 'src/content-render/unified/alerts.ts', 'src/content-render/unified/annotate.ts', - 'src/content-render/unified/code-header.ts', - 'src/content-render/unified/copilot-prompt.ts', 'src/content-render/unified/index.ts', 'src/content-render/unified/module-types.d.ts', - 'src/content-render/unified/rewrite-empty-table-rows.ts', - 'src/content-render/unified/rewrite-for-rowheaders.ts', 'src/content-render/unified/rewrite-local-links.ts', 'src/data-directory/lib/data-directory.ts', - 'src/data-directory/lib/data-schemas/features.ts', 'src/data-directory/lib/data-schemas/learning-tracks.ts', 'src/data-directory/lib/get-data.ts', 'src/data-directory/scripts/find-orphaned-features/find.ts', - 'src/dev-toc/generate.ts', 'src/early-access/scripts/migrate-early-access-product.ts', 'src/early-access/scripts/what-docs-early-access-branch.ts', 'src/fixtures/tests/categories-and-subcategory.ts', - 'src/fixtures/tests/glossary.ts', 'src/fixtures/tests/guides.ts', - 'src/fixtures/tests/homepage.ts', 'src/fixtures/tests/liquid.ts', 'src/fixtures/tests/markdown.ts', 'src/fixtures/tests/translations.ts', @@ -267,10 +257,7 @@ export default [ 'src/ghes-releases/scripts/deprecate/update-content.ts', 'src/github-apps/lib/index.ts', 'src/graphql/lib/index.ts', - 'src/graphql/pages/breaking-changes.tsx', - 'src/graphql/pages/changelog.tsx', 'src/graphql/pages/reference.tsx', - 'src/graphql/pages/schema-previews.tsx', 'src/graphql/scripts/build-changelog.ts', 'src/graphql/scripts/utils/process-previews.ts', 'src/graphql/scripts/utils/process-schemas.ts', @@ -285,7 +272,6 @@ export default [ 'src/landings/pages/home.tsx', 'src/landings/pages/product.tsx', 'src/languages/lib/correct-translation-content.ts', - 'src/languages/lib/get-alert-titles.ts', 'src/languages/lib/render-with-fallback.ts', 'src/languages/lib/translation-utils.ts', 'src/learning-track/lib/process-learning-tracks.ts', @@ -324,12 +310,10 @@ export default [ 'src/search/scripts/index/index-cli.ts', 'src/search/scripts/index/utils/indexing-elasticsearch-utils.ts', 'src/search/scripts/scrape/lib/parse-page-sections-into-records.ts', - 'src/search/tests/topics.ts', 'src/tests/helpers/check-url.ts', 'src/tests/helpers/e2etest.ts', 'src/tests/scripts/copy-fixture-data.ts', 'src/tests/vitest.setup.ts', - 'src/tools/components/Picker.tsx', 'src/types/github__markdownlint-github.d.ts', 'src/types/markdownlint-lib-rules.d.ts', 'src/types/markdownlint-rule-helpers.d.ts', diff --git a/package-lock.json b/package-lock.json index 410a504d6878..dc08aa8b1df8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,8 +29,7 @@ "azure-kusto-data": "^7.0.0", "bottleneck": "2.19.5", "boxen": "8.0.1", - "cheerio": "^1.0.0-rc.12", - "cheerio-to-text": "0.2.4", + "cheerio": "^1.2.0", "classnames": "^2.5.1", "clsx": "^2.1.1", "connect-timeout": "1.9.1", @@ -117,7 +116,6 @@ "@octokit/rest": "22.0.0", "@playwright/test": "^1.56", "@types/accept-language-parser": "1.5.7", - "@types/cheerio": "^0.22.35", "@types/connect-timeout": "1.9.0", "@types/cookie": "0.6.0", "@types/cookie-parser": "1.4.8", @@ -310,19 +308,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@axe-core/playwright": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.10.1.tgz", @@ -484,43 +469,45 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz", - "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==", - "peer": true, + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", - "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "license": "MIT", "peer": true, "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.3", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.3", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.3", - "@babel/types": "^7.23.3", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -535,30 +522,26 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true - }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "peer": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.4.tgz", - "integrity": "sha512-esuS49Cga3HcThFNebGhlgsrVLkvhqvYDTzgjfFFlHJcIfLe5jFmRRfCQ1KuBfc4Jrtn3ndLgKWAKjBE+IraYQ==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.23.4", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" @@ -576,14 +559,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "peer": true, + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -595,7 +578,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "peer": true, + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } @@ -604,70 +587,42 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "peer": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "peer": true - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dependencies": { - "@babel/types": "^7.22.5" - }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "peer": true, + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -684,73 +639,53 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "peer": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", - "peer": true, + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", - "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", - "peer": true, + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "license": "MIT", "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.10" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", - "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.26.10" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -785,54 +720,45 @@ } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.4.tgz", - "integrity": "sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg==", - "dependencies": { - "@babel/code-frame": "^7.23.4", - "@babel/generator": "^7.23.4", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.4", - "@babel/types": "^7.23.4", - "debug": "^4.1.0", - "globals": "^11.1.0" + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", - "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1401,9 +1327,9 @@ } }, "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", + "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "license": "ISC", "dependencies": { @@ -1509,9 +1435,9 @@ "license": "MIT" }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", + "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "license": "ISC", "dependencies": { @@ -2310,30 +2236,30 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "engines": { - "node": ">=6.0.0" + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -2345,9 +2271,10 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -2579,6 +2506,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", @@ -2879,6 +2807,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" } @@ -2911,6 +2840,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, @@ -2950,6 +2880,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2970,6 +2901,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2990,6 +2922,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3010,6 +2943,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3030,6 +2964,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3050,6 +2985,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3070,6 +3006,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3090,6 +3027,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3110,6 +3048,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3130,6 +3069,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3150,6 +3090,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3170,6 +3111,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3190,6 +3132,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3207,6 +3150,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": { @@ -3235,6 +3179,7 @@ "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright": "1.56.1" }, @@ -3947,15 +3892,6 @@ "assertion-error": "^2.0.1" } }, - "node_modules/@types/cheerio": { - "version": "0.22.35", - "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.35.tgz", - "integrity": "sha512-yD57BchKRvTV+JD53UZ6PD8KWY5g5rvvMLRnZR3EQBCZXiDT/HR+pKpMzFGlWNhFrXlo7VPZXtKvIEwZkAWOIA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/command-line-args": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.3.tgz", @@ -4058,6 +3994,7 @@ "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", @@ -4219,6 +4156,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.20.tgz", "integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==", "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -4230,6 +4168,7 @@ "integrity": "sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -4400,6 +4339,7 @@ "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.39.1", "@typescript-eslint/types": "8.39.1", @@ -5045,6 +4985,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5075,6 +5016,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", @@ -5602,12 +5544,24 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/braces": { @@ -5640,6 +5594,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001733", "electron-to-chromium": "^1.5.199", @@ -5917,21 +5872,25 @@ } }, "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", "license": "MIT", "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.1.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.19.0", + "whatwg-mimetype": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": ">=20.18.1" }, "funding": { "url": "https://github.com/cheeriojs/cheerio?sponsor=1" @@ -5952,15 +5911,37 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/cheerio-to-text": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/cheerio-to-text/-/cheerio-to-text-0.2.4.tgz", - "integrity": "sha512-/Qpraiyk9FEHG0WJlXkRTkKA60hprTN1eaLk+bhd+Hhv7UtO1AR3kXXLztdVVXu1YdzgoFF7cAKN8ouDuJs6kA==", + "node_modules/cheerio/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", "engines": { - "node": ">=18" + "node": ">=0.12" }, - "peerDependencies": { - "cheerio": "1.0.0-rc.12" + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/undici": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", + "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" } }, "node_modules/chokidar": { @@ -6336,6 +6317,12 @@ "node": ">= 0.6" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", @@ -6897,6 +6884,31 @@ "node": ">= 0.8" } }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/encoding-sniffer/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ensure-posix-path": { "version": "1.1.1", "license": "ISC" @@ -7188,6 +7200,7 @@ "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -7249,6 +7262,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -7524,6 +7538,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7574,9 +7589,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", + "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "license": "ISC", "dependencies": { @@ -7637,10 +7652,11 @@ } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", + "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8023,7 +8039,9 @@ } }, "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", + "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "license": "ISC", "dependencies": { @@ -8666,6 +8684,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": [ @@ -8720,8 +8739,9 @@ }, "node_modules/gensync": { "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -8848,27 +8868,6 @@ "node": ">= 6" } }, - "node_modules/glob/node_modules/balanced-match": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz", - "integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==", - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", - "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/glob/node_modules/minimatch": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", @@ -8943,6 +8942,7 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", "dev": true, + "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -9390,9 +9390,9 @@ } }, "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -9404,14 +9404,14 @@ "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "domutils": "^3.2.2", + "entities": "^7.0.1" } }, "node_modules/htmlparser2/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -10317,6 +10317,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" } @@ -10361,13 +10362,15 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-bignum": { @@ -10442,8 +10445,9 @@ }, "node_modules/json5": { "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "license": "MIT", - "peer": true, "bin": { "json5": "lib/cli.js" }, @@ -11086,9 +11090,9 @@ } }, "node_modules/matcher-collection/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", + "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -12057,13 +12061,13 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", + "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -12655,6 +12659,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 }, @@ -12712,7 +12717,9 @@ } }, "node_modules/nodemon/node_modules/minimatch": { - "version": "3.1.2", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", + "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "license": "ISC", "dependencies": { @@ -13148,10 +13155,24 @@ } }, "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", "license": "MIT", "dependencies": { - "domhandler": "^5.0.2", "parse5": "^7.0.0" }, "funding": { @@ -13411,6 +13432,7 @@ "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "playwright-core": "cli.js" }, @@ -13474,6 +13496,7 @@ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -13643,6 +13666,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -13663,6 +13687,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -13687,9 +13712,10 @@ } }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "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", "peer": true }, "node_modules/react-markdown": { @@ -14130,29 +14156,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/balanced-match": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz", - "integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", - "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/rimraf/node_modules/glob": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", @@ -14394,6 +14397,7 @@ "integrity": "sha512-d0NoFH4v6SjEK7BoX810Jsrhj7IQSYHAHLi/iSpgqKc7LaIDshFRlSg5LOymf9FqQhxEHs2W5ZQXlvy0KD45Uw==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -15287,6 +15291,7 @@ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.0.0", "@babel/traverse": "^7.4.5", @@ -15545,6 +15550,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -15869,6 +15875,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16223,6 +16230,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.2.2" }, @@ -16425,6 +16433,7 @@ "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -16533,6 +16542,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -16726,27 +16736,6 @@ "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "license": "MIT" }, - "node_modules/walk-sync/node_modules/balanced-match": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz", - "integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==", - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/walk-sync/node_modules/brace-expansion": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", - "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/walk-sync/node_modules/minimatch": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", @@ -16790,6 +16779,41 @@ "node": ">=14.14" } }, + "node_modules/website-scraper/node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/website-scraper/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/website-scraper/node_modules/got": { "version": "12.6.1", "dev": true, @@ -16814,6 +16838,26 @@ "url": "https://github.com/sindresorhus/got?sponsor=1" } }, + "node_modules/website-scraper/node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/website-scraper/node_modules/normalize-url": { "version": "7.2.0", "dev": true, @@ -16825,6 +16869,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "dev": true, @@ -17066,6 +17144,12 @@ "node": ">=0.4" } }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, "node_modules/yaml": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", diff --git a/package.json b/package.json index be7db1ba3bca..37d6c0afed8b 100644 --- a/package.json +++ b/package.json @@ -179,8 +179,7 @@ "azure-kusto-data": "^7.0.0", "bottleneck": "2.19.5", "boxen": "8.0.1", - "cheerio": "^1.0.0-rc.12", - "cheerio-to-text": "0.2.4", + "cheerio": "^1.2.0", "classnames": "^2.5.1", "clsx": "^2.1.1", "connect-timeout": "1.9.1", @@ -267,7 +266,6 @@ "@octokit/rest": "22.0.0", "@playwright/test": "^1.56", "@types/accept-language-parser": "1.5.7", - "@types/cheerio": "^0.22.35", "@types/connect-timeout": "1.9.0", "@types/cookie": "0.6.0", "@types/cookie-parser": "1.4.8", diff --git a/src/assets/scripts/validate-asset-images.ts b/src/assets/scripts/validate-asset-images.ts index 9e91bd86c0cb..f799bee711c1 100755 --- a/src/assets/scripts/validate-asset-images.ts +++ b/src/assets/scripts/validate-asset-images.ts @@ -16,7 +16,7 @@ import path from 'path' import { program } from 'commander' import chalk from 'chalk' -import cheerio from 'cheerio' +import { load } from 'cheerio' import { fileTypeFromFile } from 'file-type' import walk from 'walk-sync' import isSVG from 'is-svg' @@ -115,8 +115,8 @@ async function checkFile(filePath: string) { } try { checkSVGContent(content) - } catch (error: any) { - return [CRITICAL, filePath, error.message] + } catch (error: unknown) { + return [CRITICAL, filePath, error instanceof Error ? error.message : String(error)] } } else if (EXPECT[ext]) { const fileType = await fileTypeFromFile(filePath) @@ -138,14 +138,16 @@ async function checkFile(filePath: string) { } function checkSVGContent(content: string) { - const $ = cheerio.load(content) + const $ = load(content) const disallowedTagNames = new Set(['script', 'object', 'iframe', 'embed']) $('*').each((i, element) => { - const { tagName } = $(element).get(0) + const el = $(element).get(0) + if (!el || !('tagName' in el)) return + const { tagName } = el if (disallowedTagNames.has(tagName)) { throw new Error(`contains a <${tagName}> tag`) } - for (const key in $(element).get(0).attribs) { + for (const key in 'attribs' in el ? el.attribs : {}) { // Looks for suspicious event handlers on tags. // For example ` { 1. item three`) const html = await renderContent(template) - const $ = cheerio.load(html, { xmlMode: true }) + const $ = load(html, { xmlMode: true }) expect($('ol').length).toBe(1) expect($('ol > li').length).toBe(3) }) @@ -43,7 +43,7 @@ describe('renderContent', () => { - item`) const html = await renderContent(template) - const $ = cheerio.load(html, { xmlMode: true }) + const $ = load(html, { xmlMode: true }) expect($('ul p').length).toBe(0) }) @@ -69,7 +69,7 @@ describe('renderContent', () => { |g i | Go to the **Issues** tab. For more information, see "[About issues](/articles/about-issues)." `) const html = await renderContent(template) - const $ = cheerio.load(html, { xmlMode: true }) + const $ = load(html, { xmlMode: true }) expect( $.html().includes('"About issues."'), ).toBeTruthy() @@ -82,7 +82,7 @@ describe('renderContent', () => { | Python | \`requirements.txt\`, \`pipfile.lock\` `) const html = await renderContent(template) - const $ = cheerio.load(html, { xmlMode: true }) + const $ = load(html, { xmlMode: true }) expect( $.html().includes('requirements.txt, pipfile.lock'), ).toBeTruthy() @@ -95,7 +95,7 @@ describe('renderContent', () => { | user:USERNAME | [**user:defunkt ubuntu**](https://github.com/search?q=user%3Adefunkt+ubuntu&type=Issues) matches issues with the word "ubuntu" from repositories owned by @defunkt. `) const html = await renderContent(template) - const $ = cheerio.load(html, { xmlMode: true }) + const $ = load(html, { xmlMode: true }) expect($.html().includes('user:USERNAME')).toBeTruthy() }) @@ -110,7 +110,7 @@ describe('renderContent', () => { 1. This is another list item. `) const html = await renderContent(template) - const $ = cheerio.load(html, { xmlMode: true }) + const $ = load(html, { xmlMode: true }) expect($('ol').length).toBe(1) expect($.html().includes('# { ##### This is a level 5 `) const html = await renderContent(template) - const $ = cheerio.load(html, { xmlMode: true }) + const $ = load(html, { xmlMode: true }) for (const level of [1, 2, 3, 4, 5]) { expect( @@ -147,7 +147,7 @@ const example = true \`\`\`\` `) let html = await renderContent(template) - let $ = cheerio.load(html, { xmlMode: true }) + let $ = load(html, { xmlMode: true }) expect($.html().includes('
')).toBeTruthy()
     expect($.html().includes('const')).toBeTruthy()
 
@@ -157,7 +157,7 @@ const example = true
 \`\`\`\`
     `)
     html = await renderContent(template)
-    $ = cheerio.load(html, { xmlMode: true })
+    $ = load(html, { xmlMode: true })
     expect($.html().includes('
')).toBeTruthy()
     expect($.html().includes('@articles')).toBeTruthy()
 
@@ -167,7 +167,7 @@ POST / HTTP/2
 \`\`\`\`
     `)
     html = await renderContent(template)
-    $ = cheerio.load(html, { xmlMode: true })
+    $ = load(html, { xmlMode: true })
     expect($.html().includes('
')).toBeTruthy()
     expect($.html().includes('POST')).toBeTruthy()
 
@@ -180,7 +180,7 @@ plugins {
 \`\`\`\`
     `)
     html = await renderContent(template)
-    $ = cheerio.load(html, { xmlMode: true })
+    $ = load(html, { xmlMode: true })
     expect($.html().includes('
')).toBeTruthy()
     expect(
       $.html().includes(''maven-publish''),
@@ -192,7 +192,7 @@ FROM alpine:3.10
 \`\`\`\`
     `)
     html = await renderContent(template)
-    $ = cheerio.load(html, { xmlMode: true })
+    $ = load(html, { xmlMode: true })
     expect($.html().includes('
')).toBeTruthy()
     expect($.html().includes('FROM')).toBeTruthy()
 
@@ -202,7 +202,7 @@ $resourceGroupName = "octocat-testgroup"
 \`\`\`\`
     `)
     html = await renderContent(template)
-    $ = cheerio.load(html, { xmlMode: true })
+    $ = load(html, { xmlMode: true })
     expect($.html().includes('
')).toBeTruthy()
     expect(
       $.html().includes('$resourceGroupName'),
@@ -216,7 +216,7 @@ var a = 1
 \`\`\`
     `)
     const html = await renderContent(template)
-    const $ = cheerio.load(html, { xmlMode: true })
+    const $ = load(html, { xmlMode: true })
     expect($.html().includes('var a = 1')).toBeTruthy()
   })
 
@@ -238,7 +238,7 @@ var a = 1
 \`\`\`
     `)
     const html = await renderContent(template)
-    const $ = cheerio.load(html)
+    const $ = load(html)
     const el = $('button.js-btn-copy')
     expect(el.data('clipboard')).toBe(2967273189)
     // Generates a murmurhash based ID that matches a 
@@ -250,7 +250,7 @@ var a = 1
 > This is a note with a [link](https://example.com)
     `)
     const html = await renderContent(template, { alertTitles: { NOTE: 'Note' } })
-    const $ = cheerio.load(html)
+    const $ = load(html)
     const alertEl = $('.ghd-alert')
     expect(alertEl.length).toBe(1)
     expect(alertEl.attr('data-container')).toBe('alert')
diff --git a/src/content-render/tests/table-accessibility-labels.ts b/src/content-render/tests/table-accessibility-labels.ts
index 84dd119f4c8f..e17e246cf096 100644
--- a/src/content-render/tests/table-accessibility-labels.ts
+++ b/src/content-render/tests/table-accessibility-labels.ts
@@ -1,4 +1,4 @@
-import cheerio from 'cheerio'
+import { load } from 'cheerio'
 import { describe, expect, test } from 'vitest'
 
 import { renderContent } from '@/content-render/index'
@@ -20,7 +20,7 @@ describe('table accessibility labels', () => {
 `)
 
     const html = await renderContent(template)
-    const $ = cheerio.load(html)
+    const $ = load(html)
 
     const table = $('table')
     expect(table.length).toBe(1)
@@ -42,7 +42,7 @@ describe('table accessibility labels', () => {
 `)
 
     const html = await renderContent(template)
-    const $ = cheerio.load(html)
+    const $ = load(html)
 
     const table = $('table')
     expect(table.attr('aria-labelledby')).toBe('configuration-options')
@@ -59,7 +59,7 @@ describe('table accessibility labels', () => {
 `)
 
     const html = await renderContent(template)
-    const $ = cheerio.load(html)
+    const $ = load(html)
 
     const table = $('table')
     expect(table.attr('aria-label')).toBe('Pre-labeled table')
@@ -78,7 +78,7 @@ describe('table accessibility labels', () => {
 `)
 
     const html = await renderContent(template)
-    const $ = cheerio.load(html)
+    const $ = load(html)
 
     const table = $('table')
     expect(table.find('caption').text()).toBe('Existing caption')
@@ -101,7 +101,7 @@ describe('table accessibility labels', () => {
 `)
 
     const html = await renderContent(template)
-    const $ = cheerio.load(html)
+    const $ = load(html)
 
     const tables = $('table')
     expect(tables.length).toBe(2)
@@ -123,7 +123,7 @@ Some text here.
 `)
 
     const html = await renderContent(template)
-    const $ = cheerio.load(html)
+    const $ = load(html)
 
     const tables = $('table')
     expect(tables.length).toBe(2)
@@ -145,7 +145,7 @@ Some additional context here.
 `)
 
     const html = await renderContent(template)
-    const $ = cheerio.load(html)
+    const $ = load(html)
 
     const table = $('table')
     expect(table.attr('aria-labelledby')).toBe('data-table')
@@ -165,7 +165,7 @@ Some additional context here.
 `)
 
     const html = await renderContent(template)
-    const $ = cheerio.load(html)
+    const $ = load(html)
 
     const tables = $('table')
     expect(tables.length).toBe(2)
@@ -185,7 +185,7 @@ Some additional context here.
 `)
 
     const html = await renderContent(template)
-    const $ = cheerio.load(html)
+    const $ = load(html)
 
     const table = $('table')
     expect(table.attr('aria-labelledby')).toBe('supported-github-actions-features')
@@ -201,7 +201,7 @@ Some additional context here.
 `)
 
     const html = await renderContent(template)
-    const $ = cheerio.load(html)
+    const $ = load(html)
 
     const table = $('table')
     expect(table.find('thead th').length).toBe(2)
diff --git a/src/content-render/unified/alerts.ts b/src/content-render/unified/alerts.ts
index d354a774736d..61dc11d9961d 100644
--- a/src/content-render/unified/alerts.ts
+++ b/src/content-render/unified/alerts.ts
@@ -5,7 +5,7 @@ Custom "Alerts", based on similar filter/styling in the monolith code.
 import { visit } from 'unist-util-visit'
 import { h } from 'hastscript'
 import octicons from '@primer/octicons'
-import type { Element } from 'hast'
+import type { Element, Root, ElementContent } from 'hast'
 
 interface AlertType {
   icon: string
@@ -22,36 +22,33 @@ const alertTypes: Record = {
 
 // Must contain one of [!NOTE], [!IMPORTANT], ...
 const ALERT_REGEXP = new RegExp(`\\[!(${Object.keys(alertTypes).join('|')})\\]`, 'gi')
-
-// Using any due to conflicting unist/hast type definitions between dependencies
-const matcher = (node: any): boolean =>
-  node.type === 'element' &&
-  node.tagName === 'blockquote' &&
-  ALERT_REGEXP.test(JSON.stringify(node.children))
+// Non-global version for .test() and .match() to avoid stateful lastIndex issues
+const ALERT_REGEXP_DETECT = new RegExp(`\\[!(${Object.keys(alertTypes).join('|')})\\]`, 'i')
 
 export default function alerts({ alertTitles = {} }: { alertTitles?: Record }) {
-  // Using any due to conflicting unist/hast type definitions between dependencies
-  return (tree: any) => {
-    // Using any due to conflicting unist/hast type definitions between dependencies
-    visit(tree, matcher, (node: any) => {
-      const key = getAlertKey(node)
+  return (tree: Root) => {
+    visit(tree, 'element', (node) => {
+      const el = node as Element
+      if (el.tagName !== 'blockquote' || !ALERT_REGEXP_DETECT.test(JSON.stringify(el.children)))
+        return
+      const key = getAlertKey(el)
       if (!(key in alertTypes)) {
         console.warn(
           `Alert key '${key}' should be all uppercase (change it to '${key.toUpperCase()}')`,
         )
       }
-      const alertType = alertTypes[getAlertKey(node).toUpperCase()]
-      node.tagName = 'div'
-      node.properties.className = `ghd-alert ghd-alert-${alertType.color}`
-      node.properties.dataContainer = 'alert'
-      node.children = [
+      const alertType = alertTypes[getAlertKey(el).toUpperCase()]
+      el.tagName = 'div'
+      el.properties.className = `ghd-alert ghd-alert-${alertType.color}`
+      el.properties.dataContainer = 'alert'
+      el.children = [
         h(
           'p',
           { className: 'ghd-alert-title' },
           getOcticonSVG(alertType.icon),
           alertTitles[key] || '',
         ),
-        ...removeAlertSyntax(node.children),
+        ...removeAlertSyntax(el.children),
       ]
     })
   }
@@ -59,19 +56,22 @@ export default function alerts({ alertTitles = {} }: { alertTitles?: Record removeAlertSyntax(n))
   }
-  if (node.children) {
-    node.children = node.children.map(removeAlertSyntax)
+  if ('children' in node) {
+    node.children = node.children.map((n) => removeAlertSyntax(n)) as typeof node.children
   }
-  if (node.value) {
+  if ('value' in node) {
     node.value = node.value.replace(ALERT_REGEXP, '')
   }
   return node
diff --git a/src/content-render/unified/code-header.ts b/src/content-render/unified/code-header.ts
index f9e0c13743e3..a2ef46ff2f37 100644
--- a/src/content-render/unified/code-header.ts
+++ b/src/content-render/unified/code-header.ts
@@ -13,44 +13,40 @@ import { fromParse5 } from 'hast-util-from-parse5'
 import murmur from 'imurmurhash'
 import { getPrompt } from './copilot-prompt'
 import { generatePromptId } from '../lib/prompt-id'
-import type { Element } from 'hast'
+import type { Element, Root } from 'hast'
 
 interface LanguageConfig {
   name: string
-  // Using any for language properties that can vary (aliases, extensions, etc.)
-  [key: string]: any
+  [key: string]: string | string[] | boolean | undefined
 }
 
 type Languages = Record
 
 const languages = yaml.load(fs.readFileSync('./data/code-languages.yml', 'utf8')) as Languages
 
-// Using any due to conflicting unist/hast type definitions between dependencies
-const matcher = (node: any): boolean =>
-  node.type === 'element' &&
-  node.tagName === 'pre' &&
-  // For now, limit to ones with the copy or prompt meta,
-  // but we may enable for all examples later.
-  (getPreMeta(node).copy || getPreMeta(node).prompt) &&
-  // Don't add this header for annotated examples.
-  !getPreMeta(node).annotate
-
 export default function codeHeader() {
-  // Using any due to conflicting unist/hast type definitions between dependencies
-  return (tree: any) => {
-    // Using any due to conflicting unist/hast type definitions between dependencies
-    visit(tree, matcher, (node: any, index: number | undefined, parent: any) => {
-      if (index !== undefined && parent) {
-        parent.children[index] = wrapCodeExample(node, tree)
+  return (tree: Root) => {
+    visit(tree, 'element', (node, index, parent) => {
+      const el = node as Element
+      if (
+        el.tagName !== 'pre' ||
+        !(getPreMeta(el).copy || getPreMeta(el).prompt) ||
+        getPreMeta(el).annotate
+      )
+        return
+      if (index !== undefined && parent && 'children' in parent) {
+        ;(parent as Element).children[index] = wrapCodeExample(el, tree)
       }
     })
   }
 }
 
-// Using any due to conflicting unist/hast type definitions between dependencies
-function wrapCodeExample(node: any, tree: any): Element {
-  const lang: string = node.children[0].properties.className?.[0].replace('language-', '')
-  const code: string = node.children[0].children[0].value
+function wrapCodeExample(node: Element, tree: Root): Element {
+  const codeChild = node.children[0] as Element
+  const classNames = codeChild.properties.className as string[] | undefined
+  const lang: string = classNames?.[0]?.replace('language-', '') ?? ''
+  const textNode = codeChild.children[0] as { value: string }
+  const code: string = textNode.value
 
   const subnav = null // getSubnav() lives in annotate.ts, not needed for normal code blocks
   const hasPrompt: boolean = Boolean(getPreMeta(node).prompt)
@@ -120,16 +116,14 @@ export function header(
 function btnIcon(): Element {
   const btnIconHtml: string = octicons.copy.toSVG()
   const btnIconAst = parse(String(btnIconHtml), { sourceCodeLocationInfo: true })
-  // Using any because fromParse5 expects VFile but we only have a string
-  // This is safe because parse5 only needs the string content
-  const btnIconElement = fromParse5(btnIconAst, { file: btnIconHtml as any })
+  const btnIconElement = fromParse5(btnIconAst)
   return btnIconElement as Element
 }
 
-// Using any due to conflicting unist/hast type definitions between dependencies
-// node can be various mdast/hast node types, return value contains meta properties from code blocks
-export function getPreMeta(node: any): Record {
+// node can be various hast element types, return value contains meta properties from code blocks
+export function getPreMeta(node: Element): Record {
   // Here's why this monstrosity works:
   // https://github.com/syntax-tree/mdast-util-to-hast/blob/c87cd606731c88a27dbce4bfeaab913a9589bf83/lib/handlers/code.js#L40-L42
-  return node.children[0]?.data?.meta || {}
+  const firstChild = node.children[0] as Element | undefined
+  return (firstChild?.data as Record> | undefined)?.meta || {}
 }
diff --git a/src/content-render/unified/copilot-prompt.ts b/src/content-render/unified/copilot-prompt.ts
index 19d03cbf57e5..36a2554a87b8 100644
--- a/src/content-render/unified/copilot-prompt.ts
+++ b/src/content-render/unified/copilot-prompt.ts
@@ -9,14 +9,13 @@ import { parse } from 'parse5'
 import { fromParse5 } from 'hast-util-from-parse5'
 import { getPreMeta } from './code-header'
 import { generatePromptId } from '../lib/prompt-id'
+import type { Element, Root } from 'hast'
 
-// node and tree are hast/unist AST nodes without proper TypeScript definitions
-// Returns an object with the prompt button element and the full prompt content
 export function getPrompt(
-  node: any,
-  tree: any,
+  node: Element,
+  tree: Root,
   code: string,
-): { element: any; promptContent: string } | null {
+): { element: Element; promptContent: string } | null {
   const hasPrompt = Boolean(getPreMeta(node).prompt)
   if (!hasPrompt) return null
 
@@ -40,11 +39,9 @@ export function getPrompt(
   return { element, promptContent }
 }
 
-// Using any because node and tree are hast/unist AST nodes without proper TypeScript definitions
-// node is the current code block element, tree is used to find referenced code blocks
 function buildPromptData(
-  node: any,
-  tree: any,
+  node: Element,
+  tree: Root,
   code: string,
 ): { promptContent: string; ariaLabel: string } {
   // Find a ref meta in the format 'ref='
@@ -56,14 +53,18 @@ function buildPromptData(
   }
 
   // If the 'ref=' meta is found, find a matching code block to include as context in the prompt link.
-  const matchingCodeEl = findMatchingCode(ref, tree)
+  const matchingCodeEl = findMatchingCode(ref as string, tree)
   if (!matchingCodeEl) {
     console.warn(`Can't find referenced code block with id=${ref}`)
     return promptOnly(code)
   }
-  // Using any to access children property on untyped hast element node
   // AST structure: element -> code -> text node with value property
-  const matchingCode = (matchingCodeEl as any)?.children[0].children[0].value || null
+  const codeChild = matchingCodeEl.children[0] as Element | undefined
+  const textNode = codeChild?.children[0] as { value?: string } | undefined
+  const matchingCode = textNode?.value || null
+  if (!matchingCode) {
+    return promptOnly(code)
+  }
   return promptAndContext(code, matchingCode)
 }
 
@@ -84,21 +85,17 @@ function promptAndContext(
   }
 }
 
-// Using any because tree and node are hast/unist AST nodes without proper TypeScript definitions
-// Searches the AST tree for a code block with matching id in meta
-function findMatchingCode(ref: string, tree: any): any {
-  return find(tree, (node: any) => {
-    // Using any to access tagName property on untyped hast element node
-    return node.type === 'element' && (node as any).tagName === 'pre' && getPreMeta(node).id === ref
-  })
+function findMatchingCode(ref: string, tree: Root): Element | undefined {
+  return find(tree, ((node: { type: string; tagName?: string }) => {
+    return (
+      node.type === 'element' && node.tagName === 'pre' && getPreMeta(node as Element).id === ref
+    )
+  }) as Parameters[1])
 }
 
-// Returns a hast element node for the Copilot icon
-// Using any return type because fromParse5 returns untyped hast nodes
-function copilotIcon(): any {
+function copilotIcon(): Element {
   const copilotIconHtml = octicons.copilot.toSVG()
   const copilotIconAst = parse(String(copilotIconHtml), { sourceCodeLocationInfo: true })
-  // Using any because fromParse5 expects VFile but we only have a string
-  const copilotIconElement = fromParse5(copilotIconAst, { file: copilotIconHtml as any })
-  return copilotIconElement
+  const copilotIconElement = fromParse5(copilotIconAst)
+  return copilotIconElement as Element
 }
diff --git a/src/content-render/unified/rewrite-empty-table-rows.ts b/src/content-render/unified/rewrite-empty-table-rows.ts
index d10e95e875c5..997fbecb963d 100644
--- a/src/content-render/unified/rewrite-empty-table-rows.ts
+++ b/src/content-render/unified/rewrite-empty-table-rows.ts
@@ -1,4 +1,5 @@
 import { visit, SKIP } from 'unist-util-visit'
+import type { Element, Root } from 'hast'
 
 /**
  * Where it can mutate the AST to swap from:
@@ -49,31 +50,23 @@ import { visit, SKIP } from 'unist-util-visit'
  * isn't the same all the way down. But Unified will still parse it.
  * */
 
-// node is a hast element node without proper TypeScript definitions
-function matcher(node: any): boolean {
-  return node.type === 'element' && node.tagName === 'tr'
-}
-
-// node, parent, and grandChild are hast element nodes without proper TypeScript definitions
-function visitor(
-  node: any,
-  index: number | undefined,
-  parent: any,
-): [typeof SKIP, number] | undefined {
-  if (
-    node.children.every(
-      (grandChild: any) =>
-        grandChild.type === 'element' && grandChild.tagName === 'td' && !grandChild.children.length,
-    )
-  ) {
-    if (index !== undefined) {
-      parent.children.splice(index, 1)
-      return [SKIP, index]
-    }
-  }
-}
-
-// tree is a hast root node without proper TypeScript definitions
 export default function rewriteEmptyTableRows() {
-  return (tree: any) => visit(tree, matcher, visitor)
+  return (tree: Root) =>
+    visit(tree, 'element', (node, index, parent) => {
+      const el = node as Element
+      if (el.tagName !== 'tr') return
+      if (
+        el.children.every(
+          (grandChild) =>
+            grandChild.type === 'element' &&
+            grandChild.tagName === 'td' &&
+            !grandChild.children.length,
+        )
+      ) {
+        if (index !== undefined && parent) {
+          parent.children.splice(index, 1)
+          return [SKIP, index] as const
+        }
+      }
+    })
 }
diff --git a/src/content-render/unified/rewrite-for-rowheaders.ts b/src/content-render/unified/rewrite-for-rowheaders.ts
index f86e1d7162dc..c547e8c5c801 100644
--- a/src/content-render/unified/rewrite-for-rowheaders.ts
+++ b/src/content-render/unified/rewrite-for-rowheaders.ts
@@ -1,21 +1,10 @@
 import { visitParents } from 'unist-util-visit-parents'
+import type { Element, Root } from 'hast'
 
-interface ElementNode {
-  type: 'element'
-  tagName: string
-  properties: {
-    // Properties can have any value type (strings, booleans, arrays, etc.)
-    [key: string]: any
-  }
+interface ScopedElement extends Element {
   _scoped?: boolean
 }
 
-interface AncestorNode {
-  properties?: {
-    className?: string[]
-  }
-}
-
 /**
  * Where it can mutate the AST to swap from:
  *
@@ -49,32 +38,28 @@ interface AncestorNode {
  *
  * */
 
-function matcher(node: any): node is ElementNode {
-  return node.type === 'element' && node.tagName === 'td' && !('scope' in node.properties)
-}
-
-function insideRowheaders(ancestors: AncestorNode[]): boolean {
-  return ancestors.some(
-    (node: AncestorNode) =>
-      node.properties &&
-      node.properties.className &&
-      node.properties.className.includes('rowheaders'),
-  )
-}
+export default function rewriteForRowheaders() {
+  return (tree: Root) =>
+    visitParents(tree, 'element', (node, ancestors) => {
+      const el = node as Element
+      if (el.tagName !== 'td' || 'scope' in el.properties) return
 
-// ancestors is an array of hast nodes without proper TypeScript definitions
-function visitor(node: ElementNode, ancestors: any[]): void {
-  if (insideRowheaders(ancestors)) {
-    const tr = ancestors.at(-1) as ElementNode
-    if (!tr._scoped) {
-      tr._scoped = true
-      node.properties.scope = 'row'
-      node.tagName = 'th'
-    }
-  }
-}
+      const insideRowheaders = ancestors.some((ancestor) => {
+        const ancestorEl = ancestor as Partial
+        return (
+          ancestorEl.properties &&
+          Array.isArray(ancestorEl.properties.className) &&
+          ancestorEl.properties.className.includes('rowheaders')
+        )
+      })
 
-// tree is a hast root node without proper TypeScript definitions
-export default function rewriteForRowheaders() {
-  return (tree: any) => visitParents(tree, matcher, visitor)
+      if (insideRowheaders) {
+        const tr = ancestors.at(-1) as ScopedElement
+        if (!tr._scoped) {
+          tr._scoped = true
+          el.properties.scope = 'row'
+          el.tagName = 'th'
+        }
+      }
+    })
 }
diff --git a/src/content-render/unified/text-only.ts b/src/content-render/unified/text-only.ts
index 6488b1f90593..83ce950a5b0a 100644
--- a/src/content-render/unified/text-only.ts
+++ b/src/content-render/unified/text-only.ts
@@ -1,4 +1,4 @@
-import cheerio from 'cheerio'
+import { load } from 'cheerio'
 import { decode } from 'html-entities'
 
 // Given a piece of HTML return it without HTML. E.g.
@@ -13,6 +13,6 @@ export function fastTextOnly(html: string): string {
     const middle = html.slice(3, -4)
     if (!middle.includes('<')) return decode(middle.trim())
   }
-  const $ = cheerio.load(html, { xmlMode: true })
+  const $ = load(html, { xmlMode: true })
   return $.root().text().trim()
 }
diff --git a/src/data-directory/lib/data-schemas/features.ts b/src/data-directory/lib/data-schemas/features.ts
index 8cf101ff16b1..1b9aa310350b 100644
--- a/src/data-directory/lib/data-schemas/features.ts
+++ b/src/data-directory/lib/data-schemas/features.ts
@@ -1,9 +1,16 @@
 import { schema } from '@/frame/lib/frontmatter'
 
+interface FeatureVersionsProperties {
+  type?: string | string[]
+  properties?: Record
+  additionalProperties?: boolean
+  [key: string]: unknown
+}
+
 interface FeatureVersionsSchema {
   type: 'object'
   properties: {
-    versions: any
+    versions: FeatureVersionsProperties
   }
   additionalProperties: false
 }
@@ -12,13 +19,14 @@ interface FeatureVersionsSchema {
 const featureVersions: FeatureVersionsSchema = {
   type: 'object',
   properties: {
-    versions: Object.assign({}, (schema.properties as any).versions),
+    versions: Object.assign({}, schema.properties.versions) as FeatureVersionsProperties,
   },
   additionalProperties: false,
 }
 
 // Remove the feature versions properties.
 // We don't want to allow features within features! We just want pure versioning.
-delete featureVersions.properties.versions.properties.feature
+delete (featureVersions.properties.versions.properties as Record | undefined)
+  ?.feature
 
 export default featureVersions
diff --git a/src/dev-toc/generate.ts b/src/dev-toc/generate.ts
index f836d8afa282..5bc80594a1ce 100644
--- a/src/dev-toc/generate.ts
+++ b/src/dev-toc/generate.ts
@@ -15,7 +15,7 @@ import path from 'path'
 import { execSync } from 'child_process'
 import { program } from 'commander'
 import type { NextFunction, Response } from 'express'
-import type { ExtendedRequest } from '@/types'
+import type { ExtendedRequest, Context } from '@/types'
 import fpt from '@/versions/lib/non-enterprise-default-version'
 import { allVersionKeys } from '@/versions/lib/all-versions'
 import { liquid } from '@/content-render/index'
@@ -60,10 +60,16 @@ async function main(): Promise {
     get: () => '',
     header: () => '',
     accepts: () => false,
-    context: {} as any,
+    context: {} as Context,
   } as unknown as ExtendedRequest
 
-  async function recurse(tree: any): Promise {
+  interface PageTreeNode {
+    page: { rawTitle: string }
+    renderedFullTitle?: string
+    childPages?: PageTreeNode[]
+  }
+
+  async function recurse(tree: PageTreeNode): Promise {
     const { page } = tree
     tree.renderedFullTitle = page.rawTitle.includes('{')
       ? await liquid.parseAndRender(page.rawTitle, req.context)
@@ -92,7 +98,7 @@ async function main(): Promise {
     }
 
     if (req.context && req.context.currentEnglishTree) {
-      await recurse(req.context.currentEnglishTree)
+      await recurse(req.context.currentEnglishTree as PageTreeNode)
     }
 
     // Add any defaultOpenSections to the context.
diff --git a/src/fixtures/tests/annotations.ts b/src/fixtures/tests/annotations.ts
index 9a87ca2401ac..87f35190137e 100644
--- a/src/fixtures/tests/annotations.ts
+++ b/src/fixtures/tests/annotations.ts
@@ -1,11 +1,11 @@
 import { describe, expect, test } from 'vitest'
-import cheerio from 'cheerio'
+import type { CheerioAPI } from 'cheerio'
 
 import { getDOM } from '@/tests/helpers/e2etest'
 
 describe('annotations', () => {
   test('code-snippet-with-hashbang', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/foo/code-snippet-with-hashbang')
+    const $: CheerioAPI = await getDOM('/get-started/foo/code-snippet-with-hashbang')
     const annotations = $('#article-contents .annotate')
 
     // Check http://localhost:4000/en/get-started/foo/code-snippet-with-hashbang
diff --git a/src/fixtures/tests/breadcrumbs.ts b/src/fixtures/tests/breadcrumbs.ts
index 66eb05784a80..9356ec762215 100644
--- a/src/fixtures/tests/breadcrumbs.ts
+++ b/src/fixtures/tests/breadcrumbs.ts
@@ -1,5 +1,7 @@
 import { describe, expect, test } from 'vitest'
 
+import type { Element } from 'domhandler'
+
 import { getDOM } from '@/tests/helpers/e2etest'
 
 describe('breadcrumbs', () => {
@@ -68,7 +70,7 @@ describe('breadcrumbs', () => {
 
     expect($breadcrumbTitles.length).toBe(0)
     expect($breadcrumbLinks.length).toBe(2)
-    expect(($breadcrumbLinks[0] as cheerio.TagElement).attribs.title).toBe('Deeper secrets')
-    expect(($breadcrumbLinks[1] as cheerio.TagElement).attribs.title).toBe('Mariana Trench')
+    expect(($breadcrumbLinks[0] as Element).attribs.title).toBe('Deeper secrets')
+    expect(($breadcrumbLinks[1] as Element).attribs.title).toBe('Mariana Trench')
   })
 })
diff --git a/src/fixtures/tests/categories-and-subcategory.ts b/src/fixtures/tests/categories-and-subcategory.ts
index 3d16eb951a6a..01fb0a629124 100644
--- a/src/fixtures/tests/categories-and-subcategory.ts
+++ b/src/fixtures/tests/categories-and-subcategory.ts
@@ -1,11 +1,11 @@
 import { describe, expect, test } from 'vitest'
-import cheerio from 'cheerio'
+import type { CheerioAPI } from 'cheerio'
 
 import { getDOM, head } from '@/tests/helpers/e2etest'
 
 describe('subcategories', () => {
   test('get-started/start-your-journey subcategory', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/start-your-journey')
+    const $: CheerioAPI = await getDOM('/get-started/start-your-journey')
     const lead = $('[data-search=lead]').text()
     expect(lead).toMatch('Get started using HubGit to manage Git repositories')
 
@@ -22,7 +22,7 @@ describe('subcategories', () => {
   })
 
   test('actions/category/subcategory subcategory has its articles intro', async () => {
-    const $: cheerio.Root = await getDOM('/actions/category/subcategory')
+    const $: CheerioAPI = await getDOM('/actions/category/subcategory')
     const lead = $('[data-search=lead]').text()
     expect(lead).toMatch("Here's the intro for HubGit Actions.")
 
@@ -43,7 +43,7 @@ describe('subcategories', () => {
 
 describe('categories', () => {
   test('actions/category subcategory', async () => {
-    const $: cheerio.Root = await getDOM('/actions/category')
+    const $: CheerioAPI = await getDOM('/actions/category')
     const lead = $('[data-search=lead]').text()
     expect(lead).toMatch('Learn how to migrate your existing CI/CD')
 
diff --git a/src/fixtures/tests/footer.ts b/src/fixtures/tests/footer.ts
index 3fb6e8d48344..cfb61ee35559 100644
--- a/src/fixtures/tests/footer.ts
+++ b/src/fixtures/tests/footer.ts
@@ -1,5 +1,5 @@
 import { describe, expect, test } from 'vitest'
-import cheerio from 'cheerio'
+import type { CheerioAPI } from 'cheerio'
 
 import { getDOM } from '@/tests/helpers/e2etest'
 import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version'
@@ -7,7 +7,7 @@ import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-v
 describe('footer', () => {
   describe('"contact us" link', () => {
     test('leads to support from articles', async () => {
-      const $: cheerio.Root = await getDOM(
+      const $: CheerioAPI = await getDOM(
         `/en/${nonEnterpriseDefaultVersion}/get-started/start-your-journey/hello-world`,
       )
       expect($('a#support').attr('href')).toBe('https://support.github.com')
@@ -16,21 +16,21 @@ describe('footer', () => {
     test('leads to support on 404 pages', async () => {
       // Important to use the prefix /en/ on the failing URL or else
       // it will render a very basic plain text 404 response.
-      const $: cheerio.Root = await getDOM('/en/delicious-snacks/donuts.php', { allow404: true })
+      const $: CheerioAPI = await getDOM('/en/delicious-snacks/donuts.php', { allow404: true })
       expect($('a#support').attr('href')).toBe('https://support.github.com')
     })
   })
 
   describe('"support" link with nextjs', () => {
     test('leads to support from articles', async () => {
-      const $: cheerio.Root = await getDOM(`/en/${nonEnterpriseDefaultVersion}/get-started?nextjs=`)
+      const $: CheerioAPI = await getDOM(`/en/${nonEnterpriseDefaultVersion}/get-started?nextjs=`)
       expect($('a#support').attr('href')).toBe('https://support.github.com')
     })
   })
 
   describe('test redirects for product landing community links pages', () => {
     test('codespaces product landing page leads to discussions page', async () => {
-      const $: cheerio.Root = await getDOM('/en/get-started')
+      const $: CheerioAPI = await getDOM('/en/get-started')
       expect($('a#ask-community').attr('href')).toBe(
         'https://hubgit.com/orgs/community/discussions/categories/get-started',
       )
@@ -39,7 +39,7 @@ describe('footer', () => {
 
   describe('test redirects for non-product landing community links pages', () => {
     test('leads to https://github.community/ when clicking on the community link', async () => {
-      const $: cheerio.Root = await getDOM(`/en/get-started/start-your-journey/hello-world`)
+      const $: CheerioAPI = await getDOM(`/en/get-started/start-your-journey/hello-world`)
       expect($('a#ask-community').attr('href')).toBe(
         'https://github.com/orgs/community/discussions',
       )
diff --git a/src/fixtures/tests/glossary.ts b/src/fixtures/tests/glossary.ts
index 70b314bc7dca..e214b7927aa4 100644
--- a/src/fixtures/tests/glossary.ts
+++ b/src/fixtures/tests/glossary.ts
@@ -1,20 +1,20 @@
 import { describe, expect, test } from 'vitest'
-import cheerio from 'cheerio'
+import type { CheerioAPI } from 'cheerio'
 
 import { getDOMCached as getDOM } from '@/tests/helpers/e2etest'
 
 describe('glossary', () => {
   test('headings are sorted alphabetically', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/learning-about-github/github-glossary')
+    const $: CheerioAPI = await getDOM('/get-started/learning-about-github/github-glossary')
     const headings = $('#article-contents h2')
-    const headingTexts = headings.map((_: number, el: any) => $(el).text()).get()
+    const headingTexts = headings.map((_, el) => $(el).text()).get()
     const cloned = [...headingTexts].sort((a: string, b: string) => a.localeCompare(b))
     const equalStringArray = (a: string[], b: string[]) =>
       a.length === b.length && a.every((v, i) => v === b[i])
     expect(equalStringArray(headingTexts, cloned)).toBe(true)
   })
   test('Markdown links are correct', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/learning-about-github/github-glossary')
+    const $: CheerioAPI = await getDOM('/get-started/learning-about-github/github-glossary')
     const internalLink = $('#article-contents a[href="/en/get-started/foo"]')
     expect(internalLink.length).toBe(1)
     // That link used AUTOTITLE so it should be "expanded"
@@ -22,19 +22,19 @@ describe('glossary', () => {
   })
 
   test('all Liquid is evaluated', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/learning-about-github/github-glossary')
+    const $: CheerioAPI = await getDOM('/get-started/learning-about-github/github-glossary')
     const paragraphs = $('#article-contents p')
-    const paragraphTexts = paragraphs.map((_: number, el: any) => $(el).text()).get()
+    const paragraphTexts = paragraphs.map((_, el) => $(el).text()).get()
     expect(paragraphTexts.find((text: string) => text.includes('{%'))).toBe(undefined)
   })
 
   test('liquid in one of the description depends on version', async () => {
     // fpt
     {
-      const $: cheerio.Root = await getDOM('/get-started/learning-about-github/github-glossary')
+      const $: CheerioAPI = await getDOM('/get-started/learning-about-github/github-glossary')
       const paragraphs = $('#article-contents p')
       const paragraphTexts = paragraphs
-        .map((_: number, el: any) => $(el).text())
+        .map((_, el) => $(el).text())
         .get()
         .join('\n')
       expect(paragraphTexts).toContain('status check on HubGit.')
@@ -42,12 +42,12 @@ describe('glossary', () => {
 
     // ghes
     {
-      const $: cheerio.Root = await getDOM(
+      const $: CheerioAPI = await getDOM(
         '/enterprise-server@latest/get-started/learning-about-github/github-glossary',
       )
       const paragraphs = $('#article-contents p')
       const paragraphTexts = paragraphs
-        .map((_: number, el: any) => $(el).text())
+        .map((_, el) => $(el).text())
         .get()
         .join('\n')
       expect(paragraphTexts).toContain('status check on HubGit Enterprise Server.')
diff --git a/src/fixtures/tests/guides.ts b/src/fixtures/tests/guides.ts
index 1e1663303601..ff4adb7d231d 100644
--- a/src/fixtures/tests/guides.ts
+++ b/src/fixtures/tests/guides.ts
@@ -1,11 +1,11 @@
 import { describe, expect, test } from 'vitest'
-import cheerio from 'cheerio'
+import type { CheerioAPI } from 'cheerio'
 
 import { get, getDOMCached as getDOM } from '@/tests/helpers/e2etest'
 
 describe('guides', () => {
   test("page's title should be document title", async () => {
-    const $: cheerio.Root = await getDOM('/code-security/guides')
+    const $: CheerioAPI = await getDOM('/code-security/guides')
     // This is what you'd find in src/fixtures/fixtures/content/code-security/guides.md
     const title = 'Guides for cool security'
     expect($('title').text()).toMatch(title)
@@ -19,7 +19,7 @@ describe('guides', () => {
 
 describe('learning tracks', () => {
   test('start the first learning track and come back via the navigation banner', async () => {
-    const $: cheerio.Root = await getDOM('/code-security/guides')
+    const $: CheerioAPI = await getDOM('/code-security/guides')
     const links = $('[data-testid=learning-track] a')
     const link = links
       .filter((_: number, el: any) => $(el).text() === 'Start learning path')
@@ -28,7 +28,7 @@ describe('learning tracks', () => {
     expect(link.attr('href')).toMatch('learnProduct=code-security')
 
     // Go the first "Start learning path" link
-    const $2: cheerio.Root = await getDOM(link.attr('href')!)
+    const $2: CheerioAPI = await getDOM(link.attr('href')!)
     const card2 = $2('[data-testid=learning-track-card]')
     // The card has 2 links. One to go back to the guide page
     // whose title is the name of the learning track
@@ -57,7 +57,7 @@ describe('learning tracks', () => {
     expect(navNextLink.text()).toBe('Securing your organization')
 
     // Go to the next (last) page
-    const $3: cheerio.Root = await getDOM(nextLink.attr('href')!)
+    const $3: CheerioAPI = await getDOM(nextLink.attr('href')!)
     const card3 = $3('[data-testid=learning-track-card]')
     const span3 = card3.find('span').filter((_: number, el: any) => $(el).text().includes('2 of 2'))
     expect(span3.text()).toBe('2 of 2 in learning path')
@@ -82,15 +82,13 @@ describe('learning tracks', () => {
   test('with and without a valid ?learn= query string', async () => {
     // Valid
     {
-      const $: cheerio.Root = await getDOM(
-        '/code-security/getting-started/quickstart?learn=foo_bar',
-      )
+      const $: CheerioAPI = await getDOM('/code-security/getting-started/quickstart?learn=foo_bar')
       expect($('[data-testid=learning-track-card]').length).toBe(1)
       expect($('[data-testid=learning-track-nav]').length).toBe(1)
     }
     // Invalid
     {
-      const $: cheerio.Root = await getDOM(
+      const $: CheerioAPI = await getDOM(
         '/code-security/getting-started/quickstart?learn=blablainvalid',
       )
       expect($('[data-testid=learning-track-card]').length).toBe(0)
@@ -98,13 +96,13 @@ describe('learning tracks', () => {
     }
     // Empty
     {
-      const $: cheerio.Root = await getDOM('/code-security/getting-started/quickstart?learn=')
+      const $: CheerioAPI = await getDOM('/code-security/getting-started/quickstart?learn=')
       expect($('[data-testid=learning-track-card]').length).toBe(0)
       expect($('[data-testid=learning-track-nav]').length).toBe(0)
     }
     // Missing
     {
-      const $: cheerio.Root = await getDOM('/code-security/getting-started/quickstart')
+      const $: CheerioAPI = await getDOM('/code-security/getting-started/quickstart')
       expect($('[data-testid=learning-track-card]').length).toBe(0)
       expect($('[data-testid=learning-track-nav]').length).toBe(0)
     }
diff --git a/src/fixtures/tests/head.ts b/src/fixtures/tests/head.ts
index 99ed5c9e804a..253f43190423 100644
--- a/src/fixtures/tests/head.ts
+++ b/src/fixtures/tests/head.ts
@@ -1,11 +1,11 @@
 import { describe, expect, test } from 'vitest'
-import cheerio from 'cheerio'
+import type { CheerioAPI } from 'cheerio'
 
 import { getDOM } from '@/tests/helpers/e2etest'
 
 describe('', () => {
   test('includes page intro in `description` meta tag', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/markdown/intro')
+    const $: CheerioAPI = await getDOM('/get-started/markdown/intro')
     // The intro has Markdown syntax which becomes HTML encoded in the lead element.
     const lead = $('[data-testid="lead"] p')
     expect(lead.html()).toMatch('syntax')
diff --git a/src/fixtures/tests/homepage.ts b/src/fixtures/tests/homepage.ts
index 0b259e63de23..8ba9d7ece5e0 100644
--- a/src/fixtures/tests/homepage.ts
+++ b/src/fixtures/tests/homepage.ts
@@ -1,11 +1,11 @@
 import { describe, expect, test } from 'vitest'
-import cheerio from 'cheerio'
+import type { CheerioAPI } from 'cheerio'
 
 import { get, getDOM } from '@/tests/helpers/e2etest'
 
 describe('home page', () => {
   test('landing area', async () => {
-    const $: cheerio.Root = await getDOM('/')
+    const $: CheerioAPI = await getDOM('/')
     const container = $('#landing')
     expect(container.length).toBe(1)
     expect(container.find('h1').text()).toBe('GitHub Docs')
@@ -13,10 +13,10 @@ describe('home page', () => {
   })
 
   test('product groups can use Liquid', async () => {
-    const $: cheerio.Root = await getDOM('/')
+    const $: CheerioAPI = await getDOM('/')
     const main = $('[data-testid="product"]')
     const links = main.find('a[href*="/"]')
-    const hrefs = links.map((i: number, link: any) => $(link)).get()
+    const hrefs = links.map((_, link) => $(link)).get()
     let externalLinks = 0
     for (const href of hrefs) {
       if (!href.attr('href')?.startsWith('https://')) {
diff --git a/src/fixtures/tests/html-comments.ts b/src/fixtures/tests/html-comments.ts
index 62d8bcf42088..6ed1d098eede 100644
--- a/src/fixtures/tests/html-comments.ts
+++ b/src/fixtures/tests/html-comments.ts
@@ -1,11 +1,11 @@
 import { describe, expect, test } from 'vitest'
-import cheerio from 'cheerio'
+import type { CheerioAPI } from 'cheerio'
 
 import { getDOMCached as getDOM } from '@/tests/helpers/e2etest'
 
 describe('html-comments', () => {
   test('regular comments are removed', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/markdown/html-comments')
+    const $: CheerioAPI = await getDOM('/get-started/markdown/html-comments')
     const contents = $('#article-contents')
     const html = contents.html()
     expect(html).not.toContain('This comment should get deleted since it mentions gooblygook')
diff --git a/src/fixtures/tests/images.ts b/src/fixtures/tests/images.ts
index 876e6902b006..229d9c188d24 100644
--- a/src/fixtures/tests/images.ts
+++ b/src/fixtures/tests/images.ts
@@ -1,13 +1,13 @@
 import { describe, expect, test } from 'vitest'
 import sharp from 'sharp'
-import cheerio from 'cheerio'
+import type { CheerioAPI } from 'cheerio'
 
 import { get, head, getDOM } from '@/tests/helpers/e2etest'
 import { MAX_WIDTH } from '@/content-render/unified/rewrite-asset-img-tags'
 
 describe('render Markdown image tags', () => {
   test('page with a single image', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/images/single-image')
+    const $: CheerioAPI = await getDOM('/get-started/images/single-image')
 
     const pictures = $('#article-contents picture')
     expect(pictures.length).toBe(1)
@@ -46,7 +46,7 @@ describe('render Markdown image tags', () => {
   })
 
   test('images have density specified', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/images/retina-image')
+    const $: CheerioAPI = await getDOM('/get-started/images/retina-image')
 
     const pictures = $('#article-contents picture')
     expect(pictures.length).toBe(3)
@@ -60,14 +60,14 @@ describe('render Markdown image tags', () => {
   })
 
   test('image inside a list keeps its span', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/images/images-in-lists')
+    const $: CheerioAPI = await getDOM('/get-started/images/images-in-lists')
 
     const imageSpan = $('#article-contents > div > ol > li > div.procedural-image-wrapper')
     expect(imageSpan.length).toBe(1)
   })
 
   test("links directly to images aren't rewritten", async () => {
-    const $: cheerio.Root = await getDOM('/get-started/images/link-to-image')
+    const $: CheerioAPI = await getDOM('/get-started/images/link-to-image')
     // There is only 1 link inside that page
     const links = $('#article-contents a[href^="/"]') // exclude header link
     expect(links.length).toBe(1)
diff --git a/src/fixtures/tests/internal-links.ts b/src/fixtures/tests/internal-links.ts
index e0847dc2395e..353dc286168c 100644
--- a/src/fixtures/tests/internal-links.ts
+++ b/src/fixtures/tests/internal-links.ts
@@ -1,5 +1,6 @@
 import { describe, expect, test } from 'vitest'
-import cheerio from 'cheerio'
+import type { CheerioAPI } from 'cheerio'
+import type { Element } from 'domhandler'
 
 import { get, getDOM } from '@/tests/helpers/e2etest'
 import enterpriseServerReleases from '@/versions/lib/enterprise-server-releases'
@@ -7,9 +8,9 @@ import { allVersions } from '@/versions/lib/all-versions'
 
 describe('autotitle', () => {
   test('internal links with AUTOTITLE resolves', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/foo/autotitling')
+    const $: CheerioAPI = await getDOM('/get-started/foo/autotitling')
     const links = $('#article-contents a[href]')
-    links.each((i: number, element: cheerio.Element) => {
+    links.each((i: number, element: Element) => {
       if ($(element).attr('href')?.includes('/get-started/start-your-journey/hello-world')) {
         expect($(element).text()).toBe('Hello World')
       }
@@ -44,19 +45,19 @@ describe('cross-version-links', () => {
     'links to free-pro-team should be implicit even on %p',
     async (version: string) => {
       const URL = `/${version}/get-started/foo/cross-version-linking`
-      const $: cheerio.Root = await getDOM(URL)
+      const $: CheerioAPI = await getDOM(URL)
       const links = $('#article-contents a[href]')
 
       // Tests that the hardcoded prefix is always removed
       const firstLink = links.filter(
-        (i: number, element: cheerio.Element) =>
+        (i: number, element: Element) =>
           $(element).text() === 'Hello world always in free-pro-team',
       )
       expect(firstLink.attr('href')).toBe('/en/get-started/start-your-journey/hello-world')
 
       // Tests that the second link always goes to enterprise-server@X.Y
       const secondLink = links.filter(
-        (i: number, element: cheerio.Element) =>
+        (i: number, element: Element) =>
           $(element).text() === 'Autotitling page always in enterprise-server latest',
       )
       expect(secondLink.attr('href')).toBe(
@@ -68,12 +69,12 @@ describe('cross-version-links', () => {
 
 describe('link-rewriting', () => {
   test('/en is injected', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/start-your-journey/link-rewriting')
+    const $: CheerioAPI = await getDOM('/get-started/start-your-journey/link-rewriting')
     const links = $('#article-contents a[href]')
 
     {
       const link = links.filter(
-        (i: number, element: cheerio.Element) => $(element).text() === 'Cross Version Linking',
+        (i: number, element: Element) => $(element).text() === 'Cross Version Linking',
       )
       expect(link.attr('href')).toMatch('/en/get-started/')
     }
@@ -81,25 +82,25 @@ describe('link-rewriting', () => {
     // Some links are left untouched
 
     {
-      const link = links.filter((i: number, element: cheerio.Element) =>
+      const link = links.filter((i: number, element: Element) =>
         $(element).text().includes('Enterprise 11.10'),
       )
       expect(link.attr('href')).toMatch('/en/enterprise/')
     }
     {
-      const link = links.filter((i: number, element: cheerio.Element) =>
+      const link = links.filter((i: number, element: Element) =>
         $(element).text().includes('peterbe'),
       )
       expect(link.attr('href')).toMatch(/^https:/)
     }
     {
-      const link = links.filter((i: number, element: cheerio.Element) =>
+      const link = links.filter((i: number, element: Element) =>
         $(element).text().includes('Picture'),
       )
       expect(link.attr('href')).toMatch(/^\/assets\//)
     }
     {
-      const link = links.filter((i: number, element: cheerio.Element) =>
+      const link = links.filter((i: number, element: Element) =>
         $(element).text().includes('GraphQL Schema'),
       )
       expect(link.attr('href')).toMatch(/^\/public\//)
@@ -107,26 +108,26 @@ describe('link-rewriting', () => {
   })
 
   test('/en and current version (latest) is injected', async () => {
-    const $: cheerio.Root = await getDOM(
+    const $: CheerioAPI = await getDOM(
       '/enterprise-cloud@latest/get-started/start-your-journey/link-rewriting',
     )
     const links = $('#article-contents a[href]')
 
     const link = links.filter(
-      (i: number, element: cheerio.Element) => $(element).text() === 'Cross Version Linking',
+      (i: number, element: Element) => $(element).text() === 'Cross Version Linking',
     )
     expect(link.attr('href')).toMatch('/en/enterprise-cloud@latest/get-started/')
   })
 
   test('/en and current version number is injected', async () => {
     // enterprise-server, unlike enterprise-cloud, use numbers
-    const $: cheerio.Root = await getDOM(
+    const $: CheerioAPI = await getDOM(
       '/enterprise-server@latest/get-started/start-your-journey/link-rewriting',
     )
     const links = $('#article-contents a[href]')
 
     const link = links.filter(
-      (i: number, element: cheerio.Element) => $(element).text() === 'Cross Version Linking',
+      (i: number, element: Element) => $(element).text() === 'Cross Version Linking',
     )
     expect(link.attr('href')).toMatch(
       `/en/enterprise-server@${enterpriseServerReleases.latestStable}/get-started/`,
@@ -136,16 +137,16 @@ describe('link-rewriting', () => {
 
 describe('subcategory links', () => {
   test('no free-pro-team prefix', async () => {
-    const $: cheerio.Root = await getDOM('/rest/actions')
+    const $: CheerioAPI = await getDOM('/rest/actions')
     const links = $('[data-testid="table-of-contents"] a[href]')
-    links.each((i: number, element: cheerio.Element) => {
+    links.each((i: number, element: Element) => {
       expect($(element).attr('href')).not.toContain('/free-pro-team@latest')
     })
   })
   test('enterprise-server prefix', async () => {
-    const $: cheerio.Root = await getDOM('/enterprise-server@latest/rest/actions')
+    const $: CheerioAPI = await getDOM('/enterprise-server@latest/rest/actions')
     const links = $('[data-testid="table-of-contents"] a[href]')
-    links.each((i: number, element: cheerio.Element) => {
+    links.each((i: number, element: Element) => {
       expect($(element).attr('href')).toMatch(/\/enterprise-server@\d/)
     })
   })
diff --git a/src/fixtures/tests/landing-hero.ts b/src/fixtures/tests/landing-hero.ts
index 15eb263cf6e1..743b339fa793 100644
--- a/src/fixtures/tests/landing-hero.ts
+++ b/src/fixtures/tests/landing-hero.ts
@@ -1,23 +1,23 @@
 import { describe, expect, test } from 'vitest'
-import cheerio from 'cheerio'
+import type { CheerioAPI } from 'cheerio'
 
 import { getDOM } from '@/tests/helpers/e2etest'
 
 describe('product landing page', () => {
   test('product landing page displays full title', async () => {
-    const $: cheerio.Root = await getDOM('/get-started')
+    const $: CheerioAPI = await getDOM('/get-started')
     expect($('h1').first().text()).toMatch(/Getting started with HubGit/)
   })
 
   test('product landing page lists with shortTitle heading (free-pro-team)', async () => {
-    const $: cheerio.Root = await getDOM('/pages')
+    const $: CheerioAPI = await getDOM('/pages')
     // Note that this particular page (in the fixtures) has Liquid
     // in its shorTitle.
     expect($('#all-docs a').first().text()).toMatch('All Pages (HubGit) docs')
   })
 
   test('product landing page lists with shortTitle heading (enterprise-server)', async () => {
-    const $: cheerio.Root = await getDOM('/enterprise-server@latest/pages')
+    const $: CheerioAPI = await getDOM('/enterprise-server@latest/pages')
     // Note that this particular page (in the fixtures) has Liquid
     // in its shorTitle.
     expect($('#all-docs a').first().text()).toMatch('All Pages (HubGit Enterprise Server) docs')
diff --git a/src/fixtures/tests/liquid.ts b/src/fixtures/tests/liquid.ts
index c559d56399fb..80bad84ff455 100644
--- a/src/fixtures/tests/liquid.ts
+++ b/src/fixtures/tests/liquid.ts
@@ -1,5 +1,5 @@
 import { describe, expect, test } from 'vitest'
-import cheerio from 'cheerio'
+import type { CheerioAPI } from 'cheerio'
 
 import { getDataByLanguage } from '@/data-directory/lib/get-data'
 import { getDOM } from '@/tests/helpers/e2etest'
@@ -7,28 +7,28 @@ import { supported } from '@/versions/lib/enterprise-server-releases'
 
 describe('spotlight', () => {
   test('renders styled warnings', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/liquid/warnings')
+    const $: CheerioAPI = await getDOM('/get-started/liquid/warnings')
     const nodes = $('.ghd-spotlight-attention')
     expect(nodes.length).toBe(1)
     expect(nodes.text().includes('This is inside the warning.')).toBe(true)
   })
 
   test('renders styled danger', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/liquid/danger')
+    const $: CheerioAPI = await getDOM('/get-started/liquid/danger')
     const nodes = $('.ghd-spotlight-danger')
     expect(nodes.length).toBe(1)
     expect(nodes.text().includes('Danger, Will Robinson.')).toBe(true)
   })
 
   test('renders styled tips', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/liquid/tips')
+    const $: CheerioAPI = await getDOM('/get-started/liquid/tips')
     const nodes = $('.ghd-spotlight-success')
     expect(nodes.length).toBe(1)
     expect(nodes.text().includes('This is inside the tip.')).toBe(true)
   })
 
   test('renders styled notes', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/liquid/notes')
+    const $: CheerioAPI = await getDOM('/get-started/liquid/notes')
     const nodes = $('.ghd-spotlight-accent')
     expect(nodes.length).toBe(1)
     expect(nodes.text().includes('This is inside the note.')).toBe(true)
@@ -37,7 +37,7 @@ describe('spotlight', () => {
 
 describe('raw', () => {
   test('renders raw', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/liquid/raw')
+    const $: CheerioAPI = await getDOM('/get-started/liquid/raw')
     const lead = $('[data-testid="lead"]').html()
     expect(lead).toMatch('{% raw %}')
     const code = $('pre code').html()
@@ -48,7 +48,7 @@ describe('raw', () => {
 
 describe('tool', () => {
   test('renders platform-specific content', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/liquid/platform-specific')
+    const $: CheerioAPI = await getDOM('/get-started/liquid/platform-specific')
     expect($('.ghd-tool.mac p').length).toBe(1)
     expect($('.ghd-tool.mac p').text().includes('mac specific content')).toBe(true)
     expect($('.ghd-tool.windows p').length).toBe(1)
@@ -58,7 +58,7 @@ describe('tool', () => {
   })
 
   test('renders expected mini TOC headings in platform-specific content', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/liquid/platform-specific')
+    const $: CheerioAPI = await getDOM('/get-started/liquid/platform-specific')
     expect($('h2#in-this-article').length).toBe(1)
     expect($('h2#in-this-article + nav ul .ghd-tool.mac').length).toBe(1)
     expect($('h2#in-this-article + nav ul .ghd-tool.windows').length).toBe(1)
@@ -68,7 +68,7 @@ describe('tool', () => {
 
 describe('post', () => {
   test('whitespace control', async () => {
-    const $: cheerio.Root = await getDOM('/get-started/liquid/whitespace')
+    const $: CheerioAPI = await getDOM('/get-started/liquid/whitespace')
     const html = $('#article-contents').html()
     expect(html).toMatch('

HubGit

') expect(html).toMatch('

Text before. HubGit Text after.

') @@ -78,7 +78,7 @@ describe('post', () => { // Test what happens to `Cram{% ifversion fpt %}FPT{% endif %}ped.` // when it's not free-pro-team. { - const $inner: cheerio.Root = await getDOM( + const $inner: CheerioAPI = await getDOM( '/enterprise-server@latest/get-started/liquid/whitespace', ) const innerHtml = $inner('#article-contents').html() @@ -91,7 +91,7 @@ describe('post', () => { describe('rowheaders', () => { test('rowheaders', async () => { - const $: cheerio.Root = await getDOM('/get-started/liquid/table-row-headers') + const $: CheerioAPI = await getDOM('/get-started/liquid/table-row-headers') const tables = $('#article-contents table') expect(tables.length).toBe(2) @@ -188,7 +188,7 @@ describe('ifversion', () => { test.each(Object.keys(matchesPerVersion))( 'ifversion using rendered version %p', async (version: string) => { - const $: cheerio.Root = await getDOM(`/${version}/get-started/liquid/ifversion`) + const $: CheerioAPI = await getDOM(`/${version}/get-started/liquid/ifversion`) const html = $('#article-contents').html() const allConditions = Object.values(matchesPerVersion).flat() @@ -215,7 +215,7 @@ describe('ifversion', () => { describe('misc Liquid', () => { test('links with liquid from data', async () => { - const $: cheerio.Root = await getDOM('/get-started/liquid/links-with-liquid') + const $: CheerioAPI = await getDOM('/get-started/liquid/links-with-liquid') // The URL comes from variables.product.pricing_url const url = getDataByLanguage('variables.product.pricing_url', 'en') if (!url) throw new Error('variable could not be found') @@ -235,7 +235,7 @@ describe('misc Liquid', () => { // Markdown directly follows a tool tag like `{% linux %}...{% endlinux %}`. // The next line immediately after the `{% endlinux %}` should not // leave the Markdown unrendered - const $: cheerio.Root = await getDOM('/get-started/liquid/tool-platform-switcher') + const $: CheerioAPI = await getDOM('/get-started/liquid/tool-platform-switcher') const innerHTML = $('#article-contents').html() expect(innerHTML).not.toMatch('On *this* line is `Markdown` too.') expect(innerHTML).toMatch('

On this line is Markdown too.

') @@ -244,7 +244,7 @@ describe('misc Liquid', () => { describe('data tag', () => { test('injects data reusables with the right whitespace', async () => { - const $: cheerio.Root = await getDOM('/get-started/liquid/data') + const $: CheerioAPI = await getDOM('/get-started/liquid/data') // This proves that the two injected reusables tables work. // CommonMark is finicky if the indentation isn't perfect, so diff --git a/src/fixtures/tests/markdown.ts b/src/fixtures/tests/markdown.ts index 36b604106c38..a438412b5ffa 100644 --- a/src/fixtures/tests/markdown.ts +++ b/src/fixtures/tests/markdown.ts @@ -1,11 +1,11 @@ import { describe, expect, test } from 'vitest' -import cheerio from 'cheerio' +import type { CheerioAPI } from 'cheerio' import { getDOM } from '@/tests/helpers/e2etest' describe('markdown rendering', () => { test('markdown in intro', async () => { - const $: cheerio.Root = await getDOM('/get-started/markdown/intro') + const $: CheerioAPI = await getDOM('/get-started/markdown/intro') const html = $('[data-testid="lead"]').html() expect(html).toMatch('Markdown') expect(html).toMatch('syntax') @@ -15,7 +15,7 @@ describe('markdown rendering', () => { describe('alerts', () => { test('basic rendering', async () => { - const $: cheerio.Root = await getDOM('/get-started/markdown/alerts') + const $: CheerioAPI = await getDOM('/get-started/markdown/alerts') const alerts = $('#article-contents .ghd-alert') // See src/fixtures/fixtures/content/get-started/markdown/alerts.md // to be this confident in the assertions. diff --git a/src/fixtures/tests/minitoc.ts b/src/fixtures/tests/minitoc.ts index 9d2475853acd..8ca670e7a320 100644 --- a/src/fixtures/tests/minitoc.ts +++ b/src/fixtures/tests/minitoc.ts @@ -1,37 +1,37 @@ import { describe, expect, test } from 'vitest' -import cheerio from 'cheerio' +import type { CheerioAPI } from 'cheerio' import { getDOM } from '@/tests/helpers/e2etest' describe('minitoc', () => { test('renders mini TOC in articles with more than one heading', async () => { - const $: cheerio.Root = await getDOM('/en/get-started/minitocs/multiple-headings') + const $: CheerioAPI = await getDOM('/en/get-started/minitocs/multiple-headings') expect($('h2#in-this-article').length).toBe(1) expect($('h2#in-this-article + nav ul li').length).toBeGreaterThan(1) }) test('does not render mini TOC in articles with only one heading', async () => { - const $: cheerio.Root = await getDOM('/en/get-started/minitocs/one-heading') + const $: CheerioAPI = await getDOM('/en/get-started/minitocs/one-heading') expect($('h2#in-this-article').length).toBe(0) }) test('does not render mini TOC in articles with no headings', async () => { - const $: cheerio.Root = await getDOM('/en/get-started/minitocs/no-heading') + const $: CheerioAPI = await getDOM('/en/get-started/minitocs/no-heading') expect($('h2#in-this-article').length).toBe(0) }) test('does not render mini TOC in non-articles', async () => { - const $: cheerio.Root = await getDOM('/en/get-started') + const $: CheerioAPI = await getDOM('/en/get-started') expect($('h2#in-this-article').length).toBe(0) }) test('renders mini TOC with correct links when headings contain markup', async () => { - const $: cheerio.Root = await getDOM('/en/get-started/minitocs/markup-heading') + const $: CheerioAPI = await getDOM('/en/get-started/minitocs/markup-heading') expect($('h2#in-this-article + nav ul li a[href="#on"]').length).toBe(1) }) test("max heading doesn't exceed h2", async () => { - const $: cheerio.Root = await getDOM('/en/get-started/minitocs/multiple-headings') + const $: CheerioAPI = await getDOM('/en/get-started/minitocs/multiple-headings') expect($('h2#in-this-article').length).toBe(1) expect($('h2#in-this-article + nav ul').length).toBeGreaterThan(0) // non-indented items expect($('h2#in-this-article + nav ul div ul div').length).toBe(0) // indented items diff --git a/src/fixtures/tests/page-titles.ts b/src/fixtures/tests/page-titles.ts index 2bfe53bf8cc5..3d4492ec021f 100644 --- a/src/fixtures/tests/page-titles.ts +++ b/src/fixtures/tests/page-titles.ts @@ -1,22 +1,22 @@ import { describe, expect, test } from 'vitest' -import cheerio from 'cheerio' +import type { CheerioAPI } from 'cheerio' import enterpriseServerReleases from '@/versions/lib/enterprise-server-releases' import { getDOM } from '@/tests/helpers/e2etest' describe('page titles', () => { test('homepage', async () => { - const $: cheerio.Root = await getDOM('/en') + const $: CheerioAPI = await getDOM('/en') expect($('title').text()).toBe('GitHub Docs') }) test('fpt article', async () => { - const $: cheerio.Root = await getDOM('/get-started/start-your-journey/hello-world') + const $: CheerioAPI = await getDOM('/get-started/start-your-journey/hello-world') expect($('title').text()).toBe('Hello World - GitHub Docs') }) test('ghes article', async () => { - const $: cheerio.Root = await getDOM( + const $: CheerioAPI = await getDOM( `/enterprise-server@latest/get-started/start-your-journey/hello-world`, ) expect($('title').text()).toBe( @@ -25,12 +25,12 @@ describe('page titles', () => { }) test('fpt subcategory page', async () => { - const $: cheerio.Root = await getDOM('/en/get-started/start-your-journey') + const $: CheerioAPI = await getDOM('/en/get-started/start-your-journey') expect($('title').text()).toBe('Start your journey - GitHub Docs') }) test('fpt category page', async () => { - const $: cheerio.Root = await getDOM('/actions/category') + const $: CheerioAPI = await getDOM('/actions/category') expect($('title').text()).toBe('Category page of GitHub Actions - GitHub Docs') }) }) diff --git a/src/fixtures/tests/permissions-callout.ts b/src/fixtures/tests/permissions-callout.ts index 6c194d9a28ae..93cc31cd9d87 100644 --- a/src/fixtures/tests/permissions-callout.ts +++ b/src/fixtures/tests/permissions-callout.ts @@ -1,11 +1,11 @@ import { describe, expect, test } from 'vitest' -import cheerio from 'cheerio' +import type { CheerioAPI } from 'cheerio' import { getDOM } from '@/tests/helpers/e2etest' describe('permission statements', () => { test('article page product statement', async () => { - const $: cheerio.Root = await getDOM('/get-started/foo/page-with-callout') + const $: CheerioAPI = await getDOM('/get-started/foo/page-with-callout') const callout = $('[data-testid=product-statement] div') expect(callout.html()).toBe('

Callout for HubGit Pages

') }) @@ -16,7 +16,7 @@ describe('permission statements', () => { // an empty string. // This test tests that alert is not rendered if its output // "exits" but is empty. - const $: cheerio.Root = await getDOM( + const $: CheerioAPI = await getDOM( '/enterprise-server@latest/get-started/foo/page-with-callout', ) const callout = $('[data-testid=product-statement]') @@ -24,13 +24,13 @@ describe('permission statements', () => { }) test('toc landing page', async () => { - const $: cheerio.Root = await getDOM('/actions/category') + const $: CheerioAPI = await getDOM('/actions/category') const callout = $('[data-testid=product-statement] div') expect(callout.html()).toBe('

This is the callout text

') }) test('page with permission frontmatter', async () => { - const $: cheerio.Root = await getDOM('/get-started/markdown/permissions') + const $: CheerioAPI = await getDOM('/get-started/markdown/permissions') const html = $('[data-testid=permissions-statement] div').html() // Markdown expect(html).toMatch('admin') @@ -39,9 +39,7 @@ describe('permission statements', () => { }) test('page with permission frontmatter and product statement', async () => { - const $: cheerio.Root = await getDOM( - '/get-started/foo/page-with-permissions-and-product-callout', - ) + const $: CheerioAPI = await getDOM('/get-started/foo/page-with-permissions-and-product-callout') const html = $('[data-testid=permissions-callout] div').html() // part of the UI expect(html).toMatch('Who can use this feature') diff --git a/src/fixtures/tests/sidebar.ts b/src/fixtures/tests/sidebar.ts index 68f746c4f504..568b5bb6a369 100644 --- a/src/fixtures/tests/sidebar.ts +++ b/src/fixtures/tests/sidebar.ts @@ -1,11 +1,11 @@ import { describe, expect, test } from 'vitest' -import cheerio from 'cheerio' +import type { CheerioAPI } from 'cheerio' import { getDOMCached as getDOM } from '@/tests/helpers/e2etest' describe('sidebar', () => { test('top level product mentioned at top of sidebar', async () => { - const $: cheerio.Root = await getDOM('/get-started') + const $: CheerioAPI = await getDOM('/get-started') // Desktop const sidebarProduct = $('[data-testid="sidebar-product-xl"]') expect(sidebarProduct.text()).toBe('Get started') @@ -16,12 +16,12 @@ describe('sidebar', () => { }) test('REST pages get the REST sidebar', async () => { - const $: cheerio.Root = await getDOM('/rest') + const $: CheerioAPI = await getDOM('/rest') expect($('[data-testid=rest-sidebar-reference]').length).toBe(1) }) test('leaf-node article marked as aria-current=page', async () => { - const $: cheerio.Root = await getDOM('/get-started/start-your-journey/hello-world') + const $: CheerioAPI = await getDOM('/get-started/start-your-journey/hello-world') expect( $( '[data-testid=sidebar] [data-testid=product-sidebar] a[aria-current="page"] span span', @@ -30,7 +30,7 @@ describe('sidebar', () => { }) test('sidebar should always use the shortTitle', async () => { - const $: cheerio.Root = await getDOM('/get-started/foo/bar') + const $: CheerioAPI = await getDOM('/get-started/foo/bar') // The page /get-started/foo/bar has a short title that is different // from its regular title. expect( @@ -41,7 +41,7 @@ describe('sidebar', () => { }) test('short titles with Liquid and HTML characters', async () => { - const $: cheerio.Root = await getDOM('/get-started/foo/html-short-title') + const $: CheerioAPI = await getDOM('/get-started/foo/html-short-title') const link = $( '[data-testid=sidebar] [data-testid=product-sidebar] a[href*="/get-started/foo/html-short-title"]', ) @@ -51,26 +51,26 @@ describe('sidebar', () => { test('Liquid is rendered in short title used at top of sidebar', async () => { // Free, pro, team { - const $: cheerio.Root = await getDOM('/pages') + const $: CheerioAPI = await getDOM('/pages') const link = $('#allproducts-menu a') expect(link.text()).toBe('Pages (HubGit)') } // Enterprise Server { - const $: cheerio.Root = await getDOM('/enterprise-server@latest/pages') + const $: CheerioAPI = await getDOM('/enterprise-server@latest/pages') const link = $('#allproducts-menu a') expect(link.text()).toBe('Pages (HubGit Enterprise Server)') } // Enterprise Cloud { - const $: cheerio.Root = await getDOM('/enterprise-cloud@latest/pages') + const $: CheerioAPI = await getDOM('/enterprise-cloud@latest/pages') const link = $('#allproducts-menu a') expect(link.text()).toBe('Pages (HubGit Enterprise Cloud)') } }) test('no docset link for early-access', async () => { - const $: cheerio.Root = await getDOM('/early-access/secrets/deeper/mariana-trench') + const $: CheerioAPI = await getDOM('/early-access/secrets/deeper/mariana-trench') // Deskop expect($('[data-testid="sidebar-product-xl"]').length).toBe(0) // Mobile diff --git a/src/fixtures/tests/translations.ts b/src/fixtures/tests/translations.ts index 2fc631089f59..0682eecfcd71 100644 --- a/src/fixtures/tests/translations.ts +++ b/src/fixtures/tests/translations.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from 'vitest' -import cheerio from 'cheerio' +import type { CheerioAPI } from 'cheerio' import { TRANSLATIONS_FIXTURE_ROOT } from '@/frame/lib/constants' import { getDOM, head } from '@/tests/helpers/e2etest' @@ -12,7 +12,7 @@ if (!TRANSLATIONS_FIXTURE_ROOT) { describe('translations', () => { test('home page', async () => { - const $: cheerio.Root = await getDOM('/ja') + const $: CheerioAPI = await getDOM('/ja') const h1 = $('h1').text() // You gotta know your src/fixtures/fixtures/translations/ja-jp/data/ui.yml expect(h1).toBe('日本 GitHub Docs') @@ -32,13 +32,13 @@ describe('translations', () => { }) test('hello world', async () => { - const $: cheerio.Root = await getDOM('/ja/get-started/start-your-journey/hello-world') + const $: CheerioAPI = await getDOM('/ja/get-started/start-your-journey/hello-world') const h1 = $('h1').text() expect(h1).toBe('こんにちは World') }) test('internal links get prefixed with /ja', async () => { - const $: cheerio.Root = await getDOM('/ja/get-started/start-your-journey/link-rewriting') + const $: CheerioAPI = await getDOM('/ja/get-started/start-your-journey/link-rewriting') const links = $('#article-contents a[href]') const jaLinks = links.filter((i: number, element: any) => { const href = $(element).attr('href') @@ -53,7 +53,7 @@ describe('translations', () => { }) test('internal links with AUTOTITLE resolves', async () => { - const $: cheerio.Root = await getDOM('/ja/get-started/foo/autotitling') + const $: CheerioAPI = await getDOM('/ja/get-started/foo/autotitling') const links = $('#article-contents a[href]') links.each((i: number, element: any) => { if ($(element).attr('href')?.includes('/ja/get-started/start-your-journey/hello-world')) { @@ -67,7 +67,7 @@ describe('translations', () => { test('correction of linebreaks in translations', async () => { // free-pro-team { - const $: cheerio.Root = await getDOM('/ja/get-started/foo/table-with-ifversions') + const $: CheerioAPI = await getDOM('/ja/get-started/foo/table-with-ifversions') const paragraph = $('#article-contents p').text() expect(paragraph).toMatch('mention of HubGit in Liquid') @@ -80,7 +80,7 @@ describe('translations', () => { } // enterprise-server { - const $: cheerio.Root = await getDOM( + const $: CheerioAPI = await getDOM( '/ja/enterprise-server@latest/get-started/foo/table-with-ifversions', ) @@ -96,7 +96,7 @@ describe('translations', () => { }) test('automatic correction of bad AUTOTITLE in reusables', async () => { - const $: cheerio.Root = await getDOM('/ja/get-started/start-your-journey/hello-world') + const $: CheerioAPI = await getDOM('/ja/get-started/start-your-journey/hello-world') const links = $('#article-contents a[href]') const texts = links.map((i: number, element: any) => $(element).text()).get() // That Japanese page uses AUTOTITLE links. Both in the main `.md` file @@ -126,7 +126,7 @@ describe('translations', () => { // which needs to become: // // [Bar](バー) - const $: cheerio.Root = await getDOM('/ja/get-started/start-your-journey/hello-world') + const $: CheerioAPI = await getDOM('/ja/get-started/start-your-journey/hello-world') const links = $('#article-contents a[href]') const texts = links .filter((i: number, element: any) => { diff --git a/src/fixtures/tests/versioning.ts b/src/fixtures/tests/versioning.ts index 2665fcc2e682..5fe70db14127 100644 --- a/src/fixtures/tests/versioning.ts +++ b/src/fixtures/tests/versioning.ts @@ -1,12 +1,12 @@ import { describe, expect, test } from 'vitest' -import cheerio from 'cheerio' +import type { CheerioAPI } from 'cheerio' import { getDOM, head } from '@/tests/helpers/e2etest' import { supported } from '@/versions/lib/enterprise-server-releases' describe('article versioning', () => { test('only links to articles for fpt', async () => { - const $: cheerio.Root = await getDOM('/get-started/versioning') + const $: CheerioAPI = await getDOM('/get-started/versioning') const links = $('[data-testid="table-of-contents"] a') // Only 1 link because there's only 1 article available in fpt expect(links.length).toBe(1) @@ -14,7 +14,7 @@ describe('article versioning', () => { }) test('only links to articles for ghec', async () => { - const $: cheerio.Root = await getDOM('/enterprise-cloud@latest/get-started/versioning') + const $: CheerioAPI = await getDOM('/enterprise-cloud@latest/get-started/versioning') const links = $('[data-testid="table-of-contents"] a') expect(links.length).toBe(2) const first = links.filter((i: number) => i === 0) diff --git a/src/fixtures/tests/video-transcripts.ts b/src/fixtures/tests/video-transcripts.ts index 812e5fbc0c3d..36a4f6adca1c 100644 --- a/src/fixtures/tests/video-transcripts.ts +++ b/src/fixtures/tests/video-transcripts.ts @@ -1,12 +1,12 @@ import { describe, expect, test } from 'vitest' -import cheerio from 'cheerio' +import type { CheerioAPI } from 'cheerio' import { getDOM } from '@/tests/helpers/e2etest' describe('transcripts', () => { describe('product landing page', () => { test('video link from product landing page leads to video', async () => { - const $: cheerio.Root = await getDOM('/en/get-started') + const $: CheerioAPI = await getDOM('/en/get-started') expect($('a#product-video').attr('href')).toBe( '/en/video-transcripts/transcript--my-awesome-video', ) @@ -15,7 +15,7 @@ describe('transcripts', () => { describe('transcript page', () => { test('video link from transcript leads to video', async () => { - const $: cheerio.Root = await getDOM( + const $: CheerioAPI = await getDOM( '/en/get-started/video-transcripts/transcript--my-awesome-video', ) expect($('a#product-video').attr('href')).toBe('https://www.yourube.com/abc123') diff --git a/src/frame/lib/get-mini-toc-items.ts b/src/frame/lib/get-mini-toc-items.ts index a701e6bb9e98..9a370b71d98e 100644 --- a/src/frame/lib/get-mini-toc-items.ts +++ b/src/frame/lib/get-mini-toc-items.ts @@ -1,4 +1,5 @@ -import cheerio from 'cheerio' +import { load } from 'cheerio' +import type { Element } from 'domhandler' import { range } from 'lodash-es' import { renderContent } from '@/content-render/index' @@ -29,7 +30,7 @@ export default function getMiniTocItems( maxHeadingLevel = 2, headingScope = '', ): MiniTocItem[] { - const $ = cheerio.load(html, { xmlMode: true }) + const $ = load(html, { xmlMode: true }) // eg `h2, h3` or `h2, h3, h4` depending on maxHeadingLevel const selector = range(2, maxHeadingLevel + 1) @@ -48,9 +49,9 @@ export default function getMiniTocItems( const flatToc = headings .get() .filter((item) => { - if (!item.parent || !item.parent.attribs) return true - // Hide any items that belong to a hidden div - const { attribs } = item.parent + const parent = item.parent as Element | null + if (!parent || !parent.attribs) return true + const { attribs } = parent return !('hidden' in attribs) }) .map((item) => { @@ -73,7 +74,7 @@ export default function getMiniTocItems( $('strong', item).map((i, el) => $(el).replaceWith($(el).contents())) const contents: MiniTocContents = { href, title: $(item).text().trim() } - const element = $(item)[0] as cheerio.TagElement + const element = $(item)[0] as Element const headingLevel = parseInt(element.name.match(/\d+/)![0], 10) || 0 // the `2` from `h2` const platform = $(item).parent('.ghd-tool').attr('class') || '' diff --git a/src/frame/lib/page.ts b/src/frame/lib/page.ts index 5f3c619cbd8c..bd0079211673 100644 --- a/src/frame/lib/page.ts +++ b/src/frame/lib/page.ts @@ -1,7 +1,7 @@ import assert from 'assert' import path from 'path' import fs from 'fs/promises' -import cheerio from 'cheerio' +import { load } from 'cheerio' import getApplicableVersions from '@/versions/lib/get-applicable-versions' import generateRedirectsForPermalinks from '@/redirects/lib/permalinks' import getEnglishHeadings from '@/languages/lib/get-english-headings' @@ -440,7 +440,7 @@ class Page { if (!opts.unwrap) return html // The unwrap option removes surrounding tags from a string, preserving any inner HTML - const $ = cheerio.load(html, { xmlMode: true }) + const $ = load(html, { xmlMode: true }) return $.root().contents().html() || '' } diff --git a/src/frame/tests/page.ts b/src/frame/tests/page.ts index 266babf20100..591c0d8fe247 100644 --- a/src/frame/tests/page.ts +++ b/src/frame/tests/page.ts @@ -1,7 +1,7 @@ import { fileURLToPath } from 'url' import path from 'path' -import cheerio from 'cheerio' +import { load } from 'cheerio' import { beforeAll, beforeEach, describe, expect, test } from 'vitest' import Page, { FrontmatterErrorsError } from '@/frame/lib/page' @@ -97,7 +97,7 @@ describe('Page class', () => { } context.currentPath = `/${context.currentLanguage}/${context.currentVersion}/${page!.relativePath}` let rendered = await page!.render(context) - let $ = cheerio.load(rendered) + let $ = load(rendered) expect(($ as any).text()).toBe( 'This text should render on any actively supported version of Enterprise Server', ) @@ -108,7 +108,7 @@ describe('Page class', () => { context.currentVersion = `enterprise-server@${enterpriseServerReleases.oldestSupported}` context.currentPath = `/${context.currentLanguage}/${context.currentVersion}/${page!.relativePath}` rendered = await page!.render(context) - $ = cheerio.load(rendered) + $ = load(rendered) expect(($ as any).text()).toBe( 'This text should render on any actively supported version of Enterprise Server', ) @@ -119,7 +119,7 @@ describe('Page class', () => { context.currentVersion = nonEnterpriseDefaultVersion context.currentPath = `/${context.currentLanguage}/${context.currentVersion}/${page!.relativePath}` rendered = await page!.render(context) - $ = cheerio.load(rendered) + $ = load(rendered) expect(($ as any).text()).not.toBe( 'This text should render on any actively supported version of Enterprise Server', ) diff --git a/src/github-apps/data/fpt-2022-11-28/fine-grained-pat-permissions.json b/src/github-apps/data/fpt-2022-11-28/fine-grained-pat-permissions.json index dad471038bb0..4dbaa9b35236 100644 --- a/src/github-apps/data/fpt-2022-11-28/fine-grained-pat-permissions.json +++ b/src/github-apps/data/fpt-2022-11-28/fine-grained-pat-permissions.json @@ -1049,6 +1049,30 @@ } ] }, + "org_copilot_content_exclusion": { + "title": "Copilot content exclusion", + "displayTitle": "Organization permissions for \"Copilot content exclusion\"", + "permissions": [ + { + "category": "copilot", + "slug": "get-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/orgs/{org}/copilot/content_exclusion", + "additional-permissions": false, + "access": "read" + }, + { + "category": "copilot", + "slug": "set-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/orgs/{org}/copilot/content_exclusion", + "additional-permissions": false, + "access": "write" + } + ] + }, "organization_custom_org_roles": { "title": "Custom organization roles", "displayTitle": "Organization permissions for \"Custom organization roles\"", diff --git a/src/github-apps/data/fpt-2022-11-28/fine-grained-pat.json b/src/github-apps/data/fpt-2022-11-28/fine-grained-pat.json index ad360ed06e39..554eaa68ca40 100644 --- a/src/github-apps/data/fpt-2022-11-28/fine-grained-pat.json +++ b/src/github-apps/data/fpt-2022-11-28/fine-grained-pat.json @@ -2150,6 +2150,18 @@ "verb": "delete", "requestPath": "/orgs/{org}/copilot/billing/selected_users" }, + { + "slug": "get-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/orgs/{org}/copilot/content_exclusion" + }, + { + "slug": "set-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/orgs/{org}/copilot/content_exclusion" + }, { "slug": "get-copilot-metrics-for-an-organization", "subcategory": "copilot-metrics", diff --git a/src/github-apps/data/fpt-2022-11-28/server-to-server-permissions.json b/src/github-apps/data/fpt-2022-11-28/server-to-server-permissions.json index 05de203d8faf..d3e6f0b1d26b 100644 --- a/src/github-apps/data/fpt-2022-11-28/server-to-server-permissions.json +++ b/src/github-apps/data/fpt-2022-11-28/server-to-server-permissions.json @@ -1470,6 +1470,34 @@ } ] }, + "org_copilot_content_exclusion": { + "title": "Copilot content exclusion", + "displayTitle": "Organization permissions for \"Copilot content exclusion\"", + "permissions": [ + { + "category": "copilot", + "slug": "get-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/orgs/{org}/copilot/content_exclusion", + "access": "read", + "user-to-server": true, + "server-to-server": true, + "additional-permissions": false + }, + { + "category": "copilot", + "slug": "set-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/orgs/{org}/copilot/content_exclusion", + "access": "write", + "user-to-server": true, + "server-to-server": true, + "additional-permissions": false + } + ] + }, "organization_custom_org_roles": { "title": "Custom organization roles", "displayTitle": "Organization permissions for \"Custom organization roles\"", diff --git a/src/github-apps/data/fpt-2022-11-28/server-to-server-rest.json b/src/github-apps/data/fpt-2022-11-28/server-to-server-rest.json index 1c4433edc563..0077792c9f48 100644 --- a/src/github-apps/data/fpt-2022-11-28/server-to-server-rest.json +++ b/src/github-apps/data/fpt-2022-11-28/server-to-server-rest.json @@ -2120,6 +2120,18 @@ "verb": "delete", "requestPath": "/orgs/{org}/copilot/billing/selected_users" }, + { + "slug": "get-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/orgs/{org}/copilot/content_exclusion" + }, + { + "slug": "set-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/orgs/{org}/copilot/content_exclusion" + }, { "slug": "get-copilot-metrics-for-an-organization", "subcategory": "copilot-metrics", diff --git a/src/github-apps/data/fpt-2022-11-28/user-to-server-rest.json b/src/github-apps/data/fpt-2022-11-28/user-to-server-rest.json index 1ad695905991..eaeab1189f92 100644 --- a/src/github-apps/data/fpt-2022-11-28/user-to-server-rest.json +++ b/src/github-apps/data/fpt-2022-11-28/user-to-server-rest.json @@ -2386,6 +2386,18 @@ "verb": "delete", "requestPath": "/orgs/{org}/copilot/billing/selected_users" }, + { + "slug": "get-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/orgs/{org}/copilot/content_exclusion" + }, + { + "slug": "set-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/orgs/{org}/copilot/content_exclusion" + }, { "slug": "get-copilot-metrics-for-an-organization", "subcategory": "copilot-metrics", diff --git a/src/github-apps/data/ghec-2022-11-28/fine-grained-pat-permissions.json b/src/github-apps/data/ghec-2022-11-28/fine-grained-pat-permissions.json index 620df02ffe35..9edc25fbf1b2 100644 --- a/src/github-apps/data/ghec-2022-11-28/fine-grained-pat-permissions.json +++ b/src/github-apps/data/ghec-2022-11-28/fine-grained-pat-permissions.json @@ -1085,6 +1085,30 @@ } ] }, + "org_copilot_content_exclusion": { + "title": "Copilot content exclusion", + "displayTitle": "Organization permissions for \"Copilot content exclusion\"", + "permissions": [ + { + "category": "copilot", + "slug": "get-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/orgs/{org}/copilot/content_exclusion", + "additional-permissions": false, + "access": "read" + }, + { + "category": "copilot", + "slug": "set-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/orgs/{org}/copilot/content_exclusion", + "additional-permissions": false, + "access": "write" + } + ] + }, "organization_custom_org_roles": { "title": "Custom organization roles", "displayTitle": "Organization permissions for \"Custom organization roles\"", diff --git a/src/github-apps/data/ghec-2022-11-28/fine-grained-pat.json b/src/github-apps/data/ghec-2022-11-28/fine-grained-pat.json index f3aa0cfbf3bf..097807823737 100644 --- a/src/github-apps/data/ghec-2022-11-28/fine-grained-pat.json +++ b/src/github-apps/data/ghec-2022-11-28/fine-grained-pat.json @@ -2158,6 +2158,18 @@ "verb": "delete", "requestPath": "/orgs/{org}/copilot/billing/selected_users" }, + { + "slug": "get-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/orgs/{org}/copilot/content_exclusion" + }, + { + "slug": "set-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/orgs/{org}/copilot/content_exclusion" + }, { "slug": "get-copilot-metrics-for-an-organization", "subcategory": "copilot-metrics", diff --git a/src/github-apps/data/ghec-2022-11-28/server-to-server-permissions.json b/src/github-apps/data/ghec-2022-11-28/server-to-server-permissions.json index 47375ea90f4d..4a2553ed6bc0 100644 --- a/src/github-apps/data/ghec-2022-11-28/server-to-server-permissions.json +++ b/src/github-apps/data/ghec-2022-11-28/server-to-server-permissions.json @@ -187,6 +187,78 @@ } ] }, + "enterprise_ai_controls": { + "title": "Enterprise AI controls", + "displayTitle": "Enterprise permissions for \"Enterprise AI controls\"", + "permissions": [ + { + "category": "copilot", + "slug": "get-copilot-content-exclusion-rules-for-an-enterprise", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/enterprises/{enterprise}/copilot/content_exclusion", + "access": "read", + "user-to-server": true, + "server-to-server": true, + "additional-permissions": false + }, + { + "category": "copilot", + "slug": "set-copilot-content-exclusion-rules-for-an-enterprise", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/enterprises/{enterprise}/copilot/content_exclusion", + "access": "write", + "user-to-server": true, + "server-to-server": true, + "additional-permissions": false + }, + { + "category": "copilot", + "slug": "get-custom-agents-for-an-enterprise", + "subcategory": "copilot-custom-agents", + "verb": "get", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents", + "access": "read", + "user-to-server": true, + "server-to-server": true, + "additional-permissions": false + }, + { + "category": "copilot", + "slug": "get-the-source-organization-for-custom-agents-in-an-enterprise", + "subcategory": "copilot-custom-agents", + "verb": "get", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents/source", + "access": "read", + "user-to-server": true, + "server-to-server": true, + "additional-permissions": false + }, + { + "category": "copilot", + "slug": "set-the-source-organization-for-custom-agents-in-an-enterprise", + "subcategory": "copilot-custom-agents", + "verb": "put", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents/source", + "access": "write", + "user-to-server": true, + "server-to-server": true, + "additional-permissions": false + }, + { + "category": "copilot", + "slug": "delete-the-custom-agents-source-for-an-enterprise", + "subcategory": "copilot-custom-agents", + "verb": "delete", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents/source", + "access": "write", + "user-to-server": true, + "server-to-server": true, + "additional-permissions": false + } + ] + }, "enterprise_copilot_metrics": { "title": "Enterprise Copilot metrics", "displayTitle": "Enterprise permissions for \"Enterprise Copilot metrics\"", @@ -2150,6 +2222,34 @@ } ] }, + "org_copilot_content_exclusion": { + "title": "Copilot content exclusion", + "displayTitle": "Organization permissions for \"Copilot content exclusion\"", + "permissions": [ + { + "category": "copilot", + "slug": "get-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/orgs/{org}/copilot/content_exclusion", + "access": "read", + "user-to-server": true, + "server-to-server": true, + "additional-permissions": false + }, + { + "category": "copilot", + "slug": "set-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/orgs/{org}/copilot/content_exclusion", + "access": "write", + "user-to-server": true, + "server-to-server": true, + "additional-permissions": false + } + ] + }, "organization_custom_org_roles": { "title": "Custom organization roles", "displayTitle": "Organization permissions for \"Custom organization roles\"", diff --git a/src/github-apps/data/ghec-2022-11-28/server-to-server-rest.json b/src/github-apps/data/ghec-2022-11-28/server-to-server-rest.json index a057062e3a7a..11a6a08603ae 100644 --- a/src/github-apps/data/ghec-2022-11-28/server-to-server-rest.json +++ b/src/github-apps/data/ghec-2022-11-28/server-to-server-rest.json @@ -2122,6 +2122,42 @@ } ], "copilot": [ + { + "slug": "get-copilot-content-exclusion-rules-for-an-enterprise", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/enterprises/{enterprise}/copilot/content_exclusion" + }, + { + "slug": "set-copilot-content-exclusion-rules-for-an-enterprise", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/enterprises/{enterprise}/copilot/content_exclusion" + }, + { + "slug": "get-custom-agents-for-an-enterprise", + "subcategory": "copilot-custom-agents", + "verb": "get", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents" + }, + { + "slug": "get-the-source-organization-for-custom-agents-in-an-enterprise", + "subcategory": "copilot-custom-agents", + "verb": "get", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents/source" + }, + { + "slug": "set-the-source-organization-for-custom-agents-in-an-enterprise", + "subcategory": "copilot-custom-agents", + "verb": "put", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents/source" + }, + { + "slug": "delete-the-custom-agents-source-for-an-enterprise", + "subcategory": "copilot-custom-agents", + "verb": "delete", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents/source" + }, { "slug": "get-copilot-enterprise-usage-metrics-for-a-specific-day", "subcategory": "copilot-usage-metrics", @@ -2182,6 +2218,18 @@ "verb": "delete", "requestPath": "/orgs/{org}/copilot/billing/selected_users" }, + { + "slug": "get-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/orgs/{org}/copilot/content_exclusion" + }, + { + "slug": "set-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/orgs/{org}/copilot/content_exclusion" + }, { "slug": "get-copilot-metrics-for-an-organization", "subcategory": "copilot-metrics", diff --git a/src/github-apps/data/ghec-2022-11-28/user-to-server-rest.json b/src/github-apps/data/ghec-2022-11-28/user-to-server-rest.json index 5ced6190c6ad..ee0659b9658c 100644 --- a/src/github-apps/data/ghec-2022-11-28/user-to-server-rest.json +++ b/src/github-apps/data/ghec-2022-11-28/user-to-server-rest.json @@ -2370,6 +2370,42 @@ } ], "copilot": [ + { + "slug": "get-copilot-content-exclusion-rules-for-an-enterprise", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/enterprises/{enterprise}/copilot/content_exclusion" + }, + { + "slug": "set-copilot-content-exclusion-rules-for-an-enterprise", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/enterprises/{enterprise}/copilot/content_exclusion" + }, + { + "slug": "get-custom-agents-for-an-enterprise", + "subcategory": "copilot-custom-agents", + "verb": "get", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents" + }, + { + "slug": "get-the-source-organization-for-custom-agents-in-an-enterprise", + "subcategory": "copilot-custom-agents", + "verb": "get", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents/source" + }, + { + "slug": "set-the-source-organization-for-custom-agents-in-an-enterprise", + "subcategory": "copilot-custom-agents", + "verb": "put", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents/source" + }, + { + "slug": "delete-the-custom-agents-source-for-an-enterprise", + "subcategory": "copilot-custom-agents", + "verb": "delete", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents/source" + }, { "slug": "get-copilot-enterprise-usage-metrics-for-a-specific-day", "subcategory": "copilot-usage-metrics", @@ -2430,6 +2466,18 @@ "verb": "delete", "requestPath": "/orgs/{org}/copilot/billing/selected_users" }, + { + "slug": "get-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "get", + "requestPath": "/orgs/{org}/copilot/content_exclusion" + }, + { + "slug": "set-copilot-content-exclusion-rules-for-an-organization", + "subcategory": "copilot-content-exclusion-management", + "verb": "put", + "requestPath": "/orgs/{org}/copilot/content_exclusion" + }, { "slug": "get-copilot-metrics-for-an-organization", "subcategory": "copilot-metrics", diff --git a/src/github-apps/lib/config.json b/src/github-apps/lib/config.json index 4470102afcf8..59f6cee1c5b7 100644 --- a/src/github-apps/lib/config.json +++ b/src/github-apps/lib/config.json @@ -60,5 +60,5 @@ "2022-11-28" ] }, - "sha": "951b2951cf3bed81c9e881f8f470cfda20350d34" + "sha": "884c183f5163e1155700894ac68dd891e8668fd5" } \ No newline at end of file diff --git a/src/graphql/pages/breaking-changes.tsx b/src/graphql/pages/breaking-changes.tsx index 7e8734616df2..1224d0e54e75 100644 --- a/src/graphql/pages/breaking-changes.tsx +++ b/src/graphql/pages/breaking-changes.tsx @@ -1,5 +1,7 @@ import { GetServerSideProps } from 'next' import GithubSlugger from 'github-slugger' +import type { ExtendedRequest } from '@/types' +import type { ServerResponse } from 'http' import { MainContextT, MainContext, getMainContext } from '@/frame/components/context/MainContext' import { AutomatedPage } from '@/automated-pipelines/components/AutomatedPage' @@ -40,8 +42,8 @@ export const getServerSideProps: GetServerSideProps = async (context) => const { getGraphqlBreakingChanges } = await import('@/graphql/lib/index') const { getAutomatedPageMiniTocItems } = await import('@/frame/lib/get-mini-toc-items') - const req = context.req as any - const res = context.res as any + const req = context.req as unknown as ExtendedRequest + const res = context.res as unknown as ServerResponse const currentVersion = context.query.versionId as string const schema = getGraphqlBreakingChanges(currentVersion) if (!schema) throw new Error(`No graphql breaking changes schema found for ${currentVersion}`) @@ -65,7 +67,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => }), ) const titles = Object.values(headings).map((heading) => heading.title) - const changelogMiniTocItems = await getAutomatedPageMiniTocItems(titles, req.context.context, 2) + const changelogMiniTocItems = await getAutomatedPageMiniTocItems(titles, req.context!, 2) // Update the existing context to include the miniTocItems from GraphQL automatedPageContext.miniTocItems.push(...changelogMiniTocItems) diff --git a/src/graphql/pages/changelog.tsx b/src/graphql/pages/changelog.tsx index b39eb0bd1d36..8dd1ce67fa7d 100644 --- a/src/graphql/pages/changelog.tsx +++ b/src/graphql/pages/changelog.tsx @@ -1,4 +1,6 @@ import { GetServerSideProps } from 'next' +import type { ExtendedRequest } from '@/types' +import type { ServerResponse } from 'http' import { MainContextT, MainContext, getMainContext } from '@/frame/components/context/MainContext' import { AutomatedPage } from '@/automated-pipelines/components/AutomatedPage' @@ -31,8 +33,8 @@ export const getServerSideProps: GetServerSideProps = async (context) => const { getGraphqlChangelog } = await import('@/graphql/lib/index') const { getAutomatedPageMiniTocItems } = await import('@/frame/lib/get-mini-toc-items') - const req = context.req as any - const res = context.res as any + const req = context.req as unknown as ExtendedRequest + const res = context.res as unknown as ServerResponse const currentVersion = context.query.versionId as string const schema = getGraphqlChangelog(currentVersion) as ChangelogItemT[] if (!schema) throw new Error('No graphql free-pro-team changelog schema found.') @@ -41,7 +43,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => // content/graphql/reference/* const automatedPageContext = getAutomatedPageContextFromRequest(req) const titles = schema.map((item) => `Schema changes for ${item.date}`) - const changelogMiniTocItems = await getAutomatedPageMiniTocItems(titles, req.context.context, 2) + const changelogMiniTocItems = await getAutomatedPageMiniTocItems(titles, req.context!, 2) // Update the existing context to include the miniTocItems from GraphQL automatedPageContext.miniTocItems.push(...changelogMiniTocItems) diff --git a/src/graphql/pages/schema-previews.tsx b/src/graphql/pages/schema-previews.tsx index e9799f58908d..5a1ac1beb260 100644 --- a/src/graphql/pages/schema-previews.tsx +++ b/src/graphql/pages/schema-previews.tsx @@ -1,4 +1,6 @@ import { GetServerSideProps } from 'next' +import type { ExtendedRequest } from '@/types' +import type { ServerResponse } from 'http' import { MainContextT, @@ -36,8 +38,8 @@ export const getServerSideProps: GetServerSideProps = async (context) => const { getPreviews } = await import('@/graphql/lib/index') const { getAutomatedPageMiniTocItems } = await import('@/frame/lib/get-mini-toc-items') - const req = context.req as any - const res = context.res as any + const req = context.req as unknown as ExtendedRequest + const res = context.res as unknown as ServerResponse const currentVersion = context.query.versionId as string const schema = getPreviews(currentVersion) as PreviewT[] if (!schema) throw new Error(`No graphql preview schema found for ${currentVersion}`) @@ -47,7 +49,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => // content/graphql/reference/* const automatedPageContext = getAutomatedPageContextFromRequest(req) const titles = schema.map((item) => item.title) - const changelogMiniTocItems = await getAutomatedPageMiniTocItems(titles, req.context.context, 2) + const changelogMiniTocItems = await getAutomatedPageMiniTocItems(titles, req.context!, 2) // Update the existing context to include the miniTocItems from GraphQL automatedPageContext.miniTocItems.push(...changelogMiniTocItems) diff --git a/src/landings/tests/curated-homepage-links.ts b/src/landings/tests/curated-homepage-links.ts index fa94464a1792..c469743bcd3f 100644 --- a/src/landings/tests/curated-homepage-links.ts +++ b/src/landings/tests/curated-homepage-links.ts @@ -1,5 +1,5 @@ import { describe, expect, test, vi } from 'vitest' -import cheerio from 'cheerio' +import type { Element } from 'domhandler' import { getDOM } from '@/tests/helpers/e2etest' @@ -14,7 +14,7 @@ describe('curated homepage links', () => { expect($links.length).toBeGreaterThanOrEqual(6) // Check that each link is localized and includes a title and intro - $links.each((i: number, el: cheerio.Element) => { + $links.each((i: number, el: Element) => { const linkUrl = $(el).attr('href') as string expect(linkUrl.startsWith('/en/')).toBe(true) diff --git a/src/languages/lib/correct-translation-content.ts b/src/languages/lib/correct-translation-content.ts index db740df79bf1..1e8fb91a806c 100644 --- a/src/languages/lib/correct-translation-content.ts +++ b/src/languages/lib/correct-translation-content.ts @@ -27,6 +27,7 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% datos variables', '{% data variables') content = content.replaceAll('{% de datos variables', '{% data variables') content = content.replaceAll('{% datos reusables', '{% data reusables') + content = content.replaceAll('{% data reutilizables.', '{% data reusables.') content = content.replaceAll('{%- ifversion fpt o ghec %}', '{%- ifversion fpt or ghec %}') content = content.replaceAll('{% ifversion fpt o ghec %}', '{% ifversion fpt or ghec %}') } @@ -69,9 +70,16 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% данные variables.', '{% data variables.') content = content.replaceAll('{% данных reusables', '{% data reusables') content = content.replaceAll('{% данные reusables', '{% data reusables') + content = content.replaceAll('{% данных переменных.', '{% data variables.') + content = content.replaceAll('{% данных.product.', '{% data variables.product.') + content = content.replaceAll('{% data переменных.product.', '{% data variables.product.') + content = content.replaceAll('{% переменным данных.product.', '{% data variables.product.') content = content.replaceAll('{% необработанного %}', '{% raw %}') content = content.replaceAll('{%- ifversion fpt или ghec %}', '{%- ifversion fpt or ghec %}') content = content.replaceAll('{% ifversion fpt или ghec %}', '{% ifversion fpt or ghec %}') + content = content.replaceAll('{% ifversion ghec или fpt %}', '{% ifversion ghec or fpt %}') + content = content.replaceAll('{% ghes или ghec %}', '{% ifversion ghes or ghec %}') + content = content.replaceAll('{% elsif ghec или ghes %}', '{% elsif ghec or ghes %}') content = content.replaceAll('{% endif _%}', '{% endif %}') content = content.replaceAll('{% конечным %}', '{% endif %}') content = content.replaceAll('{% конец %}', '{% endif %}') @@ -81,6 +89,7 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% конечных головщиков %}', '{% endrowheaders %}') content = content.replaceAll('{% данных для повторного использования.', '{% data reusables.') content = content.replaceAll('{% еще %}', '{% else %}') + content = content.replaceAll('{% ещё %}', '{% else %}') content = content.replaceAll('{% необработанные %}', '{% raw %}') // Fix double quotes in Russian YAML files that cause parsing errors @@ -105,6 +114,7 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% données variables', '{% data variables') content = content.replaceAll('{% données réutilisables.', '{% data reusables.') content = content.replaceAll('{% variables de données.', '{% data variables.') + content = content.replaceAll('{% autre %}', '{% else %}') content = content.replaceAll('{%- ifversion fpt ou ghec %}', '{%- ifversion fpt or ghec %}') content = content.replaceAll('{% ifversion fpt ou ghec %}', '{% ifversion fpt or ghec %}') } @@ -125,6 +135,7 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{% Daten variables', '{% data variables') content = content.replaceAll('{% daten variables', '{% data variables') content = content.replaceAll('{%-Daten variables', '{%- data variables') + content = content.replaceAll('{%-Daten-variables', '{%- data variables') content = content.replaceAll('{%- ifversion fpt oder ghec %}', '{%- ifversion fpt or ghec %}') content = content.replaceAll('{% ifversion fpt oder ghec %}', '{% ifversion fpt or ghec %}') } diff --git a/src/languages/lib/get-alert-titles.ts b/src/languages/lib/get-alert-titles.ts index d73e33281c78..117c646adfc0 100644 --- a/src/languages/lib/get-alert-titles.ts +++ b/src/languages/lib/get-alert-titles.ts @@ -3,19 +3,28 @@ import path from 'path' import yaml from 'js-yaml' import languages from './languages-server' -const cache: Record = {} +interface AlertTitles { + [key: string]: string +} + +interface UiYaml { + alerts?: AlertTitles + [key: string]: unknown +} + +const cache: Record = {} -export async function getAlertTitles(page: Record) { +export async function getAlertTitles(page: { languageCode: string }) { const { languageCode } = page if (cache[languageCode]) return cache[languageCode] let file = '' - let yamlFile: Record = {} + let yamlFile: UiYaml = {} if (languageCode !== 'en') { try { const { dir } = languages[languageCode] file = await fs.readFile(path.join(dir, `data/ui.yml`), 'utf-8') - yamlFile = yaml.load(file) as Record + yamlFile = yaml.load(file) as UiYaml } catch (e) { console.warn(`Failed to load translated alert titles`, e) } @@ -23,9 +32,9 @@ export async function getAlertTitles(page: Record) { if (!file || !yamlFile.alerts) { const { dir } = languages.en file = await fs.readFile(path.join(dir, `data/ui.yml`), 'utf-8') - yamlFile = yaml.load(file) as Record + yamlFile = yaml.load(file) as UiYaml } - cache[languageCode] = yamlFile.alerts + cache[languageCode] = yamlFile.alerts ?? {} return cache[languageCode] } diff --git a/src/languages/tests/frame.ts b/src/languages/tests/frame.ts index fca75c4a18e7..669a12f19a49 100644 --- a/src/languages/tests/frame.ts +++ b/src/languages/tests/frame.ts @@ -2,6 +2,8 @@ import { describe, expect, test, vi } from 'vitest' import { languageKeys } from '@/languages/lib/languages-server' import { blockIndex } from '@/frame/middleware/block-robots' +import type { Element } from 'domhandler' + import { get, getDOMCached as getDOM } from '@/tests/helpers/e2etest' import Page from '@/frame/lib/page' @@ -17,13 +19,13 @@ describe('frame', () => { test.each(langs)('breadcrumbs link to %s pages', async (lang) => { const $ = await getDOM(`/${lang}/get-started/learning-about-github`) const $breadcrumbs = $('[data-testid=breadcrumbs-in-article] a') - expect(($breadcrumbs[0] as cheerio.TagElement).attribs.href).toBe(`/${lang}/get-started`) + expect(($breadcrumbs[0] as Element).attribs.href).toBe(`/${lang}/get-started`) }) test.each(langs)('homepage links go to %s pages', async (lang) => { const $ = await getDOM(`/${lang}`) const $links = $('[data-testid=bump-link]') - $links.each((i: number, el: cheerio.Element) => { + $links.each((i: number, el: Element) => { const linkUrl = $(el).attr('href') expect((linkUrl || '').startsWith(`/${lang}/`)).toBe(true) }) diff --git a/src/links/lib/excluded-links.yml b/src/links/lib/excluded-links.yml index 42e1267592fb..f606031cd003 100644 --- a/src/links/lib/excluded-links.yml +++ b/src/links/lib/excluded-links.yml @@ -70,8 +70,8 @@ - is: https://moodle.org - is: https://azure.microsoft.com - is: https://api.octocorp.ghe.com -- is: https://platform.openai.com/docs/guides/safety-best-practices -- is: https://platform.openai.com/docs/guides/function-calling +- startsWith: https://platform.openai.com/docs +- startsWith: https://openai.com - is: https://global.rel.tunnels.api.visualstudio.com/api/version - is: https://www.wireguard.com/quickstart/ - is: https://docs.openstack.org/horizon/latest/ @@ -85,8 +85,6 @@ - is: https://jsonformatter.org/ - is: https://mvnrepository.com/artifact/org.xwiki.platform/xwiki-platform-oldcore - is: https://mvnrepository.com/artifact/com.google.guava/guava -- startsWith: https://platform.openai.com/docs/models -- startsWith: https://openai.com/index - is: https://github.com/github-linguist/linguist/compare/master...octocat:master - is: https://www.servicenow.com/docs/bundle/utah-devops/page/product/enterprise-dev-ops/concept/github-integration-dev-ops.html - startsWith: https://www.ilo.org @@ -95,8 +93,7 @@ - is: https://www.nongnu.org/oath-toolkit/man-oathtool.html - is: https://www.gnu.org/software/emacs/ - is: https://www.transparency.org/what-is-corruption -- startsWith: https://platform.openai.com/docs/api-reference/ -- is: https://azuredownloads-g3ahgwb5b8bkbxhd.b01.azurefd.net/github-copilot/ +- startsWith: https://azuredownloads-g3ahgwb5b8bkbxhd.b01.azurefd.net/github-copilot/ - is: https://www.anthropic.com/claude/sonnet - is: https://www.psiexams.com/become-psi-test-center/computer-specifications/ - is: https://www.buymeacoffee.com/ @@ -115,3 +112,21 @@ - is: https://collectd.org/documentation/manpages/collectd.conf.html#plugin-fhcount - is: https://mywiki.wooledge.org/BashPitfalls - startsWith: https://code.visualstudio.com/docs/configure/telemetry + +# npmjs.com blocks automated link checkers with 403. +- startsWith: https://www.npmjs.com + +# Azure Marketplace blocks automated link checkers with 403. +- startsWith: https://azuremarketplace.microsoft.com + +# Splunk docs blocks automated link checkers with 403. +- startsWith: https://docs.splunk.com + +# HashiCorp rate-limits automated requests (429). +- startsWith: https://www.hashicorp.com + +# ISO website blocks automated link checkers with 403. +- startsWith: https://www.iso.org + +# Example domain used in style guide documentation. +- startsWith: https://some-docs.com diff --git a/src/links/lib/extract-links.ts b/src/links/lib/extract-links.ts index c2e973736cea..d6a5c79f32a1 100644 --- a/src/links/lib/extract-links.ts +++ b/src/links/lib/extract-links.ts @@ -16,7 +16,8 @@ import type { Context, Page } from '@/types' // Link patterns for Markdown const INTERNAL_LINK_PATTERN = /\]\(\/[^)]+\)/g const AUTOTITLE_LINK_PATTERN = /\[AUTOTITLE\]\(([^)]+)\)/g -const EXTERNAL_LINK_PATTERN = /\]\((https?:\/\/[^)]+)\)/g +// Handles one level of balanced parentheses in URLs (e.g., Wikipedia links) +const EXTERNAL_LINK_PATTERN = /\]\((https?:\/\/(?:[^()\s]+|\([^()]*\))*)\)/g const IMAGE_LINK_PATTERN = /!\[[^\]]*\]\(([^)]+)\)/g // Anchor link patterns (for same-page links) @@ -82,10 +83,19 @@ export function extractLinksFromMarkdown(content: string): LinkExtractionResult const anchorLinks: ExtractedLink[] = [] const imageLinks: ExtractedLink[] = [] + // Strip fenced code blocks to avoid checking example/placeholder URLs + // Replaces non-newline characters with spaces to preserve line numbers and positions + const strippedContent = content.replace( + /^ {0,3}(`{3,})[^\n]*\n[\s\S]*?^ {0,3}\1\s*$/gm, + (match) => { + return match.replace(/[^\n]/g, ' ') + }, + ) + // Extract AUTOTITLE links first (they're a special case of internal links) let match - while ((match = AUTOTITLE_LINK_PATTERN.exec(content)) !== null) { - const { line, column } = getLineAndColumn(content, match.index) + while ((match = AUTOTITLE_LINK_PATTERN.exec(strippedContent)) !== null) { + const { line, column } = getLineAndColumn(strippedContent, match.index) const href = match[1].split('#')[0] // Remove anchor if present if (href.startsWith('/')) { internalLinks.push({ @@ -102,17 +112,17 @@ export function extractLinksFromMarkdown(content: string): LinkExtractionResult AUTOTITLE_LINK_PATTERN.lastIndex = 0 // Extract regular internal links - while ((match = INTERNAL_LINK_PATTERN.exec(content)) !== null) { + while ((match = INTERNAL_LINK_PATTERN.exec(strippedContent)) !== null) { // Skip if this is an AUTOTITLE link (already captured) const fullMatch = match[0] - if (content.substring(match.index - 10, match.index).includes('AUTOTITLE')) { + if (strippedContent.substring(match.index - 10, match.index).includes('AUTOTITLE')) { continue } - const { line, column } = getLineAndColumn(content, match.index) + const { line, column } = getLineAndColumn(strippedContent, match.index) // Extract href from ](/path) format const href = fullMatch.substring(2, fullMatch.length - 1).split('#')[0] - const text = extractLinkText(content, match.index) + const text = extractLinkText(strippedContent, match.index) internalLinks.push({ href, @@ -127,10 +137,10 @@ export function extractLinksFromMarkdown(content: string): LinkExtractionResult INTERNAL_LINK_PATTERN.lastIndex = 0 // Extract external links - while ((match = EXTERNAL_LINK_PATTERN.exec(content)) !== null) { - const { line, column } = getLineAndColumn(content, match.index) + while ((match = EXTERNAL_LINK_PATTERN.exec(strippedContent)) !== null) { + const { line, column } = getLineAndColumn(strippedContent, match.index) const href = match[1] - const text = extractLinkText(content, match.index) + const text = extractLinkText(strippedContent, match.index) externalLinks.push({ href, @@ -144,8 +154,8 @@ export function extractLinksFromMarkdown(content: string): LinkExtractionResult EXTERNAL_LINK_PATTERN.lastIndex = 0 // Extract anchor links - while ((match = ANCHOR_LINK_PATTERN.exec(content)) !== null) { - const { line, column } = getLineAndColumn(content, match.index) + while ((match = ANCHOR_LINK_PATTERN.exec(strippedContent)) !== null) { + const { line, column } = getLineAndColumn(strippedContent, match.index) const href = match[0].substring(2, match[0].length - 1) anchorLinks.push({ @@ -160,8 +170,8 @@ export function extractLinksFromMarkdown(content: string): LinkExtractionResult ANCHOR_LINK_PATTERN.lastIndex = 0 // Extract image links - while ((match = IMAGE_LINK_PATTERN.exec(content)) !== null) { - const { line, column } = getLineAndColumn(content, match.index) + while ((match = IMAGE_LINK_PATTERN.exec(strippedContent)) !== null) { + const { line, column } = getLineAndColumn(strippedContent, match.index) const href = match[1] // Only include internal images (starting with /) @@ -345,6 +355,19 @@ export function checkInternalLink( } } + // Strip language prefix and check redirects (which are stored without it) + const langPrefixMatch = resolved.match(/^\/[a-z]{2}\//) + if (langPrefixMatch) { + const withoutLang = resolved.slice(langPrefixMatch[0].length - 1) + if (redirects[withoutLang]) { + return { + exists: true, + isRedirect: true, + redirectTarget: redirects[withoutLang], + } + } + } + return { exists: false, isRedirect: false } } diff --git a/src/links/lib/validate-docs-urls.ts b/src/links/lib/validate-docs-urls.ts index 886dd43a5b15..fbcc7e265525 100644 --- a/src/links/lib/validate-docs-urls.ts +++ b/src/links/lib/validate-docs-urls.ts @@ -1,5 +1,5 @@ import type { Response } from 'express' -import cheerio from 'cheerio' +import { load } from 'cheerio' import warmServer from '@/frame/lib/warm-server' import { liquid } from '@/content-render/index' @@ -89,7 +89,7 @@ export async function validateDocsUrl(docsUrls: DocsUrls, { checkFragments = fal if (checkFragments && fragment) { const permalink = (redirectedPage || page).permalinks[0] const html = await renderInnerHTML(redirectedPage || page, permalink) - const $ = cheerio.load(html) + const $ = load(html) check.fragmentFound = $(`#${fragment}`).length > 0 || $(`a[name="${fragment}"]`).length > 0 if (!check.fragmentFound) { const fragmentCandidates: string[] = [] diff --git a/src/links/scripts/check-links-internal.ts b/src/links/scripts/check-links-internal.ts index 12a9c7ad98b9..a7a1b9c0da28 100644 --- a/src/links/scripts/check-links-internal.ts +++ b/src/links/scripts/check-links-internal.ts @@ -20,7 +20,7 @@ import { program } from 'commander' import chalk from 'chalk' -import cheerio from 'cheerio' +import { load } from 'cheerio' import warmServer from '@/frame/lib/warm-server' import { renderContent } from '@/content-render/index' @@ -73,7 +73,7 @@ async function getLinksFromRenderedPage( try { // Render the page content const html = await renderContent(page.markdown, context) - const $ = cheerio.load(html) + const $ = load(html) // Extract all anchor links $('a[href]').each((_, el) => { @@ -103,7 +103,7 @@ async function checkAnchorsOnPage( try { const html = await renderContent(page.markdown, context) - const $ = cheerio.load(html) + const $ = load(html) // Find all anchor links (same-page links) $('a[href^="#"]').each((_, el) => { diff --git a/src/links/tests/extract-links.ts b/src/links/tests/extract-links.ts index eb75e6bde958..6465ae4c7b01 100644 --- a/src/links/tests/extract-links.ts +++ b/src/links/tests/extract-links.ts @@ -140,6 +140,75 @@ Also [versioned](/enterprise-server@{{ currentVersion }}/admin). expect(result.internalLinks.length).toBeGreaterThanOrEqual(0) }) + test('extracts external links with parentheses in URLs', () => { + const content = ` +See the [shebang article](https://en.wikipedia.org/wiki/Shebang_(Unix)) for more. +Also [Continuum](https://en.wikipedia.org/wiki/Continuum_(measurement)) is relevant. +` + const result = extractLinksFromMarkdown(content) + + expect(result.externalLinks).toHaveLength(2) + expect(result.externalLinks[0].href).toBe('https://en.wikipedia.org/wiki/Shebang_(Unix)') + expect(result.externalLinks[1].href).toBe( + 'https://en.wikipedia.org/wiki/Continuum_(measurement)', + ) + }) + + test('skips links inside fenced code blocks', () => { + const content = ` +Here is [a real link](https://example.com). + +\`\`\`yaml +![badge](https://github.com/octocat/repo/actions/workflows/ci.yml/badge.svg) +[example](https://fake-example.com/not-real) +\`\`\` + +And [another real link](https://real.example.com/page). +` + const result = extractLinksFromMarkdown(content) + + expect(result.externalLinks).toHaveLength(2) + expect(result.externalLinks[0].href).toBe('https://example.com') + expect(result.externalLinks[1].href).toBe('https://real.example.com/page') + }) + + test('preserves correct line numbers when code blocks are stripped', () => { + const content = `Line 1 +[Link on line 2](/path/one) +\`\`\` +code block on line 3 +code block on line 4 +\`\`\` +Line 6 +[Link on line 8](/path/two) +` + const result = extractLinksFromMarkdown(content) + + expect(result.internalLinks).toHaveLength(2) + expect(result.internalLinks[0].line).toBe(2) + // Line numbers are preserved because code block content is replaced with spaces + expect(result.internalLinks[1].line).toBe(8) + }) + + test('skips links inside indented fenced code blocks', () => { + const content = ` +Here is [a real link](https://example.com). + +1. Step one: + + \`\`\`yaml + [example](https://fake-example.com/not-real) + \`\`\` + +And [another real link](https://real.example.com/page). +` + const result = extractLinksFromMarkdown(content) + + expect(result.externalLinks).toHaveLength(2) + expect(result.externalLinks[0].href).toBe('https://example.com') + expect(result.externalLinks[1].href).toBe('https://real.example.com/page') + }) + test('handles complex nested brackets', () => { const content = ` Use the [\`git clone\`](/repositories/cloning) command. @@ -186,6 +255,8 @@ describe('checkInternalLink', () => { const redirects = { '/en/old-path': '/en/new-path', '/en/deprecated': '/en/current', + '/enterprise-server@3.19/actions/old-path': '/enterprise-server@3.19/actions/new-path', + '/actions/legacy-path': '/actions/current-path', } test('finds direct page match', () => { @@ -233,6 +304,25 @@ describe('checkInternalLink', () => { const result = checkInternalLink('/enterprise-server@latest/does/not/exist', pageMap, redirects) expect(result.exists).toBe(false) }) + + test('finds redirect after stripping language prefix', () => { + // Links from rendered HTML have /en/ prefix but redirects are stored without it + const result = checkInternalLink( + '/en/enterprise-server@3.19/actions/old-path', + pageMap, + redirects, + ) + expect(result.exists).toBe(true) + expect(result.isRedirect).toBe(true) + expect(result.redirectTarget).toBe('/enterprise-server@3.19/actions/new-path') + }) + + test('finds versionless redirect after stripping language prefix', () => { + const result = checkInternalLink('/en/actions/legacy-path', pageMap, redirects) + expect(result.exists).toBe(true) + expect(result.isRedirect).toBe(true) + expect(result.redirectTarget).toBe('/actions/current-path') + }) }) describe('isAssetLink', () => { diff --git a/src/rest/data/fpt-2022-11-28/schema.json b/src/rest/data/fpt-2022-11-28/schema.json index 3bb4d41d8a4c..6b219e0a1f17 100644 --- a/src/rest/data/fpt-2022-11-28/schema.json +++ b/src/rest/data/fpt-2022-11-28/schema.json @@ -264876,6 +264876,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -265201,6 +265210,18 @@ ], "default": "disabled" }, + { + "type": "string", + "name": "secret_scanning_extended_metadata", + "in": "body", + "description": "

The enablement status of secret scanning extended metadata

", + "enum": [ + "enabled", + "disabled", + "not_set" + ], + "default": "disabled" + }, { "type": "string", "name": "private_vulnerability_reporting", @@ -265533,6 +265554,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -265984,6 +266014,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -266369,6 +266408,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -266693,6 +266741,18 @@ ], "default": "disabled" }, + { + "type": "string", + "name": "secret_scanning_extended_metadata", + "in": "body", + "description": "

The enablement status of secret scanning extended metadata

", + "enum": [ + "enabled", + "disabled", + "not_set" + ], + "default": "disabled" + }, { "type": "string", "name": "private_vulnerability_reporting", @@ -267021,6 +267081,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -267630,6 +267699,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -268851,6 +268929,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -269234,6 +269321,17 @@ "not_set" ] }, + { + "type": "string", + "name": "secret_scanning_extended_metadata", + "in": "body", + "description": "

The enablement status of secret scanning extended metadata

", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, { "type": "string", "name": "private_vulnerability_reporting", @@ -269573,6 +269671,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -270016,6 +270123,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -270508,6 +270624,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -270887,6 +271012,17 @@ "not_set" ] }, + { + "type": "string", + "name": "secret_scanning_extended_metadata", + "in": "body", + "description": "

The enablement status of secret scanning extended metadata

", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, { "type": "string", "name": "private_vulnerability_reporting", @@ -271222,6 +271358,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -271832,6 +271977,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -273010,6 +273164,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -335628,6 +335791,192 @@ ] }, "copilot": { + "copilot-content-exclusion-management": [ + { + "serverUrl": "https://api.github.com", + "verb": "get", + "requestPath": "/orgs/{org}/copilot/content_exclusion", + "title": "Get Copilot content exclusion rules for an organization", + "category": "copilot", + "subcategory": "copilot-content-exclusion-management", + "parameters": [ + { + "name": "org", + "description": "

The organization name. The name is not case sensitive.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "bodyParameters": [], + "descriptionHTML": "

Note

\n

\nThis endpoint is in public preview and is subject to change.

\n
\n

Gets information about an organization's Copilot content exclusion path rules.\nTo configure these settings, go to the organization's settings on GitHub.\nFor more information, see \"Excluding content from GitHub Copilot.\"

\n

Organization owners can view details about Copilot content exclusion rules for the organization.

\n

OAuth app tokens and personal access tokens (classic) need either the copilot or read:org scopes to use this endpoint.

\n

Caution

\n

\n
    \n
  • At this time, the API does not support comments. This endpoint will not return any comments in the existing rules.
  • \n
  • At this time, the API does not support duplicate keys. If your content exclusion configuration contains duplicate keys, the API will return only the last occurrence of that key. For example, if duplicate entries are present, only the final value will be included in the response.
  • \n
\n
", + "codeExamples": [ + { + "key": "default", + "request": { + "description": "Example", + "acceptHeader": "application/vnd.github.v3+json", + "parameters": { + "org": "ORG" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

OK

", + "example": { + "octo-repo": [ + "/src/some-dir/kernel.rs" + ] + }, + "schema": { + "title": "Copilot Organization Content Exclusion Details", + "description": "List all Copilot Content Exclusion rules for an organization.", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string", + "description": "The path to the file that will be excluded." + } + } + } + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "200", + "description": "

OK

" + }, + { + "httpStatusCode": "401", + "description": "

Requires authentication

" + }, + { + "httpStatusCode": "403", + "description": "

Forbidden

" + }, + { + "httpStatusCode": "404", + "description": "

Resource not found

" + }, + { + "httpStatusCode": "500", + "description": "

Internal Error

" + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": true, + "permissions": [ + { + "\"Copilot content exclusion\" organization permissions": "read" + } + ] + } + }, + { + "serverUrl": "https://api.github.com", + "verb": "put", + "requestPath": "/orgs/{org}/copilot/content_exclusion", + "title": "Set Copilot content exclusion rules for an organization", + "category": "copilot", + "subcategory": "copilot-content-exclusion-management", + "parameters": [ + { + "name": "org", + "description": "

The organization name. The name is not case sensitive.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "bodyParameters": [], + "descriptionHTML": "

Note

\n

\nThis endpoint is in public preview and is subject to change.

\n
\n

Sets Copilot content exclusion path rules for an organization.\nTo configure these settings, go to the organization's settings on GitHub.\nFor more information, see \"Excluding content from GitHub Copilot.\"

\n

Organization owners can set Copilot content exclusion rules for the organization.

\n

OAuth app tokens and personal access tokens (classic) need the copilot scope to use this endpoint.

\n

Caution

\n

\n
    \n
  • At this time, the API does not support comments. When using this endpoint, any existing comments in your rules will be deleted.
  • \n
  • At this time, the API does not support duplicate keys. If you submit content exclusions through the API with duplicate keys, only the last occurrence will be saved. Earlier entries with the same key will be overwritten.
  • \n
\n
", + "codeExamples": [ + { + "key": "default", + "request": { + "contentType": "application/json", + "description": "Example of content exclusion paths", + "acceptHeader": "application/vnd.github.v3+json", + "bodyParameters": { + "octo-repo": [ + "/src/some-dir/kernel.rs" + ] + }, + "parameters": { + "org": "ORG" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

Success

", + "example": { + "message": "Content exclusion rules updated successfully." + }, + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "200", + "description": "

Success

" + }, + { + "httpStatusCode": "401", + "description": "

Requires authentication

" + }, + { + "httpStatusCode": "403", + "description": "

Forbidden

" + }, + { + "httpStatusCode": "404", + "description": "

Resource not found

" + }, + { + "httpStatusCode": "413", + "description": "

Payload Too Large

" + }, + { + "httpStatusCode": "422", + "description": "

Validation failed, or the endpoint has been spammed.

" + }, + { + "httpStatusCode": "500", + "description": "

Internal Error

" + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": true, + "permissions": [ + { + "\"Copilot content exclusion\" organization permissions": "write" + } + ] + } + } + ], "copilot-metrics": [ { "serverUrl": "https://api.github.com", @@ -339284,6 +339633,14 @@ ] } }, + { + "name": "assignee", + "in": "query", + "description": "

Filter alerts by assignees.\nProvide a comma-separated list of user handles (e.g., octocat or octocat,hubot) to return alerts assigned to any of the specified users.\nUse * to list alerts with at least one assignee or none to list alerts with no assignees.

", + "schema": { + "type": "string" + } + }, { "name": "scope", "in": "query", @@ -339506,6 +339863,28 @@ "dismissed_reason": "tolerable_risk", "dismissed_comment": "This alert is accurate but we use a sanitizer.", "fixed_at": null, + "assignees": [ + { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + } + ], "repository": { "id": 217723378, "node_id": "MDEwOlJlcG9zaXRvcnkyMTc3MjMzNzg=", @@ -339691,6 +340070,7 @@ "dismissed_reason": null, "dismissed_comment": null, "fixed_at": null, + "assignees": [], "repository": { "id": 664700648, "node_id": "MDEwOlJlcG9zaXRvcnk2NjQ3MDA2NDg=", @@ -341432,6 +341812,14 @@ ] } }, + { + "name": "assignee", + "in": "query", + "description": "

Filter alerts by assignees.\nProvide a comma-separated list of user handles (e.g., octocat or octocat,hubot) to return alerts assigned to any of the specified users.\nUse * to list alerts with at least one assignee or none to list alerts with no assignees.

", + "schema": { + "type": "string" + } + }, { "name": "runtime_risk", "in": "query", @@ -341662,6 +342050,28 @@ "dismissed_reason": "tolerable_risk", "dismissed_comment": "This alert is accurate but we use a sanitizer.", "fixed_at": null, + "assignees": [ + { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + } + ], "repository": { "id": 217723378, "node_id": "MDEwOlJlcG9zaXRvcnkyMTc3MjMzNzg=", @@ -341847,6 +342257,7 @@ "dismissed_reason": null, "dismissed_comment": null, "fixed_at": null, + "assignees": [], "repository": { "id": 664700648, "node_id": "MDEwOlJlcG9zaXRvcnk2NjQ3MDA2NDg=", @@ -343596,6 +344007,14 @@ ] } }, + { + "name": "assignee", + "in": "query", + "description": "

Filter alerts by assignees.\nProvide a comma-separated list of user handles (e.g., octocat or octocat,hubot) to return alerts assigned to any of the specified users.\nUse * to list alerts with at least one assignee or none to list alerts with no assignees.

", + "schema": { + "type": "string" + } + }, { "name": "scope", "in": "query", @@ -343818,7 +344237,29 @@ }, "dismissed_reason": "tolerable_risk", "dismissed_comment": "This alert is accurate but we use a sanitizer.", - "fixed_at": null + "fixed_at": null, + "assignees": [ + { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + } + ] }, { "number": 1, @@ -343936,7 +344377,8 @@ "dismissed_by": null, "dismissed_reason": null, "dismissed_comment": null, - "fixed_at": null + "fixed_at": null, + "assignees": [] } ], "schema": { @@ -345150,7 +345592,29 @@ "dismissed_by": null, "dismissed_reason": null, "dismissed_comment": null, - "fixed_at": null + "fixed_at": null, + "assignees": [ + { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + } + ] }, "schema": { "type": "object", @@ -346402,7 +346866,8 @@ }, "dismissed_reason": "tolerable_risk", "dismissed_comment": "This alert is accurate but we use a sanitizer.", - "fixed_at": null + "fixed_at": null, + "assignees": [] }, "schema": { "type": "object", @@ -348750,7 +349215,7 @@ "type": "array", "name": "selected_repository_ids", "in": "body", - "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints. Use integers when possible, as strings are supported only to maintain backwards compatibility and may be removed in the future.

" + "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints.

" } ], "descriptionHTML": "

Creates or updates an organization secret with an encrypted value. Encrypt your secret using\nLibSodium. For more information, see \"Encrypting secrets for the REST API.\"

\n

OAuth app tokens and personal access tokens (classic) need the admin:org scope to use this endpoint.

", @@ -542067,7 +542532,7 @@ "schema": { "type": "string", "minLength": 1, - "maxLength": 64, + "maxLength": 128, "pattern": "^[a-zA-Z0-9._-]+$" } } @@ -562597,6 +563062,10 @@ "httpStatusCode": "404", "description": "

Resource not found

" }, + { + "httpStatusCode": "422", + "description": "

Validation failed, or the endpoint has been spammed.

" + }, { "httpStatusCode": "500", "description": "

Internal Error

" @@ -566614,6 +567083,10 @@ "httpStatusCode": "404", "description": "

Resource not found

" }, + { + "httpStatusCode": "422", + "description": "

Validation failed, or the endpoint has been spammed.

" + }, { "httpStatusCode": "500", "description": "

Internal Error

" @@ -788176,6 +788649,10 @@ "httpStatusCode": "404", "description": "

Resource not found

" }, + { + "httpStatusCode": "422", + "description": "

Validation failed, or the endpoint has been spammed.

" + }, { "httpStatusCode": "500", "description": "

Internal Error

" @@ -792111,6 +792588,10 @@ "httpStatusCode": "404", "description": "

Resource not found

" }, + { + "httpStatusCode": "422", + "description": "

Validation failed, or the endpoint has been spammed.

" + }, { "httpStatusCode": "500", "description": "

Internal Error

" @@ -838932,6 +839413,10 @@ { "httpStatusCode": "204", "description": "

No Content

" + }, + { + "httpStatusCode": "422", + "description": "

Unprocessable entity if you attempt to modify an enterprise team at the organization level.

" } ], "previews": [], @@ -848185,6 +848670,10 @@ { "httpStatusCode": "200", "description": "

OK

" + }, + { + "httpStatusCode": "422", + "description": "

Unprocessable entity if you attempt to modify an enterprise team at the organization level.

" } ], "previews": [], diff --git a/src/rest/data/ghec-2022-11-28/schema.json b/src/rest/data/ghec-2022-11-28/schema.json index e6019d9cffc3..7573af1b959e 100644 --- a/src/rest/data/ghec-2022-11-28/schema.json +++ b/src/rest/data/ghec-2022-11-28/schema.json @@ -281561,6 +281561,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -281886,6 +281895,18 @@ ], "default": "disabled" }, + { + "type": "string", + "name": "secret_scanning_extended_metadata", + "in": "body", + "description": "

The enablement status of secret scanning extended metadata

", + "enum": [ + "enabled", + "disabled", + "not_set" + ], + "default": "disabled" + }, { "type": "string", "name": "private_vulnerability_reporting", @@ -282218,6 +282239,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -282669,6 +282699,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -283054,6 +283093,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -283378,6 +283426,18 @@ ], "default": "disabled" }, + { + "type": "string", + "name": "secret_scanning_extended_metadata", + "in": "body", + "description": "

The enablement status of secret scanning extended metadata

", + "enum": [ + "enabled", + "disabled", + "not_set" + ], + "default": "disabled" + }, { "type": "string", "name": "private_vulnerability_reporting", @@ -283706,6 +283766,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -284315,6 +284384,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -285536,6 +285614,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -285919,6 +286006,17 @@ "not_set" ] }, + { + "type": "string", + "name": "secret_scanning_extended_metadata", + "in": "body", + "description": "

The enablement status of secret scanning extended metadata

", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, { "type": "string", "name": "private_vulnerability_reporting", @@ -286258,6 +286356,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -286701,6 +286808,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -287193,6 +287309,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -287572,6 +287697,17 @@ "not_set" ] }, + { + "type": "string", + "name": "secret_scanning_extended_metadata", + "in": "body", + "description": "

The enablement status of secret scanning extended metadata

", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, { "type": "string", "name": "private_vulnerability_reporting", @@ -287907,6 +288043,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -288517,6 +288662,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -289695,6 +289849,15 @@ "not_set" ] }, + "secret_scanning_extended_metadata": { + "type": "string", + "description": "The enablement status of secret scanning extended metadata", + "enum": [ + "enabled", + "disabled", + "not_set" + ] + }, "private_vulnerability_reporting": { "type": "string", "description": "The enablement status of private vulnerability reporting", @@ -352589,6 +352752,923 @@ ] }, "copilot": { + "copilot-content-exclusion-management": [ + { + "serverUrl": "https://api.github.com", + "verb": "get", + "requestPath": "/enterprises/{enterprise}/copilot/content_exclusion", + "title": "Get Copilot content exclusion rules for an enterprise", + "category": "copilot", + "subcategory": "copilot-content-exclusion-management", + "parameters": [ + { + "name": "enterprise", + "description": "

The slug version of the enterprise name.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "bodyParameters": [], + "descriptionHTML": "

Note

\n

\nThis endpoint is in public preview and is subject to change.

\n
\n

Gets information about an enterprise's Copilot content exclusion path rules.\nTo configure these settings, go to the enterprise's settings on GitHub.\nFor more information, see \"Excluding content from GitHub Copilot.\"

\n

Enterprise owners can view details about Copilot content exclusion rules for the enterprise.

\n

OAuth app tokens and personal access tokens (classic) need either the read:enterprise or copilot scopes to use this endpoint.

\n

Caution

\n

\n
    \n
  • At this time, the API does not support comments. This endpoint will not return any comments in the existing rules.
  • \n
  • At this time, the API does not support duplicate keys. If your content exclusion configuration contains duplicate keys, the API will return only the last occurrence of that key. For example, if duplicate entries are present, only the final value will be included in the response.
  • \n
\n
", + "codeExamples": [ + { + "key": "default", + "request": { + "description": "Example", + "acceptHeader": "application/vnd.github.v3+json", + "parameters": { + "enterprise": "ENTERPRISE" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

OK

", + "example": { + "git@github.com:*/copilot": [ + "/__tests__/**" + ], + "octo-org/octo-repo": [ + "/src/some-dir/kernel.rs" + ] + }, + "schema": { + "title": "Copilot Enterprise Content Exclusion Details", + "description": "List all Copilot Content Exclusion rules for an enterprise.", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string", + "description": "The path to the file that will be excluded." + } + } + } + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "200", + "description": "

OK

" + }, + { + "httpStatusCode": "401", + "description": "

Requires authentication

" + }, + { + "httpStatusCode": "403", + "description": "

Forbidden

" + }, + { + "httpStatusCode": "404", + "description": "

Resource not found

" + }, + { + "httpStatusCode": "500", + "description": "

Internal Error

" + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": false, + "permissions": [ + { + "\"Enterprise AI controls\" enterprise permissions": "read" + } + ], + "disabledForPatV2": true + } + }, + { + "serverUrl": "https://api.github.com", + "verb": "put", + "requestPath": "/enterprises/{enterprise}/copilot/content_exclusion", + "title": "Set Copilot content exclusion rules for an enterprise", + "category": "copilot", + "subcategory": "copilot-content-exclusion-management", + "parameters": [ + { + "name": "enterprise", + "description": "

The slug version of the enterprise name.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "bodyParameters": [], + "descriptionHTML": "

Note

\n

\nThis endpoint is in public preview and is subject to change.

\n
\n

Sets Copilot content exclusion path rules for an enterprise.\nTo configure these settings, go to the enterprise's settings on GitHub.\nFor more information, see \"Excluding content from GitHub Copilot.\"

\n

Enterprise owners can set Copilot content exclusion rules for the enterprise.

\n

OAuth app tokens and personal access tokens (classic) need either the admin:enterprise or copilot scopes to use this endpoint.

\n

Caution

\n

\n
    \n
  • At this time, the API does not support comments. When using this endpoint, any existing comments in your rules will be deleted.
  • \n
  • At this time, the API does not support duplicate keys. If you submit content exclusions through the API with duplicate keys, only the last occurrence will be saved. Earlier entries with the same key will be overwritten.
  • \n
\n
", + "codeExamples": [ + { + "key": "default", + "request": { + "contentType": "application/json", + "description": "Example of content exclusion paths", + "acceptHeader": "application/vnd.github.v3+json", + "bodyParameters": { + "git@github.com:*/copilot": [ + "/__tests__/**" + ], + "octo-org/octo-repo": [ + "/src/some-dir/kernel.rs" + ] + }, + "parameters": { + "enterprise": "ENTERPRISE" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

Success

", + "example": { + "message": "Content exclusion rules updated successfully." + }, + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "200", + "description": "

Success

" + }, + { + "httpStatusCode": "401", + "description": "

Requires authentication

" + }, + { + "httpStatusCode": "403", + "description": "

Forbidden

" + }, + { + "httpStatusCode": "404", + "description": "

Resource not found

" + }, + { + "httpStatusCode": "413", + "description": "

Payload Too Large

" + }, + { + "httpStatusCode": "422", + "description": "

Validation failed, or the endpoint has been spammed.

" + }, + { + "httpStatusCode": "500", + "description": "

Internal Error

" + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": false, + "permissions": [ + { + "\"Enterprise AI controls\" enterprise permissions": "write" + } + ], + "disabledForPatV2": true + } + }, + { + "serverUrl": "https://api.github.com", + "verb": "get", + "requestPath": "/orgs/{org}/copilot/content_exclusion", + "title": "Get Copilot content exclusion rules for an organization", + "category": "copilot", + "subcategory": "copilot-content-exclusion-management", + "parameters": [ + { + "name": "org", + "description": "

The organization name. The name is not case sensitive.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "bodyParameters": [], + "descriptionHTML": "

Note

\n

\nThis endpoint is in public preview and is subject to change.

\n
\n

Gets information about an organization's Copilot content exclusion path rules.\nTo configure these settings, go to the organization's settings on GitHub.\nFor more information, see \"Excluding content from GitHub Copilot.\"

\n

Organization owners can view details about Copilot content exclusion rules for the organization.

\n

OAuth app tokens and personal access tokens (classic) need either the copilot or read:org scopes to use this endpoint.

\n

Caution

\n

\n
    \n
  • At this time, the API does not support comments. This endpoint will not return any comments in the existing rules.
  • \n
  • At this time, the API does not support duplicate keys. If your content exclusion configuration contains duplicate keys, the API will return only the last occurrence of that key. For example, if duplicate entries are present, only the final value will be included in the response.
  • \n
\n
", + "codeExamples": [ + { + "key": "default", + "request": { + "description": "Example", + "acceptHeader": "application/vnd.github.v3+json", + "parameters": { + "org": "ORG" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

OK

", + "example": { + "octo-repo": [ + "/src/some-dir/kernel.rs" + ] + }, + "schema": { + "title": "Copilot Organization Content Exclusion Details", + "description": "List all Copilot Content Exclusion rules for an organization.", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string", + "description": "The path to the file that will be excluded." + } + } + } + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "200", + "description": "

OK

" + }, + { + "httpStatusCode": "401", + "description": "

Requires authentication

" + }, + { + "httpStatusCode": "403", + "description": "

Forbidden

" + }, + { + "httpStatusCode": "404", + "description": "

Resource not found

" + }, + { + "httpStatusCode": "500", + "description": "

Internal Error

" + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": true, + "permissions": [ + { + "\"Copilot content exclusion\" organization permissions": "read" + } + ] + } + }, + { + "serverUrl": "https://api.github.com", + "verb": "put", + "requestPath": "/orgs/{org}/copilot/content_exclusion", + "title": "Set Copilot content exclusion rules for an organization", + "category": "copilot", + "subcategory": "copilot-content-exclusion-management", + "parameters": [ + { + "name": "org", + "description": "

The organization name. The name is not case sensitive.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "bodyParameters": [], + "descriptionHTML": "

Note

\n

\nThis endpoint is in public preview and is subject to change.

\n
\n

Sets Copilot content exclusion path rules for an organization.\nTo configure these settings, go to the organization's settings on GitHub.\nFor more information, see \"Excluding content from GitHub Copilot.\"

\n

Organization owners can set Copilot content exclusion rules for the organization.

\n

OAuth app tokens and personal access tokens (classic) need the copilot scope to use this endpoint.

\n

Caution

\n

\n
    \n
  • At this time, the API does not support comments. When using this endpoint, any existing comments in your rules will be deleted.
  • \n
  • At this time, the API does not support duplicate keys. If you submit content exclusions through the API with duplicate keys, only the last occurrence will be saved. Earlier entries with the same key will be overwritten.
  • \n
\n
", + "codeExamples": [ + { + "key": "default", + "request": { + "contentType": "application/json", + "description": "Example of content exclusion paths", + "acceptHeader": "application/vnd.github.v3+json", + "bodyParameters": { + "octo-repo": [ + "/src/some-dir/kernel.rs" + ] + }, + "parameters": { + "org": "ORG" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

Success

", + "example": { + "message": "Content exclusion rules updated successfully." + }, + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "200", + "description": "

Success

" + }, + { + "httpStatusCode": "401", + "description": "

Requires authentication

" + }, + { + "httpStatusCode": "403", + "description": "

Forbidden

" + }, + { + "httpStatusCode": "404", + "description": "

Resource not found

" + }, + { + "httpStatusCode": "413", + "description": "

Payload Too Large

" + }, + { + "httpStatusCode": "422", + "description": "

Validation failed, or the endpoint has been spammed.

" + }, + { + "httpStatusCode": "500", + "description": "

Internal Error

" + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": true, + "permissions": [ + { + "\"Copilot content exclusion\" organization permissions": "write" + } + ] + } + } + ], + "copilot-custom-agents": [ + { + "serverUrl": "https://api.github.com", + "verb": "get", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents", + "title": "Get custom agents for an enterprise", + "category": "copilot", + "subcategory": "copilot-custom-agents", + "parameters": [ + { + "name": "enterprise", + "description": "

The slug version of the enterprise name.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "per_page", + "description": "

The number of results per page (max 100). For more information, see \"Using pagination in the REST API.\"

", + "in": "query", + "schema": { + "type": "integer", + "default": 30 + } + }, + { + "name": "page", + "description": "

The page number of the results to fetch. For more information, see \"Using pagination in the REST API.\"

", + "in": "query", + "schema": { + "type": "integer", + "default": 1 + } + } + ], + "bodyParameters": [], + "descriptionHTML": "

Gets the list of all custom agents defined in the /agents/*.md files in the .github-private repository for the enterprise.

\n

If no source repository has been configured, returns null for custom_agents.

\n

Enterprise owners with read access to AI Controls can use this endpoint.

\n

OAuth app tokens and personal access tokens (classic) need the admin:enterprise scope to use this endpoint.

", + "codeExamples": [ + { + "key": "with_agents", + "request": { + "description": "Example 1: Status Code 200", + "acceptHeader": "application/vnd.github.v3+json", + "parameters": { + "enterprise": "ENTERPRISE" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

Response with custom agents

", + "example": { + "custom_agents": [ + { + "name": "Security Reviewer", + "file_path": "agents/security_reviewer.md", + "url": "https://github.com/my-org/.github-private/blob/main/agents/security_reviewer.md" + }, + { + "name": "Code Documenter", + "file_path": "agents/code_documenter.md", + "url": "https://github.com/my-org/.github-private/blob/main/agents/code_documenter.md" + } + ] + }, + "schema": { + "type": "object", + "properties": { + "custom_agents": { + "type": [ + "array", + "null" + ], + "description": "List of custom agents defined in the repository. Returns `null` if no source repository is configured.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The display name of the custom agent (derived from filename)." + }, + "file_path": { + "type": "string", + "description": "The path to the agent definition file." + }, + "url": { + "type": "string", + "description": "The URL to view the agent definition file." + } + } + } + } + } + } + } + }, + { + "key": "no_source", + "request": { + "description": "Example 2: Status Code 200", + "acceptHeader": "application/vnd.github.v3+json", + "parameters": { + "enterprise": "ENTERPRISE" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

Response when no source repository configured

", + "example": { + "custom_agents": null + }, + "schema": { + "type": "object", + "properties": { + "custom_agents": { + "type": [ + "array", + "null" + ], + "description": "List of custom agents defined in the repository. Returns `null` if no source repository is configured.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The display name of the custom agent (derived from filename)." + }, + "file_path": { + "type": "string", + "description": "The path to the agent definition file." + }, + "url": { + "type": "string", + "description": "The URL to view the agent definition file." + } + } + } + } + } + } + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "200", + "description": "

Success

" + }, + { + "httpStatusCode": "401", + "description": "

Requires authentication

" + }, + { + "httpStatusCode": "403", + "description": "

Forbidden

" + }, + { + "httpStatusCode": "404", + "description": "

Not found or source repository not found

" + }, + { + "httpStatusCode": "500", + "description": "

Internal Error

" + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": false, + "permissions": [ + { + "\"Enterprise AI controls\" enterprise permissions": "read" + } + ], + "disabledForPatV2": true + } + }, + { + "serverUrl": "https://api.github.com", + "verb": "get", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents/source", + "title": "Get the source organization for custom agents in an enterprise", + "category": "copilot", + "subcategory": "copilot-custom-agents", + "parameters": [ + { + "name": "enterprise", + "description": "

The slug version of the enterprise name.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "bodyParameters": [], + "descriptionHTML": "

Gets the organization and repository configured as the source for custom agent definitions in an enterprise.

\n

Custom agents are enterprise-defined AI agents stored as markdown files in a special repository.\nAn enterprise admin configures one organization as the \"source\" and that org must have a repo named\n.github-private containing agent definitions in /agents/*.md.

\n

Enterprise owners with read access to AI Controls can use this endpoint.

\n

OAuth app tokens and personal access tokens (classic) need the admin:enterprise scope to use this endpoint.

", + "codeExamples": [ + { + "key": "default", + "request": { + "description": "Example", + "acceptHeader": "application/vnd.github.v3+json", + "parameters": { + "enterprise": "ENTERPRISE" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

Response

", + "example": { + "organization": { + "id": 1, + "login": "octocat-org" + }, + "repository": { + "id": 123, + "name": ".github-private", + "full_name": "octocat-org/.github-private" + } + }, + "schema": { + "type": "object", + "required": [ + "organization", + "repository" + ], + "properties": { + "organization": { + "type": [ + "object", + "null" + ], + "required": [ + "id", + "login" + ], + "properties": { + "id": { + "type": "integer", + "description": "Unique identifier of the organization" + }, + "login": { + "type": "string", + "description": "Login of the organization" + } + } + }, + "repository": { + "type": [ + "object", + "null" + ], + "required": [ + "id", + "name", + "full_name" + ], + "properties": { + "id": { + "type": "integer", + "description": "Unique identifier of the repository" + }, + "name": { + "type": "string", + "description": "Name of the repository" + }, + "full_name": { + "type": "string", + "description": "Full name of the repository including owner" + } + } + } + } + } + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "200", + "description": "

OK

" + }, + { + "httpStatusCode": "403", + "description": "

Forbidden

" + }, + { + "httpStatusCode": "404", + "description": "

Resource not found

" + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": false, + "permissions": [ + { + "\"Enterprise AI controls\" enterprise permissions": "read" + } + ], + "disabledForPatV2": true + } + }, + { + "serverUrl": "https://api.github.com", + "verb": "put", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents/source", + "title": "Set the source organization for custom agents in an enterprise", + "category": "copilot", + "subcategory": "copilot-custom-agents", + "parameters": [ + { + "name": "enterprise", + "description": "

The slug version of the enterprise name.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "bodyParameters": [ + { + "type": "integer", + "name": "organization_id", + "in": "body", + "description": "

The ID of the organization to use as the custom agents source.

", + "isRequired": true + }, + { + "type": "boolean", + "name": "create_ruleset", + "in": "body", + "description": "

Whether to create a ruleset to protect agent definition files. Defaults to true.

", + "default": true + } + ], + "descriptionHTML": "

Sets an organization as the source for custom agent definitions in the enterprise.\nThe organization must have a .github-private repository containing agent definitions.

\n

By default, this endpoint also creates an enterprise-level ruleset to protect\nagent definition files (agents/.md and .github/agents/.md). You can opt out\nof ruleset creation by setting create_ruleset to false.

\n

Enterprise owners with write access to AI Controls can use this endpoint.

\n

OAuth app tokens and personal access tokens (classic) need the admin:enterprise scope to use this endpoint.

", + "codeExamples": [ + { + "key": "without_ruleset", + "request": { + "contentType": "application/json", + "description": "Set source organization without creating a ruleset", + "acceptHeader": "application/vnd.github.v3+json", + "bodyParameters": { + "organization_id": 123, + "create_ruleset": false + }, + "parameters": { + "enterprise": "ENTERPRISE" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

Response without ruleset

", + "example": { + "organization": { + "id": 123, + "login": "my-org", + "avatar_url": "https://avatars.githubusercontent.com/u/123" + }, + "repository": { + "id": 456, + "name": ".github-private", + "full_name": "my-org/.github-private" + } + }, + "schema": { + "type": "object", + "properties": { + "organization": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The ID of the organization." + }, + "login": { + "type": "string", + "description": "The login name of the organization." + }, + "avatar_url": { + "type": "string", + "description": "The avatar URL of the organization." + } + } + }, + "repository": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The ID of the .github-private repository." + }, + "name": { + "type": "string", + "description": "The name of the repository." + }, + "full_name": { + "type": "string", + "description": "The full name of the repository (owner/name)." + } + } + }, + "ruleset": { + "type": "object", + "description": "The created or existing ruleset (if create_ruleset was true).", + "properties": { + "id": { + "type": "integer", + "description": "The ID of the ruleset." + }, + "name": { + "type": "string", + "description": "The name of the ruleset." + }, + "enforcement": { + "type": "string", + "description": "The enforcement level of the ruleset." + } + } + } + } + } + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "200", + "description": "

Success

" + }, + { + "httpStatusCode": "401", + "description": "

Requires authentication

" + }, + { + "httpStatusCode": "403", + "description": "

Forbidden

" + }, + { + "httpStatusCode": "404", + "description": "

Resource not found

" + }, + { + "httpStatusCode": "422", + "description": "

Validation failed

" + }, + { + "httpStatusCode": "500", + "description": "

Internal Error

" + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": false, + "permissions": [ + { + "\"Enterprise AI controls\" enterprise permissions": "write" + } + ], + "disabledForPatV2": true + } + }, + { + "serverUrl": "https://api.github.com", + "verb": "delete", + "requestPath": "/enterprises/{enterprise}/copilot/custom-agents/source", + "title": "Delete the custom agents source for an enterprise", + "category": "copilot", + "subcategory": "copilot-custom-agents", + "parameters": [ + { + "name": "enterprise", + "description": "

The slug version of the enterprise name.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "bodyParameters": [], + "descriptionHTML": "

Removes the custom agents source configuration for the enterprise.\nThis effectively disables custom agents for the enterprise by removing\nthe reference to the source organization's .github-private repository.

\n

Note: This does not delete the .github-private repository or any agent\ndefinition files. It only removes the association between the enterprise\nand the source repository.

\n

Enterprise owners with write access to AI Controls can use this endpoint.

\n

OAuth app tokens and personal access tokens (classic) need the admin:enterprise scope to use this endpoint.

", + "codeExamples": [ + { + "key": "default", + "request": { + "description": "Example", + "acceptHeader": "application/vnd.github.v3+json", + "parameters": { + "enterprise": "ENTERPRISE" + } + }, + "response": { + "statusCode": "204", + "description": "

No Content - The custom agents source was successfully removed.

" + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "204", + "description": "

No Content - The custom agents source was successfully removed.

" + }, + { + "httpStatusCode": "403", + "description": "

Forbidden - The user does not have enterprise admin access.

" + }, + { + "httpStatusCode": "404", + "description": "

Not found - Enterprise not found, feature not enabled, or no custom agents source is configured.

" + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": false, + "permissions": [ + { + "\"Enterprise AI controls\" enterprise permissions": "write" + } + ], + "disabledForPatV2": true + } + } + ], "copilot-metrics": [ { "serverUrl": "https://api.github.com", @@ -362203,6 +363283,14 @@ ] } }, + { + "name": "assignee", + "in": "query", + "description": "

Filter alerts by assignees.\nProvide a comma-separated list of user handles (e.g., octocat or octocat,hubot) to return alerts assigned to any of the specified users.\nUse * to list alerts with at least one assignee or none to list alerts with no assignees.

", + "schema": { + "type": "string" + } + }, { "name": "scope", "in": "query", @@ -362425,6 +363513,28 @@ "dismissed_reason": "tolerable_risk", "dismissed_comment": "This alert is accurate but we use a sanitizer.", "fixed_at": null, + "assignees": [ + { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + } + ], "repository": { "id": 217723378, "node_id": "MDEwOlJlcG9zaXRvcnkyMTc3MjMzNzg=", @@ -362610,6 +363720,7 @@ "dismissed_reason": null, "dismissed_comment": null, "fixed_at": null, + "assignees": [], "repository": { "id": 664700648, "node_id": "MDEwOlJlcG9zaXRvcnk2NjQ3MDA2NDg=", @@ -364351,6 +365462,14 @@ ] } }, + { + "name": "assignee", + "in": "query", + "description": "

Filter alerts by assignees.\nProvide a comma-separated list of user handles (e.g., octocat or octocat,hubot) to return alerts assigned to any of the specified users.\nUse * to list alerts with at least one assignee or none to list alerts with no assignees.

", + "schema": { + "type": "string" + } + }, { "name": "runtime_risk", "in": "query", @@ -364581,6 +365700,28 @@ "dismissed_reason": "tolerable_risk", "dismissed_comment": "This alert is accurate but we use a sanitizer.", "fixed_at": null, + "assignees": [ + { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + } + ], "repository": { "id": 217723378, "node_id": "MDEwOlJlcG9zaXRvcnkyMTc3MjMzNzg=", @@ -364766,6 +365907,7 @@ "dismissed_reason": null, "dismissed_comment": null, "fixed_at": null, + "assignees": [], "repository": { "id": 664700648, "node_id": "MDEwOlJlcG9zaXRvcnk2NjQ3MDA2NDg=", @@ -366515,6 +367657,14 @@ ] } }, + { + "name": "assignee", + "in": "query", + "description": "

Filter alerts by assignees.\nProvide a comma-separated list of user handles (e.g., octocat or octocat,hubot) to return alerts assigned to any of the specified users.\nUse * to list alerts with at least one assignee or none to list alerts with no assignees.

", + "schema": { + "type": "string" + } + }, { "name": "scope", "in": "query", @@ -366737,7 +367887,29 @@ }, "dismissed_reason": "tolerable_risk", "dismissed_comment": "This alert is accurate but we use a sanitizer.", - "fixed_at": null + "fixed_at": null, + "assignees": [ + { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + } + ] }, { "number": 1, @@ -366855,7 +368027,8 @@ "dismissed_by": null, "dismissed_reason": null, "dismissed_comment": null, - "fixed_at": null + "fixed_at": null, + "assignees": [] } ], "schema": { @@ -368069,7 +369242,29 @@ "dismissed_by": null, "dismissed_reason": null, "dismissed_comment": null, - "fixed_at": null + "fixed_at": null, + "assignees": [ + { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + } + ] }, "schema": { "type": "object", @@ -369321,7 +370516,8 @@ }, "dismissed_reason": "tolerable_risk", "dismissed_comment": "This alert is accurate but we use a sanitizer.", - "fixed_at": null + "fixed_at": null, + "assignees": [] }, "schema": { "type": "object", @@ -371669,7 +372865,7 @@ "type": "array", "name": "selected_repository_ids", "in": "body", - "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints. Use integers when possible, as strings are supported only to maintain backwards compatibility and may be removed in the future.

" + "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints.

" } ], "descriptionHTML": "

Creates or updates an organization secret with an encrypted value. Encrypt your secret using\nLibSodium. For more information, see \"Encrypting secrets for the REST API.\"

\n

OAuth app tokens and personal access tokens (classic) need the admin:org scope to use this endpoint.

", @@ -590093,7 +591289,7 @@ "schema": { "type": "string", "minLength": 1, - "maxLength": 64, + "maxLength": 128, "pattern": "^[a-zA-Z0-9._-]+$" } } @@ -615338,6 +616534,10 @@ "httpStatusCode": "404", "description": "

Resource not found

" }, + { + "httpStatusCode": "422", + "description": "

Validation failed, or the endpoint has been spammed.

" + }, { "httpStatusCode": "500", "description": "

Internal Error

" @@ -619358,6 +620558,10 @@ "httpStatusCode": "404", "description": "

Resource not found

" }, + { + "httpStatusCode": "422", + "description": "

Validation failed, or the endpoint has been spammed.

" + }, { "httpStatusCode": "500", "description": "

Internal Error

" @@ -842100,6 +843304,10 @@ "httpStatusCode": "404", "description": "

Resource not found

" }, + { + "httpStatusCode": "422", + "description": "

Validation failed, or the endpoint has been spammed.

" + }, { "httpStatusCode": "500", "description": "

Internal Error

" @@ -846038,6 +847246,10 @@ "httpStatusCode": "404", "description": "

Resource not found

" }, + { + "httpStatusCode": "422", + "description": "

Validation failed, or the endpoint has been spammed.

" + }, { "httpStatusCode": "500", "description": "

Internal Error

" @@ -901185,6 +902397,10 @@ { "httpStatusCode": "204", "description": "

No Content

" + }, + { + "httpStatusCode": "422", + "description": "

Unprocessable entity if you attempt to modify an enterprise team at the organization level.

" } ], "previews": [], @@ -910340,521 +911556,529 @@ { "httpStatusCode": "200", "description": "

OK

" - } - ], - "previews": [], - "progAccess": { - "userToServerRest": true, - "serverToServer": true, - "fineGrainedPat": true, - "permissions": [ - { - "\"Members\" organization permissions": "write" - } - ] - } - }, - { - "serverUrl": "https://api.github.com", - "verb": "get", - "requestPath": "/orgs/{org}/external-groups", - "title": "List external groups available to an organization", - "category": "teams", - "subcategory": "external-groups", - "parameters": [ - { - "name": "org", - "description": "

The organization name. The name is not case sensitive.

", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "per_page", - "description": "

The number of results per page (max 100). For more information, see \"Using pagination in the REST API.\"

", - "in": "query", - "schema": { - "type": "integer", - "default": 30 - } - }, - { - "name": "page", - "description": "

Page token

", - "in": "query", - "schema": { - "type": "integer" - } - }, - { - "name": "display_name", - "description": "

Limits the list to groups containing the text in the group name

", - "in": "query", - "schema": { - "type": "string" - } - } - ], - "bodyParameters": [], - "descriptionHTML": "

Lists external groups provisioned on the enterprise that are available to an organization. You can query the groups using the display_name parameter, only groups with a group_name containing the text provided in the display_name parameter will be returned. You can also limit your page results using the per_page parameter. GitHub Enterprise Cloud generates a url-encoded page token using a cursor value for where the next page begins. For more information on cursor pagination, see \"Offset and Cursor Pagination explained.\"

\n

You can manage team membership with your identity provider using Enterprise Managed Users for GitHub Enterprise Cloud. For more information, see \"GitHub's products\" in the GitHub Help documentation.

", - "codeExamples": [ - { - "key": "default", - "request": { - "description": "Example", - "acceptHeader": "application/vnd.github.v3+json", - "parameters": { - "org": "ORG" - } - }, - "response": { - "statusCode": "200", - "contentType": "application/json", - "description": "

Response

", - "example": { - "groups": [ - { - "group_id": "123", - "group_name": "Octocat admins", - "updated_at": "2021-01-24T11:31:04-06:00" - }, - { - "group_id": "456", - "group_name": "Octocat docs members", - "updated_at": "2021-03-24T11:31:04-06:00" - } - ] - }, - "schema": { - "title": "ExternalGroups", - "description": "A list of external groups available to be connected to a team", - "type": "object", - "properties": { - "groups": { - "description": "An array of external groups available to be mapped to a team", - "type": "array", - "items": { - "type": "object", - "required": [ - "group_id", - "group_name", - "updated_at" - ], - "properties": { - "group_id": { - "description": "The internal ID of the group", - "type": "integer", - "examples": [ - 1 - ] - }, - "group_name": { - "description": "The display name of the group", - "type": "string", - "examples": [ - "group-azuread-test" - ] - }, - "updated_at": { - "description": "The time of the last update for this group", - "type": "string", - "examples": [ - "2019-06-03 22:27:15:000 -700" - ] - } - } - }, - "examples": [ - { - "group_id": 1, - "group_name": "group-azuread-test", - "updated_at": "2021-01-03 22:27:15:000 -700" - }, - { - "group_id": 2, - "group_name": "group-azuread-test2", - "updated_at": "2021-06-03 22:27:15:000 -700" - } - ] - } - } - } - } - } - ], - "statusCodes": [ - { - "httpStatusCode": "200", - "description": "

OK

" - } - ], - "previews": [], - "progAccess": { - "userToServerRest": true, - "serverToServer": true, - "fineGrainedPat": true, - "permissions": [ - { - "\"Members\" organization permissions": "write" - } - ] - } - }, - { - "serverUrl": "https://api.github.com", - "verb": "get", - "requestPath": "/orgs/{org}/teams/{team_slug}/external-groups", - "title": "List a connection between an external group and a team", - "category": "teams", - "subcategory": "external-groups", - "parameters": [ - { - "name": "org", - "description": "

The organization name. The name is not case sensitive.

", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "team_slug", - "description": "

The slug of the team name.

", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "bodyParameters": [], - "descriptionHTML": "

Lists a connection between a team and an external group.

\n

You can manage team membership with your identity provider using Enterprise Managed Users for GitHub Enterprise Cloud. For more information, see \"GitHub's products\" in the GitHub Help documentation.

", - "codeExamples": [ - { - "key": "default", - "request": { - "description": "Example", - "acceptHeader": "application/vnd.github.v3+json", - "parameters": { - "org": "ORG", - "team_slug": "TEAM_SLUG" - } - }, - "response": { - "statusCode": "200", - "contentType": "application/json", - "description": "

Response

", - "example": { - "groups": [ - { - "group_id": "123", - "group_name": "Octocat admins", - "updated_at": "2021-01-24T11:31:04-06:00" - }, - { - "group_id": "456", - "group_name": "Octocat docs members", - "updated_at": "2021-03-24T11:31:04-06:00" - } - ] - }, - "schema": { - "title": "ExternalGroups", - "description": "A list of external groups available to be connected to a team", - "type": "object", - "properties": { - "groups": { - "description": "An array of external groups available to be mapped to a team", - "type": "array", - "items": { - "type": "object", - "required": [ - "group_id", - "group_name", - "updated_at" - ], - "properties": { - "group_id": { - "description": "The internal ID of the group", - "type": "integer", - "examples": [ - 1 - ] - }, - "group_name": { - "description": "The display name of the group", - "type": "string", - "examples": [ - "group-azuread-test" - ] - }, - "updated_at": { - "description": "The time of the last update for this group", - "type": "string", - "examples": [ - "2019-06-03 22:27:15:000 -700" - ] - } - } - }, - "examples": [ - { - "group_id": 1, - "group_name": "group-azuread-test", - "updated_at": "2021-01-03 22:27:15:000 -700" - }, - { - "group_id": 2, - "group_name": "group-azuread-test2", - "updated_at": "2021-06-03 22:27:15:000 -700" - } - ] - } - } - } - } - } - ], - "statusCodes": [ - { - "httpStatusCode": "200", - "description": "

OK

" - } - ], - "previews": [], - "progAccess": { - "userToServerRest": true, - "serverToServer": true, - "fineGrainedPat": true, - "permissions": [ - { - "\"Members\" organization permissions": "write" - } - ] - } - }, - { - "serverUrl": "https://api.github.com", - "verb": "patch", - "requestPath": "/orgs/{org}/teams/{team_slug}/external-groups", - "title": "Update the connection between an external group and a team", - "category": "teams", - "subcategory": "external-groups", - "parameters": [ - { - "name": "org", - "description": "

The organization name. The name is not case sensitive.

", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": true, + "permissions": [ + { + "\"Members\" organization permissions": "write" + } + ] + } + }, + { + "serverUrl": "https://api.github.com", + "verb": "get", + "requestPath": "/orgs/{org}/external-groups", + "title": "List external groups available to an organization", + "category": "teams", + "subcategory": "external-groups", + "parameters": [ + { + "name": "org", + "description": "

The organization name. The name is not case sensitive.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "per_page", + "description": "

The number of results per page (max 100). For more information, see \"Using pagination in the REST API.\"

", + "in": "query", + "schema": { + "type": "integer", + "default": 30 + } + }, + { + "name": "page", + "description": "

Page token

", + "in": "query", + "schema": { + "type": "integer" + } + }, + { + "name": "display_name", + "description": "

Limits the list to groups containing the text in the group name

", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "bodyParameters": [], + "descriptionHTML": "

Lists external groups provisioned on the enterprise that are available to an organization. You can query the groups using the display_name parameter, only groups with a group_name containing the text provided in the display_name parameter will be returned. You can also limit your page results using the per_page parameter. GitHub Enterprise Cloud generates a url-encoded page token using a cursor value for where the next page begins. For more information on cursor pagination, see \"Offset and Cursor Pagination explained.\"

\n

You can manage team membership with your identity provider using Enterprise Managed Users for GitHub Enterprise Cloud. For more information, see \"GitHub's products\" in the GitHub Help documentation.

", + "codeExamples": [ + { + "key": "default", + "request": { + "description": "Example", + "acceptHeader": "application/vnd.github.v3+json", + "parameters": { + "org": "ORG" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

Response

", + "example": { + "groups": [ + { + "group_id": "123", + "group_name": "Octocat admins", + "updated_at": "2021-01-24T11:31:04-06:00" + }, + { + "group_id": "456", + "group_name": "Octocat docs members", + "updated_at": "2021-03-24T11:31:04-06:00" + } + ] + }, + "schema": { + "title": "ExternalGroups", + "description": "A list of external groups available to be connected to a team", + "type": "object", + "properties": { + "groups": { + "description": "An array of external groups available to be mapped to a team", + "type": "array", + "items": { + "type": "object", + "required": [ + "group_id", + "group_name", + "updated_at" + ], + "properties": { + "group_id": { + "description": "The internal ID of the group", + "type": "integer", + "examples": [ + 1 + ] + }, + "group_name": { + "description": "The display name of the group", + "type": "string", + "examples": [ + "group-azuread-test" + ] + }, + "updated_at": { + "description": "The time of the last update for this group", + "type": "string", + "examples": [ + "2019-06-03 22:27:15:000 -700" + ] + } + } + }, + "examples": [ + { + "group_id": 1, + "group_name": "group-azuread-test", + "updated_at": "2021-01-03 22:27:15:000 -700" + }, + { + "group_id": 2, + "group_name": "group-azuread-test2", + "updated_at": "2021-06-03 22:27:15:000 -700" + } + ] + } + } + } + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "200", + "description": "

OK

" + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": true, + "permissions": [ + { + "\"Members\" organization permissions": "write" + } + ] + } + }, + { + "serverUrl": "https://api.github.com", + "verb": "get", + "requestPath": "/orgs/{org}/teams/{team_slug}/external-groups", + "title": "List a connection between an external group and a team", + "category": "teams", + "subcategory": "external-groups", + "parameters": [ + { + "name": "org", + "description": "

The organization name. The name is not case sensitive.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "team_slug", + "description": "

The slug of the team name.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "bodyParameters": [], + "descriptionHTML": "

Lists a connection between a team and an external group.

\n

You can manage team membership with your identity provider using Enterprise Managed Users for GitHub Enterprise Cloud. For more information, see \"GitHub's products\" in the GitHub Help documentation.

", + "codeExamples": [ + { + "key": "default", + "request": { + "description": "Example", + "acceptHeader": "application/vnd.github.v3+json", + "parameters": { + "org": "ORG", + "team_slug": "TEAM_SLUG" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

Response

", + "example": { + "groups": [ + { + "group_id": "123", + "group_name": "Octocat admins", + "updated_at": "2021-01-24T11:31:04-06:00" + }, + { + "group_id": "456", + "group_name": "Octocat docs members", + "updated_at": "2021-03-24T11:31:04-06:00" + } + ] + }, + "schema": { + "title": "ExternalGroups", + "description": "A list of external groups available to be connected to a team", + "type": "object", + "properties": { + "groups": { + "description": "An array of external groups available to be mapped to a team", + "type": "array", + "items": { + "type": "object", + "required": [ + "group_id", + "group_name", + "updated_at" + ], + "properties": { + "group_id": { + "description": "The internal ID of the group", + "type": "integer", + "examples": [ + 1 + ] + }, + "group_name": { + "description": "The display name of the group", + "type": "string", + "examples": [ + "group-azuread-test" + ] + }, + "updated_at": { + "description": "The time of the last update for this group", + "type": "string", + "examples": [ + "2019-06-03 22:27:15:000 -700" + ] + } + } + }, + "examples": [ + { + "group_id": 1, + "group_name": "group-azuread-test", + "updated_at": "2021-01-03 22:27:15:000 -700" + }, + { + "group_id": 2, + "group_name": "group-azuread-test2", + "updated_at": "2021-06-03 22:27:15:000 -700" + } + ] + } + } + } + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "200", + "description": "

OK

" + }, + { + "httpStatusCode": "422", + "description": "

Unprocessable entity if you attempt to modify an enterprise team at the organization level.

" + } + ], + "previews": [], + "progAccess": { + "userToServerRest": true, + "serverToServer": true, + "fineGrainedPat": true, + "permissions": [ + { + "\"Members\" organization permissions": "write" + } + ] + } + }, + { + "serverUrl": "https://api.github.com", + "verb": "patch", + "requestPath": "/orgs/{org}/teams/{team_slug}/external-groups", + "title": "Update the connection between an external group and a team", + "category": "teams", + "subcategory": "external-groups", + "parameters": [ + { + "name": "org", + "description": "

The organization name. The name is not case sensitive.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "team_slug", + "description": "

The slug of the team name.

", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "bodyParameters": [ + { + "type": "integer", + "name": "group_id", + "in": "body", + "description": "

External Group Id

", + "isRequired": true + } + ], + "descriptionHTML": "

Creates a connection between a team and an external group. Only one external group can be linked to a team.

\n

You can manage team membership with your identity provider using Enterprise Managed Users for GitHub Enterprise Cloud. For more information, see \"GitHub's products\" in the GitHub Help documentation.

", + "codeExamples": [ + { + "key": "default", + "request": { + "contentType": "application/json", + "description": "Example", + "acceptHeader": "application/vnd.github.v3+json", + "bodyParameters": { + "group_id": 123 + }, + "parameters": { + "org": "ORG", + "team_slug": "TEAM_SLUG" + } + }, + "response": { + "statusCode": "200", + "contentType": "application/json", + "description": "

Response

", + "example": { + "group_id": "123", + "group_name": "Octocat admins", + "updated_at": "2021-01-24T11:31:04-06:00", + "teams": [ + { + "team_id": 1, + "team_name": "team-test" + }, + { + "team_id": 2, + "team_name": "team-test2" + } + ], + "members": [ + { + "member_id": 1, + "member_login": "mona-lisa_eocsaxrs", + "member_name": "Mona Lisa", + "member_email": "mona_lisa@github.com" + }, + { + "member_id": 2, + "member_login": "octo-lisa_eocsaxrs", + "member_name": "Octo Lisa", + "member_email": "octo_lisa@github.com" + } + ] + }, + "schema": { + "title": "ExternalGroup", + "description": "Information about an external group's usage and its members", + "type": "object", + "required": [ + "group_id", + "group_name", + "teams", + "members" + ], + "properties": { + "group_id": { + "description": "The internal ID of the group", + "type": "integer", + "examples": [ + 1 + ] + }, + "group_name": { + "description": "The display name for the group", + "type": "string", + "examples": [ + "group-azuread-test" + ] + }, + "updated_at": { + "description": "The date when the group was last updated_at", + "type": "string", + "examples": [ + "2021-01-03 22:27:15:000 -700" + ] + }, + "teams": { + "description": "An array of teams linked to this group", + "type": "array", + "items": { + "type": "object", + "required": [ + "team_id", + "team_name" + ], + "properties": { + "team_id": { + "description": "The id for a team", + "type": "integer", + "examples": [ + 1 + ] + }, + "team_name": { + "description": "The name of the team", + "type": "string", + "examples": [ + "team-test" + ] + } + } + }, + "examples": [ + { + "team_id": 1, + "team_name": "team-test" + }, + { + "team_id": 2, + "team_name": "team-test2" + } + ] + }, + "members": { + "description": "An array of external members linked to this group", + "type": "array", + "items": { + "type": "object", + "required": [ + "member_id", + "member_login", + "member_name", + "member_email" + ], + "properties": { + "member_id": { + "description": "The internal user ID of the identity", + "type": "integer", + "examples": [ + 1 + ] + }, + "member_login": { + "description": "The handle/login for the user", + "type": "string", + "examples": [ + "mona-lisa_eocsaxrs" + ] + }, + "member_name": { + "description": "The user display name/profile name", + "type": "string", + "examples": [ + "Mona Lisa" + ] + }, + "member_email": { + "description": "An email attached to a user", + "type": "string", + "examples": [ + "mona_lisa@github.com" + ] + } + } + }, + "examples": [ + { + "member_id": 1, + "member_login": "mona-lisa_eocsaxrs", + "member_name": "Mona Lisa", + "member_email": "mona_lisa@github.com" + }, + { + "member_id": 2, + "member_login": "octo-lisa_eocsaxrs", + "member_name": "Octo Lisa", + "member_email": "octo_lisa@github.com" + } + ] + } + } + } + } + } + ], + "statusCodes": [ + { + "httpStatusCode": "200", + "description": "

OK

" }, { - "name": "team_slug", - "description": "

The slug of the team name.

", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "bodyParameters": [ - { - "type": "integer", - "name": "group_id", - "in": "body", - "description": "

External Group Id

", - "isRequired": true - } - ], - "descriptionHTML": "

Creates a connection between a team and an external group. Only one external group can be linked to a team.

\n

You can manage team membership with your identity provider using Enterprise Managed Users for GitHub Enterprise Cloud. For more information, see \"GitHub's products\" in the GitHub Help documentation.

", - "codeExamples": [ - { - "key": "default", - "request": { - "contentType": "application/json", - "description": "Example", - "acceptHeader": "application/vnd.github.v3+json", - "bodyParameters": { - "group_id": 123 - }, - "parameters": { - "org": "ORG", - "team_slug": "TEAM_SLUG" - } - }, - "response": { - "statusCode": "200", - "contentType": "application/json", - "description": "

Response

", - "example": { - "group_id": "123", - "group_name": "Octocat admins", - "updated_at": "2021-01-24T11:31:04-06:00", - "teams": [ - { - "team_id": 1, - "team_name": "team-test" - }, - { - "team_id": 2, - "team_name": "team-test2" - } - ], - "members": [ - { - "member_id": 1, - "member_login": "mona-lisa_eocsaxrs", - "member_name": "Mona Lisa", - "member_email": "mona_lisa@github.com" - }, - { - "member_id": 2, - "member_login": "octo-lisa_eocsaxrs", - "member_name": "Octo Lisa", - "member_email": "octo_lisa@github.com" - } - ] - }, - "schema": { - "title": "ExternalGroup", - "description": "Information about an external group's usage and its members", - "type": "object", - "required": [ - "group_id", - "group_name", - "teams", - "members" - ], - "properties": { - "group_id": { - "description": "The internal ID of the group", - "type": "integer", - "examples": [ - 1 - ] - }, - "group_name": { - "description": "The display name for the group", - "type": "string", - "examples": [ - "group-azuread-test" - ] - }, - "updated_at": { - "description": "The date when the group was last updated_at", - "type": "string", - "examples": [ - "2021-01-03 22:27:15:000 -700" - ] - }, - "teams": { - "description": "An array of teams linked to this group", - "type": "array", - "items": { - "type": "object", - "required": [ - "team_id", - "team_name" - ], - "properties": { - "team_id": { - "description": "The id for a team", - "type": "integer", - "examples": [ - 1 - ] - }, - "team_name": { - "description": "The name of the team", - "type": "string", - "examples": [ - "team-test" - ] - } - } - }, - "examples": [ - { - "team_id": 1, - "team_name": "team-test" - }, - { - "team_id": 2, - "team_name": "team-test2" - } - ] - }, - "members": { - "description": "An array of external members linked to this group", - "type": "array", - "items": { - "type": "object", - "required": [ - "member_id", - "member_login", - "member_name", - "member_email" - ], - "properties": { - "member_id": { - "description": "The internal user ID of the identity", - "type": "integer", - "examples": [ - 1 - ] - }, - "member_login": { - "description": "The handle/login for the user", - "type": "string", - "examples": [ - "mona-lisa_eocsaxrs" - ] - }, - "member_name": { - "description": "The user display name/profile name", - "type": "string", - "examples": [ - "Mona Lisa" - ] - }, - "member_email": { - "description": "An email attached to a user", - "type": "string", - "examples": [ - "mona_lisa@github.com" - ] - } - } - }, - "examples": [ - { - "member_id": 1, - "member_login": "mona-lisa_eocsaxrs", - "member_name": "Mona Lisa", - "member_email": "mona_lisa@github.com" - }, - { - "member_id": 2, - "member_login": "octo-lisa_eocsaxrs", - "member_name": "Octo Lisa", - "member_email": "octo_lisa@github.com" - } - ] - } - } - } - } - } - ], - "statusCodes": [ - { - "httpStatusCode": "200", - "description": "

OK

" + "httpStatusCode": "422", + "description": "

Unprocessable entity if you attempt to modify an enterprise team at the organization level.

" } ], "previews": [], @@ -910919,6 +912143,10 @@ { "httpStatusCode": "204", "description": "

No Content

" + }, + { + "httpStatusCode": "422", + "description": "

Unprocessable entity if you attempt to modify an enterprise team at the organization level.

" } ], "previews": [], @@ -911283,6 +912511,10 @@ { "httpStatusCode": "200", "description": "

OK

" + }, + { + "httpStatusCode": "422", + "description": "

Unprocessable entity if you attempt to modify an enterprise team at the organization level.

" } ], "previews": [], @@ -913390,6 +914622,10 @@ { "httpStatusCode": "200", "description": "

OK

" + }, + { + "httpStatusCode": "422", + "description": "

Unprocessable entity if you attempt to modify an enterprise team at the organization level.

" } ], "previews": [], @@ -913577,6 +914813,10 @@ { "httpStatusCode": "200", "description": "

OK

" + }, + { + "httpStatusCode": "422", + "description": "

Unprocessable entity if you attempt to modify an enterprise team at the organization level.

" } ], "previews": [], diff --git a/src/rest/data/ghes-3.14-2022-11-28/schema.json b/src/rest/data/ghes-3.14-2022-11-28/schema.json index 8ee24cd692dc..0fb2b87d12dc 100644 --- a/src/rest/data/ghes-3.14-2022-11-28/schema.json +++ b/src/rest/data/ghes-3.14-2022-11-28/schema.json @@ -382167,8 +382167,7 @@ "site_admin": false }, "dismissed_reason": "tolerable_risk", - "dismissed_comment": "This alert is accurate but we use a sanitizer.", - "fixed_at": null + "dismissed_comment": "This alert is accurate but we use a sanitizer." }, "schema": { "type": "object", @@ -383407,7 +383406,7 @@ "type": "array", "name": "selected_repository_ids", "in": "body", - "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints. Use integers when possible, as strings are supported only to maintain backwards compatibility and may be removed in the future.

" + "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints.

" } ], "descriptionHTML": "

Creates or updates an organization secret with an encrypted value. Encrypt your secret using\nLibSodium. For more information, see \"Encrypting secrets for the REST API.\"

\n

OAuth app tokens and personal access tokens (classic) need the admin:org scope to use this endpoint.

", diff --git a/src/rest/data/ghes-3.15-2022-11-28/schema.json b/src/rest/data/ghes-3.15-2022-11-28/schema.json index 249ac3ca4b5d..7d8f91826d7d 100644 --- a/src/rest/data/ghes-3.15-2022-11-28/schema.json +++ b/src/rest/data/ghes-3.15-2022-11-28/schema.json @@ -386526,8 +386526,7 @@ "site_admin": false }, "dismissed_reason": "tolerable_risk", - "dismissed_comment": "This alert is accurate but we use a sanitizer.", - "fixed_at": null + "dismissed_comment": "This alert is accurate but we use a sanitizer." }, "schema": { "type": "object", @@ -387766,7 +387765,7 @@ "type": "array", "name": "selected_repository_ids", "in": "body", - "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints. Use integers when possible, as strings are supported only to maintain backwards compatibility and may be removed in the future.

" + "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints.

" } ], "descriptionHTML": "

Creates or updates an organization secret with an encrypted value. Encrypt your secret using\nLibSodium. For more information, see \"Encrypting secrets for the REST API.\"

\n

OAuth app tokens and personal access tokens (classic) need the admin:org scope to use this endpoint.

", diff --git a/src/rest/data/ghes-3.16-2022-11-28/schema.json b/src/rest/data/ghes-3.16-2022-11-28/schema.json index ddf977594d54..bcb9375c5e97 100644 --- a/src/rest/data/ghes-3.16-2022-11-28/schema.json +++ b/src/rest/data/ghes-3.16-2022-11-28/schema.json @@ -389249,12 +389249,7 @@ "url": "https://api.github.com/repos/octocat/hello-world/dependabot/alerts/1", "html_url": "https://github.com/octocat/hello-world/security/dependabot/1", "created_at": "2022-06-14T15:21:52Z", - "updated_at": "2022-06-14T15:21:52Z", - "dismissed_at": null, - "dismissed_by": null, - "dismissed_reason": null, - "dismissed_comment": null, - "fixed_at": null + "updated_at": "2022-06-14T15:21:52Z" }, "schema": { "type": "object", @@ -390318,8 +390313,7 @@ "site_admin": false }, "dismissed_reason": "tolerable_risk", - "dismissed_comment": "This alert is accurate but we use a sanitizer.", - "fixed_at": null + "dismissed_comment": "This alert is accurate but we use a sanitizer." }, "schema": { "type": "object", @@ -391578,7 +391572,7 @@ "type": "array", "name": "selected_repository_ids", "in": "body", - "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints. Use integers when possible, as strings are supported only to maintain backwards compatibility and may be removed in the future.

" + "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints.

" } ], "descriptionHTML": "

Creates or updates an organization secret with an encrypted value. Encrypt your secret using\nLibSodium. For more information, see \"Encrypting secrets for the REST API.\"

\n

OAuth app tokens and personal access tokens (classic) need the admin:org scope to use this endpoint.

", diff --git a/src/rest/data/ghes-3.17-2022-11-28/schema.json b/src/rest/data/ghes-3.17-2022-11-28/schema.json index f1539829a2a8..899cebb6ec75 100644 --- a/src/rest/data/ghes-3.17-2022-11-28/schema.json +++ b/src/rest/data/ghes-3.17-2022-11-28/schema.json @@ -390202,12 +390202,7 @@ "url": "https://api.github.com/repos/octocat/hello-world/dependabot/alerts/1", "html_url": "https://github.com/octocat/hello-world/security/dependabot/1", "created_at": "2022-06-14T15:21:52Z", - "updated_at": "2022-06-14T15:21:52Z", - "dismissed_at": null, - "dismissed_by": null, - "dismissed_reason": null, - "dismissed_comment": null, - "fixed_at": null + "updated_at": "2022-06-14T15:21:52Z" }, "schema": { "type": "object", @@ -391271,8 +391266,7 @@ "site_admin": false }, "dismissed_reason": "tolerable_risk", - "dismissed_comment": "This alert is accurate but we use a sanitizer.", - "fixed_at": null + "dismissed_comment": "This alert is accurate but we use a sanitizer." }, "schema": { "type": "object", @@ -392531,7 +392525,7 @@ "type": "array", "name": "selected_repository_ids", "in": "body", - "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints. Use integers when possible, as strings are supported only to maintain backwards compatibility and may be removed in the future.

" + "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints.

" } ], "descriptionHTML": "

Creates or updates an organization secret with an encrypted value. Encrypt your secret using\nLibSodium. For more information, see \"Encrypting secrets for the REST API.\"

\n

OAuth app tokens and personal access tokens (classic) need the admin:org scope to use this endpoint.

", diff --git a/src/rest/data/ghes-3.18-2022-11-28/schema.json b/src/rest/data/ghes-3.18-2022-11-28/schema.json index 02a771c3c665..b0545e64cd56 100644 --- a/src/rest/data/ghes-3.18-2022-11-28/schema.json +++ b/src/rest/data/ghes-3.18-2022-11-28/schema.json @@ -390421,12 +390421,7 @@ "url": "https://api.github.com/repos/octocat/hello-world/dependabot/alerts/1", "html_url": "https://github.com/octocat/hello-world/security/dependabot/1", "created_at": "2022-06-14T15:21:52Z", - "updated_at": "2022-06-14T15:21:52Z", - "dismissed_at": null, - "dismissed_by": null, - "dismissed_reason": null, - "dismissed_comment": null, - "fixed_at": null + "updated_at": "2022-06-14T15:21:52Z" }, "schema": { "type": "object", @@ -391490,8 +391485,7 @@ "site_admin": false }, "dismissed_reason": "tolerable_risk", - "dismissed_comment": "This alert is accurate but we use a sanitizer.", - "fixed_at": null + "dismissed_comment": "This alert is accurate but we use a sanitizer." }, "schema": { "type": "object", @@ -393652,7 +393646,7 @@ "type": "array", "name": "selected_repository_ids", "in": "body", - "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints. Use integers when possible, as strings are supported only to maintain backwards compatibility and may be removed in the future.

" + "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints.

" } ], "descriptionHTML": "

Creates or updates an organization secret with an encrypted value. Encrypt your secret using\nLibSodium. For more information, see \"Encrypting secrets for the REST API.\"

\n

OAuth app tokens and personal access tokens (classic) need the admin:org scope to use this endpoint.

", diff --git a/src/rest/data/ghes-3.19-2022-11-28/schema.json b/src/rest/data/ghes-3.19-2022-11-28/schema.json index 37ee711842aa..9afbc0b8dc94 100644 --- a/src/rest/data/ghes-3.19-2022-11-28/schema.json +++ b/src/rest/data/ghes-3.19-2022-11-28/schema.json @@ -396937,12 +396937,7 @@ "url": "https://api.github.com/repos/octocat/hello-world/dependabot/alerts/1", "html_url": "https://github.com/octocat/hello-world/security/dependabot/1", "created_at": "2022-06-14T15:21:52Z", - "updated_at": "2022-06-14T15:21:52Z", - "dismissed_at": null, - "dismissed_by": null, - "dismissed_reason": null, - "dismissed_comment": null, - "fixed_at": null + "updated_at": "2022-06-14T15:21:52Z" }, "schema": { "type": "object", @@ -398006,8 +398001,7 @@ "site_admin": false }, "dismissed_reason": "tolerable_risk", - "dismissed_comment": "This alert is accurate but we use a sanitizer.", - "fixed_at": null + "dismissed_comment": "This alert is accurate but we use a sanitizer." }, "schema": { "type": "object", @@ -400168,7 +400162,7 @@ "type": "array", "name": "selected_repository_ids", "in": "body", - "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints. Use integers when possible, as strings are supported only to maintain backwards compatibility and may be removed in the future.

" + "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints.

" } ], "descriptionHTML": "

Creates or updates an organization secret with an encrypted value. Encrypt your secret using\nLibSodium. For more information, see \"Encrypting secrets for the REST API.\"

\n

OAuth app tokens and personal access tokens (classic) need the admin:org scope to use this endpoint.

", diff --git a/src/rest/data/ghes-3.20-2022-11-28/schema.json b/src/rest/data/ghes-3.20-2022-11-28/schema.json index be1a099ab5ad..66f356084ed7 100644 --- a/src/rest/data/ghes-3.20-2022-11-28/schema.json +++ b/src/rest/data/ghes-3.20-2022-11-28/schema.json @@ -397498,12 +397498,7 @@ "url": "https://api.github.com/repos/octocat/hello-world/dependabot/alerts/1", "html_url": "https://github.com/octocat/hello-world/security/dependabot/1", "created_at": "2022-06-14T15:21:52Z", - "updated_at": "2022-06-14T15:21:52Z", - "dismissed_at": null, - "dismissed_by": null, - "dismissed_reason": null, - "dismissed_comment": null, - "fixed_at": null + "updated_at": "2022-06-14T15:21:52Z" }, "schema": { "type": "object", @@ -398567,8 +398562,7 @@ "site_admin": false }, "dismissed_reason": "tolerable_risk", - "dismissed_comment": "This alert is accurate but we use a sanitizer.", - "fixed_at": null + "dismissed_comment": "This alert is accurate but we use a sanitizer." }, "schema": { "type": "object", @@ -400729,7 +400723,7 @@ "type": "array", "name": "selected_repository_ids", "in": "body", - "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints. Use integers when possible, as strings are supported only to maintain backwards compatibility and may be removed in the future.

" + "description": "

An array of repository ids that can access the organization secret. You can only provide a list of repository ids when the visibility is set to selected. You can manage the list of selected repositories using the List selected repositories for an organization secret, Set selected repositories for an organization secret, and Remove selected repository from an organization secret endpoints.

" } ], "descriptionHTML": "

Creates or updates an organization secret with an encrypted value. Encrypt your secret using\nLibSodium. For more information, see \"Encrypting secrets for the REST API.\"

\n

OAuth app tokens and personal access tokens (classic) need the admin:org scope to use this endpoint.

", diff --git a/src/rest/lib/config.json b/src/rest/lib/config.json index 3ed1d35571f9..d7deac68c5eb 100644 --- a/src/rest/lib/config.json +++ b/src/rest/lib/config.json @@ -53,5 +53,5 @@ ] } }, - "sha": "951b2951cf3bed81c9e881f8f470cfda20350d34" + "sha": "884c183f5163e1155700894ac68dd891e8668fd5" } \ No newline at end of file diff --git a/src/search/tests/topics.ts b/src/search/tests/topics.ts index f7a62ce02ffd..023bedff4294 100644 --- a/src/search/tests/topics.ts +++ b/src/search/tests/topics.ts @@ -21,7 +21,8 @@ const topics: string[] = walk(contentDir, { includeBasePath: true }) throw new Error(`More than 0 front-matter errors in file: ${filename}`) } - return (data as any).topics || [] + const pageTopics = (data as Record).topics + return Array.isArray(pageTopics) ? (pageTopics as string[]) : [] }) .flat() diff --git a/src/tests/helpers/e2etest.ts b/src/tests/helpers/e2etest.ts index 69224a2ecd41..85f66ad70412 100644 --- a/src/tests/helpers/e2etest.ts +++ b/src/tests/helpers/e2etest.ts @@ -1,4 +1,4 @@ -import cheerio from 'cheerio' +import { load, type CheerioAPI } from 'cheerio' import { fetchWithRetry } from '@/frame/lib/fetch-utils' import { omitBy, isUndefined } from 'lodash-es' @@ -35,7 +35,7 @@ interface ResponseWithHeaders { } // Type alias for cached DOM results to improve maintainability -type CachedDOMResult = cheerio.Root & { res: ResponseWithHeaders; $: cheerio.Root } +type CachedDOMResult = CheerioAPI & { res: ResponseWithHeaders; $: CheerioAPI } // Cache to store DOM objects const getDOMCache = new Map() @@ -174,7 +174,7 @@ export async function getDOM(route: string, options: GetDOMOptions = {}): Promis throw new Error(`Page not found on ${route} (${res.statusCode})`) } - const $ = cheerio.load(res.body || '', { xmlMode: true }) + const $ = load(res.body || '', { xmlMode: true }) const result = $ as CachedDOMResult // Attach res to the cheerio object for backward compatibility result.res = res diff --git a/src/tests/helpers/script-data.ts b/src/tests/helpers/script-data.ts index f28f6b7c7ad1..f3bef288faa3 100644 --- a/src/tests/helpers/script-data.ts +++ b/src/tests/helpers/script-data.ts @@ -1,9 +1,9 @@ -import cheerio from 'cheerio' +import type { CheerioAPI } from 'cheerio' const NEXT_DATA_QUERY = 'script#__NEXT_DATA__' const PRIMER_DATA_QUERY = 'script#__PRIMER_DATA__' -function getScriptData($: ReturnType, key: string): unknown { +function getScriptData($: CheerioAPI, key: string): unknown { const data = $(key) if (data.length !== 1) { throw new Error(`Not exactly 1 element match for '${key}'. Found ${data.length}`) @@ -18,7 +18,5 @@ function getScriptData($: ReturnType, key: string): unknown throw new Error(`Could not extract data from '${key}'`) } -export const getNextData = ($: ReturnType): unknown => - getScriptData($, NEXT_DATA_QUERY) -export const getPrimerData = ($: ReturnType): unknown => - getScriptData($, PRIMER_DATA_QUERY) +export const getNextData = ($: CheerioAPI): unknown => getScriptData($, NEXT_DATA_QUERY) +export const getPrimerData = ($: CheerioAPI): unknown => getScriptData($, PRIMER_DATA_QUERY) diff --git a/src/tools/components/Picker.tsx b/src/tools/components/Picker.tsx index e125576795f6..1d4fcf7bafdf 100644 --- a/src/tools/components/Picker.tsx +++ b/src/tools/components/Picker.tsx @@ -24,7 +24,10 @@ export interface PickerItem { text: string selected: boolean extra?: { - [key: string]: any + arrow?: boolean + info?: boolean + version?: string + currentDate?: string } divider?: boolean } diff --git a/src/webhooks/lib/config.json b/src/webhooks/lib/config.json index 2e75a5b0ae3d..a54d095efb68 100644 --- a/src/webhooks/lib/config.json +++ b/src/webhooks/lib/config.json @@ -1,3 +1,3 @@ { - "sha": "951b2951cf3bed81c9e881f8f470cfda20350d34" + "sha": "884c183f5163e1155700894ac68dd891e8668fd5" } \ No newline at end of file diff --git a/src/workflows/experimental/readability-report.ts b/src/workflows/experimental/readability-report.ts index 14f51d2b1078..26dc8155fbe6 100644 --- a/src/workflows/experimental/readability-report.ts +++ b/src/workflows/experimental/readability-report.ts @@ -38,7 +38,7 @@ import fs from 'fs' import path from 'path' -import cheerio from 'cheerio' +import { load } from 'cheerio' import { fetchWithRetry } from '@/frame/lib/fetch-utils' interface ReadabilityMetrics { @@ -219,7 +219,7 @@ async function analyzeFile(filePath: string): Promise { // Parse HTML and extract content const body = await response.text() - const $ = cheerio.load(body) + const $ = load(body) // Get page title const title = $('h1').first().text().trim() || $('title').text().trim() || 'Untitled'