Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/instructions/content.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}.
```
10 changes: 10 additions & 0 deletions .github/workflows/changelog-agent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/copy-api-issue-to-internal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/delete-orphan-translation-files.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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" \
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/generate-code-scanning-query-lists.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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._'
4 changes: 3 additions & 1 deletion .github/workflows/moda-allowed-ips.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/orphaned-features-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/orphaned-files-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/purge-fastly-all.yml
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions .github/workflows/sme-review-tracking-issue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
});
4 changes: 3 additions & 1 deletion .github/workflows/sync-openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sync-sdk-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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

Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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

Expand Down
Loading
Loading