diff --git a/.github/instructions/content.instructions.md b/.github/instructions/content.instructions.md index a4606d6d0487..d6a1443502d2 100644 --- a/.github/instructions/content.instructions.md +++ b/.github/instructions/content.instructions.md @@ -92,3 +92,61 @@ Examples: * ❌ Incorrect: "The cat – which sat on a branch – smiled with a broad grin." (en dash with spaces) * ❌ Incorrect: "The cat-which sat on a branch-smiled with a broad grin." (hyphen without spaces) * ❌ Incorrect: "The cat - which sat on a branch - smiled with a broad grin." (hyphen with spaces) + +## Versioning + +Avoid `{% ifversion fpt %}`, `{% ifversion ghec %}`, and `{% ifversion fpt or ghec %}` in content files whenever possible. Instead of suggesting or adding version-gating within an article: + +* Write content that applies to all versions the article is versioned for +* If content is truly version-specific, consider whether it is low-harm to show it to all readers (e.g., an enterprise-only row in a reference table) +* Only use `{% ifversion %}` as a last resort when content would be actively misleading for readers on a different version + +**FPT and GHEC content**: When dotcom content applies to both products, version the page for `fpt` and `ghec` in the frontmatter. Do NOT use in-article Liquid versioning. Do NOT suggest adding `{% ifversion fpt or ghec %}` blocks as a fix for content that mentions a dotcom-only feature. Instead, suggest rewriting the content using the alternatives to inline versioning options listed below. + +**GHES content**: If versioning is necessary for GitHub Enterprise Server content, use feature-based versioning (FBV). GHES content should rely on feature flags defined in `data/features/` rather than inline `{% ifversion ghes %}` blocks. Feature flags allow centralized control of when content appears for specific GHES releases. + +### Alternatives to inline versioning + +Before resorting to in-article versioning, first consider whether the content is actually different across versions. Often procedures can be simplified to work at both levels. + +Use these strategies instead of `{% ifversion %}`, depending on the level of content: + +**At the article level:** + +* When the feature is only available in certain products, use the "Who can use this feature?" box to convey that the content of the article applies only to specific products +* When an article only exists because the functionality is only available in older GHES releases (and not on {% data variables.product.prodname_dotcom_the_website %} or newer GHES releases), just remove that article + +**At the heading level:** + +* Use prose similar to "Who can use this feature?" to convey that the content of a section applies only to specific products + +**At the paragraph or sentence level:** + +* If you're briefly introducing a feature and then linking to an article, there's no need to specify versioning. Let readers learn availability when they follow the link, via the "Who can use this feature?" box +* When necessary, start sentences with "With {% data variables.product.prodname_ghe_cloud %}...", "On {% data variables.product.prodname_dotcom_the_website %}...", etc. +* End list items with "({% data variables.product.prodname_ghe_cloud %} only)", "({% data variables.product.prodname_dotcom_the_website %} only)", etc. +* Specify if the feature is not available for GHES with "NAME-OF-FEATURE is not available for {% data variables.product.prodname_ghe_server %}", "... (not available in {% data variables.product.prodname_ghe_server %})", etc. + +### Example + +When documenting a feature that only applies to dotcom (not GHES): + +❌ Don't wrap content in version blocks: + +```markdown +{% ifversion fpt or ghec %} + +## Immutable subject claims + +Repositories created after July 15, 2026 now use an immutable default subject format. + +{% endif %} +``` + +✅ Do use prose to indicate availability: + +```markdown +## Immutable subject claims + +Repositories created after July 15, 2026 now use an immutable default subject format. This rollout does not include {% data variables.product.prodname_ghe_server %}. +``` diff --git a/.github/workflows/changelog-agent.yml b/.github/workflows/changelog-agent.yml index b7a25cd66bb6..de8637dacef1 100644 --- a/.github/workflows/changelog-agent.yml +++ b/.github/workflows/changelog-agent.yml @@ -404,6 +404,16 @@ jobs: fs.writeFileSync('prompt.txt', userPrompt); core.setOutput('date_str', dateStr); + - name: Set up Node.js for Copilot CLI + if: steps.check_parent.outputs.has_parent == 'true' && steps.check_existing.outputs.exists == 'false' + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 + with: + node-version: 24 + + - name: Install GitHub Copilot CLI + if: steps.check_parent.outputs.has_parent == 'true' && steps.check_existing.outputs.exists == 'false' + run: npm install -g @github/copilot + - name: Generate changelog draft via Copilot if: steps.check_parent.outputs.has_parent == 'true' && steps.check_existing.outputs.exists == 'false' id: generate_draft diff --git a/.github/workflows/copy-api-issue-to-internal.yml b/.github/workflows/copy-api-issue-to-internal.yml index a76c18026330..4e1c6243e471 100644 --- a/.github/workflows/copy-api-issue-to-internal.yml +++ b/.github/workflows/copy-api-issue-to-internal.yml @@ -45,7 +45,9 @@ jobs: - name: Create an issue in the docs-content repo run: | - new_issue_url="$(gh issue create --title "$ISSUE_TITLE" --body "$ISSUE_BODY" --repo github/docs-content --label "workflow-generated")" + new_issue_url="$(gh issue create --title "$ISSUE_TITLE" --body "$ISSUE_BODY + + Copied to this repo by the [$GITHUB_WORKFLOW workflow run]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID)." --repo github/docs-content --label "workflow-generated")" echo 'NEW_ISSUE='$new_issue_url >> $GITHUB_ENV env: GITHUB_TOKEN: ${{secrets.DOCS_BOT_PAT_BASE}} diff --git a/.github/workflows/delete-orphan-translation-files.yml b/.github/workflows/delete-orphan-translation-files.yml index aa1757f1c51d..ba45c5c63897 100644 --- a/.github/workflows/delete-orphan-translation-files.yml +++ b/.github/workflows/delete-orphan-translation-files.yml @@ -127,7 +127,7 @@ jobs: echo "Creating pull request..." gh pr create \ --title "Delete orphan files ($current_daystamp)" \ - --body '👋 humans. This PR was generated from docs-internal/.github/workflows/delete-orphan-translation-files.yml. + --body '👋 humans. This PR was generated by the [Delete orphan translation files](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) workflow run. ' \ --repo "${{ matrix.language_repo }}" \ --label "workflow-generated" \ diff --git a/.github/workflows/generate-code-scanning-query-lists.yml b/.github/workflows/generate-code-scanning-query-lists.yml index da3e7f726a7b..e3fd557f6014 100644 --- a/.github/workflows/generate-code-scanning-query-lists.yml +++ b/.github/workflows/generate-code-scanning-query-lists.yml @@ -259,4 +259,6 @@ jobs: No action is required from the first responder for the Docs content team. This PR is automatically added to the Docs content review board. Any writer can review this by checking that the PR looks sensible. If CI does not pass or other problems arise, contact #docs-engineering on slack. - When the DRI for the CodeQL CLI release is ready to publish, they will ask us to merge this PR in #docs-content.' + When the DRI for the CodeQL CLI release is ready to publish, they will ask us to merge this PR in #docs-content. + + _Generated by the [Generate code scanning query lists](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) workflow run._' diff --git a/.github/workflows/moda-allowed-ips.yml b/.github/workflows/moda-allowed-ips.yml index eefba673bca2..5d134793c418 100644 --- a/.github/workflows/moda-allowed-ips.yml +++ b/.github/workflows/moda-allowed-ips.yml @@ -48,7 +48,9 @@ jobs: git push gh pr create \ --title "Update list of allowed IPs" \ - --body 'This PR updates the list of allowed IPs in Moda. It is automatically generated.' \ + --body "This PR updates the list of allowed IPs in Moda. It is automatically generated. + + _Generated by the [Update Moda allowed IPs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) workflow run._" \ --label "workflow-generated" \ --head=$branchname echo "Pull request created" diff --git a/.github/workflows/orphaned-features-check.yml b/.github/workflows/orphaned-features-check.yml index 5689a95f764a..21f39d8cf2a4 100644 --- a/.github/workflows/orphaned-features-check.yml +++ b/.github/workflows/orphaned-features-check.yml @@ -90,6 +90,8 @@ jobs: to make sure they aren't referenced anywhere and then approve and merge the pull request. For more information, see [Doc: Orphaned Features](https://github.com/github/docs-engineering/blob/main/docs/orphaned-features.md). + + Generated by the [orphaned features workflow run]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID). EOM ) diff --git a/.github/workflows/orphaned-files-check.yml b/.github/workflows/orphaned-files-check.yml index 09b604711ab6..600efa06c2ca 100644 --- a/.github/workflows/orphaned-files-check.yml +++ b/.github/workflows/orphaned-files-check.yml @@ -98,6 +98,8 @@ jobs: If you are the first responder, please spot check some of the unused assets to make sure they aren't referenced anywhere. Then, approve and merge the pull request. For more information, see [Doc: Orphaned Assets](https://github.com/github/docs-engineering/blob/main/docs/orphaned-assets.md) and [Doc: Reusables CLI](https://github.com/github/docs-internal/tree/main/src/content-render/scripts/reusables-cli). + + Generated by the [orphaned files workflow run]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID). EOM ) diff --git a/.github/workflows/purge-fastly-all.yml b/.github/workflows/purge-fastly-all.yml new file mode 100644 index 000000000000..4aa2745ce409 --- /dev/null +++ b/.github/workflows/purge-fastly-all.yml @@ -0,0 +1,44 @@ +name: Purge Fastly (all) + +# **What it does**: Purges the ENTIRE Fastly cache for docs (every URL) on demand. +# **Why we have it**: So docs engineering can clear a bad cache state without asking core engineering to run a purge-all in the Fastly UI. +# **Who does it impact**: All readers. Origin sees a traffic spike while the cache refills, so only run this when a targeted purge will not do. + +on: + workflow_dispatch: + inputs: + confirm: + description: "Type 'purge everything' to confirm a full hard purge of the Fastly cache." + required: true + +permissions: + contents: read + +# Only one purge-all may run at a time. Do not cancel an in-flight purge: +# a half-finished purge leaves the cache in an unknown state. +concurrency: + group: purge-fastly-all + cancel-in-progress: false + +env: + FASTLY_TOKEN: ${{ secrets.FASTLY_TOKEN }} + FASTLY_SERVICE_ID: ${{ secrets.FASTLY_SERVICE_ID }} + +jobs: + purge-all: + if: github.repository == 'github/docs-internal' + runs-on: ubuntu-latest + steps: + - name: Validate confirmation input + if: ${{ inputs.confirm != 'purge everything' }} + run: | + echo "::error::Confirmation text did not match. Re-run and type exactly: purge everything" + exit 1 + + - name: Check out repo + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - uses: ./.github/actions/node-npm-setup + + - name: Purge entire Fastly cache + run: npm run purge-fastly-all diff --git a/.github/workflows/sme-review-tracking-issue.yml b/.github/workflows/sme-review-tracking-issue.yml index c8725ca73d52..b8fa0a50289c 100644 --- a/.github/workflows/sme-review-tracking-issue.yml +++ b/.github/workflows/sme-review-tracking-issue.yml @@ -53,6 +53,9 @@ jobs: - Routed to another channel / team - Reviewer stating they'll need to get back to us at a later time - Review provided was unclear or missing key information, and a follow-up is necessary + + --- + _Generated by the [${context.workflow}](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) workflow run._ `, labels: ['on track','open source', 'sme-review', 'workflow-generated'], }); diff --git a/.github/workflows/sync-openapi.yml b/.github/workflows/sync-openapi.yml index 4c1221cf36df..a8e596864ee3 100644 --- a/.github/workflows/sync-openapi.yml +++ b/.github/workflows/sync-openapi.yml @@ -113,7 +113,9 @@ jobs: Docs First Responders should follow [the acting-as-the-first-responder instructions](https://github.com/github/docs-team/blob/main/contributing-to-docs/first-responder/acting-as-the-first-responder.md?plain=1#L156). - If CI does not pass or other problems arise, contact #docs-engineering on slack.' \ + If CI does not pass or other problems arise, contact #docs-engineering on slack. + + _Generated by the [Sync OpenAPI schema](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) workflow run._' \ --repo github/docs-internal \ --label github-openapi-bot,workflow-generated \ --head=$branchname \ diff --git a/.github/workflows/sync-sdk-docs.yml b/.github/workflows/sync-sdk-docs.yml index 789da315dcea..df0fb34b4537 100644 --- a/.github/workflows/sync-sdk-docs.yml +++ b/.github/workflows/sync-sdk-docs.yml @@ -216,7 +216,7 @@ jobs: > This PR is auto-generated. Do not edit it directly — make changes in the [copilot-sdk docs](https://github.com/github/copilot-sdk/tree/main/docs) instead. --- - _Generated by the [sync-sdk-docs](https://github.com/github/docs-internal/actions/workflows/sync-sdk-docs.yml) workflow._" + _Generated by the [sync-sdk-docs workflow run]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID)._" EXISTING_PR=$(gh pr list --head "$SYNC_BRANCH" --json number --jq '.[0].number' 2>/dev/null || true) diff --git a/content/copilot/concepts/billing/budgets-for-usage-based-billing.md b/content/copilot/concepts/billing/budgets-for-usage-based-billing.md index 6439cf068cf7..16e3c878e129 100644 --- a/content/copilot/concepts/billing/budgets-for-usage-based-billing.md +++ b/content/copilot/concepts/billing/budgets-for-usage-based-billing.md @@ -1,10 +1,10 @@ --- title: Budgets for usage-based billing shortTitle: Budgets -intro: 'Under usage-based billing, budget controls at the user, cost center, and enterprise levels determine how {% data variables.product.prodname_copilot_short %} usage is served, metered, or blocked.' +intro: 'Under usage-based billing, budget controls at the user, organization, cost center, and enterprise levels determine how {% data variables.product.prodname_copilot_short %} usage is served, metered, or blocked.' versions: feature: copilot -permissions: 'Enterprise owners and billing managers can set all budget controls. Organization owners can set organization-level and cost center budgets.' +permissions: 'Enterprise owners and billing managers can set all budget controls. Organization owners can set organization-level budgets.' product: '{% data variables.copilot.copilot_enterprise_short %} or {% data variables.copilot.copilot_business_short %}' contentType: concepts category: @@ -13,9 +13,9 @@ category: Every {% data variables.product.prodname_copilot_short %} license includes {% data variables.product.prodname_ai_credits_short %} that are pooled across your enterprise. Budget controls let you govern how individual users draw from that pool, and cap any additional spending once it's exhausted. This article explains what each budget control does, how the system evaluates them, and what happens when a limit is reached. -## Understanding the four budget controls +## Understanding budget controls -You have four budget controls, each serving a different purpose. They work together, not as alternatives. +You have budget controls at the user, organization, cost center, and enterprise levels, each serving a different purpose. They work together, not as alternatives. ### User-level budget @@ -38,6 +38,14 @@ A cost center budget caps metered charges for a defined group of users or an org When a cost center's budget is exhausted, only users in that cost center are blocked. Other users and cost centers are unaffected. +### Organization budget + +An organization budget caps metered charges for users who receive their {% data variables.product.prodname_copilot_short %} license through that organization. Like cost center budgets, it is only active after the shared pool is exhausted. + +Organization budgets are the only budget option available to organization owners. They can only further restrict usage below any budget set by an enterprise admin, and they cannot override a higher-level budget. + +If a user receives {% data variables.product.prodname_copilot_short %} licenses from multiple organizations, {% data variables.product.github %} picks one organization at random each billing cycle to bill the seat. This means the user's spend could count against a different organization's budget from month to month, making enforcement unpredictable. To avoid this, ensure each user has a single license through one organization, or use cost center budgets with direct user assignment. + ### Enterprise budget The enterprise budget caps total metered charges across your entire enterprise. Like cost center budgets, it is only active after the shared pool is exhausted. @@ -52,6 +60,7 @@ The enterprise budget caps total metered charges across your entire enterprise. | Universal user-level budget | Each user's total {% data variables.product.prodname_ai_credit_singular %} consumption | Always (pool + metered) | Per user | Always | | Individual user-level budget | A specific user's total consumption (overrides universal) | Always (pool + metered) | Per user | Always | | Cost center budget | A team's metered charges after pool exhaustion | Metered phase only | Per cost center | Only if "Stop usage when budget limit is reached" is enabled | +| Organization budget | An organization's metered charges after pool exhaustion | Metered phase only | Per organization | Only if "Stop usage when budget limit is reached" is enabled | | Enterprise budget | Total enterprise metered charges after pool exhaustion | Metered phase only | Enterprise-wide | Only if "Stop usage when budget limit is reached" is enabled | Any budget set to $0 USD stops usage immediately for the users it applies to. @@ -67,15 +76,16 @@ Each request for an {% data variables.product.prodname_ai_credit_singular %}-con 1. **User-level budget check.** The system first checks whether the user has exceeded their user-level budget. If yes, the request is blocked immediately—user-level budgets are always a hard stop. If no (or no ULB is set), the request continues. 1. **Shared pool check.** Next, the system checks whether the shared pool has {% data variables.product.prodname_ai_credits_short %} remaining. If yes, the request is served from the pool at no extra cost. If the pool is empty, the request moves to metered usage at {% data variables.product.prodname_ai_credits_value %} per {% data variables.product.prodname_ai_credit_singular %}. -1. **Cost center or enterprise check.** For metered usage, the system checks whether the user is assigned to a cost center. +1. **Cost center, organization, or enterprise check.** For metered usage, the system checks budgets in the following order: * **If the user is in a cost center:** The cost center's budget is checked. If budget remains, the cost center pays. If the budget is exhausted, the system checks whether "Stop usage when budget limit is reached" is enabled. - * **If the user is not in a cost center:** The enterprise spending limit is checked. If the limit has not been reached, the enterprise pays. If the limit has been reached, the system checks whether "Stop usage when budget limit is reached" is enabled. + * **If the user is not in a cost center but their license is billed to an organization with a budget:** The organization's budget is checked. If budget remains, the organization pays. If the budget is exhausted, the system checks whether "Stop usage when budget limit is reached" is enabled. + * **If no cost center or organization budget applies:** The enterprise spending limit is checked. If the limit has not been reached, the enterprise pays. If the limit has been reached, the system checks whether "Stop usage when budget limit is reached" is enabled. - In both cases, if "Stop usage when budget limit is reached" is on, the user is blocked. If it is off, charges continue to accrue without a cap. + In all cases, if "Stop usage when budget limit is reached" is on, the user is blocked. If it is off, charges continue to accrue without a cap. > [!IMPORTANT] -> "Stop usage when budget limit is reached" applies to enterprise spending limits and cost center budgets only, and is off by default. Without it, charges continue to accrue past the limit. Always enable it when creating a spending limit. User-level budgets always enforce a hard stop and do not have this setting. +> "Stop usage when budget limit is reached" applies to enterprise spending limits, cost center budgets, and organization budgets only, and is off by default. Without it, charges continue to accrue past the limit. Always enable it when creating a budget. User-level budgets always enforce a hard stop and do not have this setting. ## How user-level budgets and spending limits interact diff --git a/content/copilot/concepts/billing/usage-based-billing-for-organizations-and-enterprises.md b/content/copilot/concepts/billing/usage-based-billing-for-organizations-and-enterprises.md index bdd283caffc0..f158267ed7f8 100644 --- a/content/copilot/concepts/billing/usage-based-billing-for-organizations-and-enterprises.md +++ b/content/copilot/concepts/billing/usage-based-billing-for-organizations-and-enterprises.md @@ -68,12 +68,12 @@ Additional usage budgets are set in US dollars, and usage is shown in {% data va ## How can I control costs with budgets? -Budget controls let you govern how individual users draw from the shared pool and cap any additional spending once it's exhausted. You can set budgets at four levels: +Budget controls let you govern how individual users draw from the shared pool and cap any additional spending once it's exhausted. You can set budgets at multiple levels: * **User-level budgets** cap how much an individual user can consume per billing cycle, from both the shared pool and additional usage. A $0 USD user-level budget blocks the user immediately. * **Cost-center budgets** cap metered charges for a defined group of users after the pool is exhausted. * **Enterprise spending limits** cap total metered charges across your entire enterprise after the pool is exhausted. -* **Organization-level budgets** track spending for all repositories in the organization. +* **Organization-level budgets** cap metered charges for users whose {% data variables.product.prodname_copilot_short %} seats are billed to the organization, after the pool is exhausted. For a full explanation of how these controls work together and when usage gets blocked, see [AUTOTITLE](/copilot/concepts/billing/budgets-for-usage-based-billing). diff --git a/content/copilot/tutorials/budgets/optimizing-your-budget-configuration.md b/content/copilot/tutorials/budgets/optimizing-your-budget-configuration.md index 295d576efc21..bd807048094f 100644 --- a/content/copilot/tutorials/budgets/optimizing-your-budget-configuration.md +++ b/content/copilot/tutorials/budgets/optimizing-your-budget-configuration.md @@ -18,7 +18,7 @@ category: - Manage Copilot for a team --- -Before optimizing your budget configuration, make sure you understand how the four budget controls work and how the system evaluates them. See [AUTOTITLE](/copilot/concepts/billing/budgets-for-usage-based-billing). +Before optimizing your budget configuration, make sure you understand how budget controls work and how the system evaluates them. See [AUTOTITLE](/copilot/concepts/billing/budgets-for-usage-based-billing). If you haven't set up budgets yet, start with [AUTOTITLE](/copilot/tutorials/budgets/getting-started-with-budget-controls) to get the basics in place, then come back to this guide to optimize your configuration. @@ -37,9 +37,29 @@ If you also use cost center budgets, the sum of your cost center budgets and you > [!TIP] > Whenever you raise user-level budgets, re-check this calculation. Raising ULBs without raising the enterprise budget can cause the enterprise budget to block users before they reach their individual budgets. +## Choosing a scope + +For most enterprises, we recommend **cost center budgets with users directly assigned**. When users are assigned directly to a cost center, charges always follow the user, so enforcement is predictable regardless of how licenses are structured. + +| Scope | Use when | Who can set it | +| --- | --- | --- | +| Cost center budget | You want predictable organization-level spending control as an enterprise admin. | Enterprise owners, billing managers | +| Organization budget | Organization owners need to set their own spending limits without enterprise admin involvement. | Organization owners | +| Enterprise budget | You need a failsafe that caps total metered charges for all users not covered by a narrower budget. | Enterprise owners, billing managers | + +If users in your enterprise have {% data variables.product.prodname_copilot_short %} licenses from multiple organizations, both organization budgets and cost centers that only contain organizations (not users) will enforce unpredictably. The billing organization is chosen at random each cycle, so spend may count against a different budget from month to month. Assigning users directly to cost centers avoids this problem. + +### Migrating from organization budgets to cost centers + +If your enterprise already has organization budgets, they will continue to work. However, if you have users with {% data variables.product.prodname_copilot_short %} licenses assigned through multiple organizations, migrating to cost centers with direct user assignment gives more predictable enforcement. + +1. Create cost centers and assign users directly (not just organizations). +1. Set cost center budgets matching your desired spending caps. +1. Remove the organization budgets once cost center budgets are in place. + ## Common scenarios -The following scenarios show common budget configurations for different enterprise structures. Each one builds on the previous, adding more controls. Start with the simplest configuration that matches your needs. You can layer on additional controls later as your usage patterns become clearer. +The following scenarios show common budget configurations for different enterprise structures. ### Manage shared usage responsibly @@ -84,6 +104,20 @@ Consider enabling **cost center exclusion** if you want business units to operat This is the most granular configuration. It combines per-user controls (who can consume how much), per-team controls (how much metered spend each business unit can generate), and an enterprise-wide safety net. Use this when you have a mix of usage patterns across teams and need fine-grained governance. +### Delegating control to organization owners + +**Situation:** Organization owners need to set their own spending guardrails without involving an enterprise admin. + +**Configuration:** + +* Each organization owner sets an **organization budget** for their organization. +* The enterprise admin sets an **enterprise budget** as a safety net. +* Enable **"Stop usage when budget limit is reached"** on all budgets. + +Organization budgets are the only budget option available to organization owners. An organization budget can only further restrict usage below any budget set by an enterprise admin. It cannot override a higher-level budget. + +If users in your enterprise have {% data variables.product.prodname_copilot_short %} licenses assigned through multiple organizations, organization budgets may not enforce predictably for those users. In this case, {% data variables.product.github %} picks one organization at random each billing cycle to bill the seat. This means the user's spend could count against a different organization's budget from month to month, making enforcement unpredictable. To avoid this, ensure each user has a single license through one organization, or use cost center budgets with direct user assignment. + ## Using historical data to size budgets Your AI usage dashboard and the usage export CSV are the best tools for sizing budgets. Look at: diff --git a/data/reusables/copilot/code-review/custom-instructions-limit.md b/data/reusables/copilot/code-review/custom-instructions-limit.md index 86ec2eb08fc1..1596bc3065c0 100644 --- a/data/reusables/copilot/code-review/custom-instructions-limit.md +++ b/data/reusables/copilot/code-review/custom-instructions-limit.md @@ -1,3 +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_cloud_agent %}. -> * {% data reusables.copilot.code-review.custom-instructions-branch %} +> {% data reusables.copilot.code-review.custom-instructions-branch %} diff --git a/package.json b/package.json index 543eb1028096..c4764c901218 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ "prevent-pushes-to-main": "tsx src/workflows/prevent-pushes-to-main.ts", "purge-fastly-edge-cache": "tsx src/workflows/purge-fastly-edge-cache.ts", "purge-fastly-edge-cache-per-language": "tsx src/languages/scripts/purge-fastly-edge-cache-per-language.ts", + "purge-fastly-all": "tsx src/workflows/purge-fastly-all.ts", "readability-report": "tsx src/workflows/experimental/readability-report.ts", "ready-for-docs-review": "tsx src/workflows/ready-for-docs-review.ts", "release-banner": "tsx src/ghes-releases/scripts/release-banner.ts", diff --git a/src/secret-scanning/data/pattern-docs/fpt/public-docs.yml b/src/secret-scanning/data/pattern-docs/fpt/public-docs.yml index 0027e5b73a70..47c64d42597b 100644 --- a/src/secret-scanning/data/pattern-docs/fpt/public-docs.yml +++ b/src/secret-scanning/data/pattern-docs/fpt/public-docs.yml @@ -1679,6 +1679,16 @@ hasValidityCheck: false hasExtendedMetadata: false base64Supported: false + isduplicate: true +- provider: Datadog + supportedSecret: Datadog Personal Access Token + secretType: datadog_pat + isPublic: true + isPrivateWithGhas: false + hasPushProtection: false + hasValidityCheck: false + hasExtendedMetadata: false + base64Supported: false isduplicate: false - provider: Datadog supportedSecret: Datadog RCM @@ -1690,6 +1700,16 @@ hasExtendedMetadata: false base64Supported: false isduplicate: false +- provider: Datadog + supportedSecret: Datadog Service Account Token + secretType: datadog_sat + isPublic: true + isPrivateWithGhas: false + hasPushProtection: false + hasValidityCheck: false + hasExtendedMetadata: false + base64Supported: false + isduplicate: false - provider: Datastax supportedSecret: Datastax AstraCS Tokens secretType: datastax_astracs_token @@ -3431,7 +3451,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: true - provider: NuGet diff --git a/src/secret-scanning/data/pattern-docs/ghec/public-docs.yml b/src/secret-scanning/data/pattern-docs/ghec/public-docs.yml index 0027e5b73a70..47c64d42597b 100644 --- a/src/secret-scanning/data/pattern-docs/ghec/public-docs.yml +++ b/src/secret-scanning/data/pattern-docs/ghec/public-docs.yml @@ -1679,6 +1679,16 @@ hasValidityCheck: false hasExtendedMetadata: false base64Supported: false + isduplicate: true +- provider: Datadog + supportedSecret: Datadog Personal Access Token + secretType: datadog_pat + isPublic: true + isPrivateWithGhas: false + hasPushProtection: false + hasValidityCheck: false + hasExtendedMetadata: false + base64Supported: false isduplicate: false - provider: Datadog supportedSecret: Datadog RCM @@ -1690,6 +1700,16 @@ hasExtendedMetadata: false base64Supported: false isduplicate: false +- provider: Datadog + supportedSecret: Datadog Service Account Token + secretType: datadog_sat + isPublic: true + isPrivateWithGhas: false + hasPushProtection: false + hasValidityCheck: false + hasExtendedMetadata: false + base64Supported: false + isduplicate: false - provider: Datastax supportedSecret: Datastax AstraCS Tokens secretType: datastax_astracs_token @@ -3431,7 +3451,7 @@ isPrivateWithGhas: true hasPushProtection: true hasValidityCheck: true - hasExtendedMetadata: false + hasExtendedMetadata: '{% ifversion ghes %}false{% else %}true{% endif %}' base64Supported: false isduplicate: true - provider: NuGet diff --git a/src/workflows/purge-fastly-all.ts b/src/workflows/purge-fastly-all.ts new file mode 100644 index 000000000000..2d94ec0b0694 --- /dev/null +++ b/src/workflows/purge-fastly-all.ts @@ -0,0 +1,59 @@ +import { fetchWithRetry } from '@/frame/lib/fetch-utils' + +// Purges the ENTIRE Fastly cache for the docs service via Fastly's purge_all +// endpoint. This is the workflow equivalent of the "Purge all" button in the +// Fastly UI. It is intentionally a separate, manually triggered entry point: +// routine post-deploy purging is handled by +// `purge-fastly-edge-cache-per-language.ts`, and a gentler "refresh everything" +// can be done by running the Purge Fastly workflow with an empty languages +// input (a soft surrogate-key purge of every language). +// +// NOTE: Fastly's purge_all is always a HARD purge. The `fastly-soft-purge` +// header has no effect on this endpoint, so every object is evicted immediately +// and origin sees a traffic spike while the cache refills. Only reach for this +// when a targeted purge will not do. +// https://www.fastly.com/documentation/reference/api/purging/ + +async function purgeFastlyAll({ apiToken, serviceId }: { apiToken: string; serviceId: string }) { + const safeServiceId = encodeURIComponent(serviceId) + const requestPath = `https://api.fastly.com/service/${safeServiceId}/purge_all` + const response = await fetchWithRetry( + requestPath, + { + method: 'POST', + headers: { + 'fastly-key': apiToken, + accept: 'application/json', + 'Content-Type': 'application/json', + }, + }, + { + retries: 0, + timeout: 30_000, + throwHttpErrors: false, + }, + ) + if (!response.ok) { + // Fastly puts permission/feature-disabled details in the response body, + // which is often the only actionable signal, so surface it best-effort. + let body = '' + try { + body = await response.text() + } catch { + body = '' + } + throw new Error( + `Fastly purge_all failed: HTTP ${response.status} ${response.statusText}${body ? `: ${body}` : ''}`, + ) + } + return response +} + +const { FASTLY_TOKEN, FASTLY_SERVICE_ID } = process.env +if (!FASTLY_TOKEN || !FASTLY_SERVICE_ID) { + throw new Error('Fastly env vars not detected; refusing to run purge_all') +} + +console.log('Attempting Fastly purge_all (hard purge of the entire cache)...') +const result = await purgeFastlyAll({ apiToken: FASTLY_TOKEN, serviceId: FASTLY_SERVICE_ID }) +console.log('Fastly purge_all result:', result.status)