From 746c3f7c1220a54605788100adf70141e852297a Mon Sep 17 00:00:00 2001 From: docs-bot <77750099+docs-bot@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:46:09 -0700 Subject: [PATCH 01/11] Sync Copilot SDK docs (auto-generated) (#62581) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: sunbrye Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3d652994-a326-4b43-a774-a276c9b8470d --- .github/workflows/sync-sdk-docs.yml | 2 +- .../features-agent-loop-diagram-1.png | Bin 76662 -> 76669 bytes .../features-agent-loop-diagram-2.png | Bin 69668 -> 69649 bytes .../how-tos/copilot-sdk/auth/authenticate.md | 11 +- .../copilot/how-tos/copilot-sdk/auth/byok.md | 27 +- .../copilot/how-tos/copilot-sdk/auth/index.md | 1 + .../auth/server-to-server-tokens.md | 213 +++ .../copilot-sdk/features/custom-agents.md | 14 +- .../copilot-sdk/features/fleet-mode.md | 2 +- .../how-tos/copilot-sdk/features/hooks.md | 4 +- .../how-tos/copilot-sdk/features/index.md | 1 + .../copilot-sdk/features/remote-sessions.md | 10 +- .../copilot-sdk/features/session-limits.md | 2 +- .../copilot-sdk/features/streaming-events.md | 45 +- .../copilot-sdk/features/usage-and-billing.md | 1271 +++++++++++++++++ .../how-tos/copilot-sdk/getting-started.md | 5 +- .../copilot-sdk/hooks/hooks-overview.md | 6 +- .../copilot-sdk/hooks/session-lifecycle.md | 36 + .../hooks/user-prompt-submitted.md | 19 +- .../integrations/microsoft-agent-framework.md | 18 +- .../observability/opentelemetry.md | 2 +- .../how-tos/copilot-sdk/setup/bundled-cli.md | 4 +- .../how-tos/copilot-sdk/setup/local-cli.md | 4 +- .../how-tos/copilot-sdk/setup/scaling.md | 2 + .../troubleshooting/compatibility.md | 2 + 25 files changed, 1626 insertions(+), 75 deletions(-) create mode 100644 content/copilot/how-tos/copilot-sdk/auth/server-to-server-tokens.md create mode 100644 content/copilot/how-tos/copilot-sdk/features/usage-and-billing.md diff --git a/.github/workflows/sync-sdk-docs.yml b/.github/workflows/sync-sdk-docs.yml index 92b8679dbb09..b8d56344aaca 100644 --- a/.github/workflows/sync-sdk-docs.yml +++ b/.github/workflows/sync-sdk-docs.yml @@ -77,7 +77,7 @@ jobs: - name: Copy SDK docs run: | mkdir -p "$SDK_DOCS_TARGET" - rsync -av --exclude='.validation/' "$SDK_TMP/docs/" "$SDK_DOCS_TARGET/" + rsync -av --exclude='.validation/' --exclude='developer-docs/' "$SDK_TMP/docs/" "$SDK_DOCS_TARGET/" echo "Copied $(find "$SDK_DOCS_TARGET" -name '*.md' | wc -l | tr -d ' ') markdown files" - name: Normalize content diff --git a/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png b/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png index 99414b8c12b4c9cb25ce62cc1c35bfcdbb8d04c5..64dfe9d18b1af7170c6898fade6ee992741b66ff 100644 GIT binary patch delta 37162 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 XT;`d{xbsvYP=vwL)z4*}Q$iB}dRl&! delta 37155 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 z%0D47Jc^4cs;g^j2uQI!q*9FBsK96Pkg`I*i}ltNOpMG6L0;j%W`6%LP7A-Km63=o z?_QGH6itE*d38tJ*ZAIFAy2?=zI?<6?+tlFa(l;xm5D2;lT`NM`oN!j`<0Z#I2yyr z>wky-BX5|uMgDP8=O$iPcF9GfA9#KXPXJg)p5d@s+q{hKU}!6Y z9;&ft^1{-_9_u4|wB>2<>*LS-tdYXqM!(A9|;@n1hPROzmJ*2AK2^``)6 zP5VwU|NJ^huWsMcPen>9IYg?!ePz647K!ENCx#?a3Q{8 z%NkKy!D3IYHoB#}T3e(dnZG_#n7`eh0R5OEZVxC%13G2jP+(p)4Jr?Go!fNmK%LPZZxB*^tw% za0!4uijGZ=JHyU|9LhhSe`1P_SIS82;j+#1M1#h>P-QaJ!<3KUa7}Z&AWdC z=mqJN`ZM$w0(M{F{-8O!1SgNsI8vL6 z7sFtfpG+v%HPAGje+Vkxk4K9oHiFdzT`chj7=@V(DqO%6JOwR803|$Mnn} z8(&&_JO5PK(&O$oJB5`b6 zs=-% zJLMGDXt>ea+7G$>_n9of+n$Og3z}?ru3)~$K)XNX)RDv?JKs|X zZXWSeU=}~4K}?`{%nbI*NP(oHYiiJQ!h$6kSq7ErC{JtPHp{y`pWAX|@ZMW{#RXbh z?TxqmWigZggpB78Yid*SBVJd(?!%-J?6|N)_a%r`gK~THnZfw%>?{>;Fr%C*1*BgR z5W-I=!vdb{mYpeul)Lr*5O|}_Y~4cznP$jZ6c61>69-!gczAof9QoG%<#_PH=Wm&B zn6kc;X7b~6kCC`M3?DFaQ=&e7X` zPc#Kf(#_FPXN+>*|H3KgG#5#56U`jnUjD|>JmdGl?O^+`lO|Q=@cs5|G~o{<*liZN zt=4@u?a%K)dgN&G^lvKy!YVV+551+RNyvy(2R%%5f{*YTFH;w~qRnn{G6mK_xClAe z(_V9Ei=#FZ=iM5;lWUMq8n3kZGuD_wle+J*R7J(_NBh($NKE(gFHjN4I8)_eZ@<)n zHyI-~eL1KWc@-kYqtu0$0cqMzK<#CH$99rjR|B)$Q94}{Jhu_3+gj7W_1halzu~Gq zO*KgwF_FmdTEP}Fi<88}@4kGIILp#ec53J30m_BhTwGn{V)$ZH^viR`*wdeSZw_Cghc(kem14Y*e46T*vI~a??%ygn zK5xU)M_~{T)X;rsykuaU!rCU_EzVtj>Pac?M;n$4w{m{!e9X9KQ(5>kK}lDNf!XTW z+n4Ic%R_sty1niUFcMcc1JGp^73}Hb8=j=3=J2ewVJi{gXsO*)ky{Mf+A~aM71o0;rerBXV#R7;LiJu z3mF1y%?|lwO1nBLg#}qZ%5FS)L6VWGaM(Ayzq2XM$JRL9b?MQyS;va%=~atvY+%cz zp5W$v==C2kdjqPTpcE_UCSPASJub{qi0{sCJ9z$+=WbDe#8{8Z=8#681F*JGqF0rd z7t@Ivlq@4{k0;(%H|{XlmW)MEUl=ibYaZvyrK{q66+8D+xF^@}QW&4t-gB+bCU-?v zYQJeCQbIQ^hbvAPkJqmBJr+uW5rJ(ySZ1;9HeYIIhoU8E5I!O7IuWI#2M>R4z;#_d z^QS+spu7n|h8N^~}-%*u#Q`9rpib^VTww&hyR z=b1z)U-S8V^R_qKty{NyqU>t$jGdi%f$0^;ClM24eembklDRO)REs;^RBNTMJNp7+ zDf=y`;Eph_Y2({I+0=TPnDX7FR`J86l8yed57`UrpXM`GCUOH}9>q~b=x=2I*&1}H zjt#TT)$KjAIp`oGGrBCP=e7OwaSN}SQH{^Gc4Y7Y2}oS5$*=vH`~0i7R79t8`|Edm z!5<@J7)(1J3n4Bh!H6;~``6Zsv(~!?h~1zMc%pJ z0_X1K+P)sH+%-!MJuoa|NG#nR(boLdp2#2+roYtYmZ9Ewn>V%QCg?$p5gtBv{~sZ_ zuXSy=mEJoKzliN2-q54DYT%NUT2PdcU1u1^%F1wLTR&{mWJ>)6Q@{RmYADXsyR$ObDDAf+ z?JbAeKY)4jf>`Oz#2_c(ialg?#27onQTI{fZO8C1c?h$9{CVB3z8bjKmapF5_U9Vw@sk*+C={Jn4+!~xc9W_;2 z^fZ1A-*c<9{oardYNRsK;*?|X)w)gVR@=7ox^Xd;X;b5`q>dBU<{=Ll@=v%I-`|L~ ze_;Kops;;S7~O3}G5E!=X`&uR)?z>2S`OV|Vc+v~z=RdswMPH?oOp*dB7nl7%kxSF z7--t-PSQbsAR}|z#g2<|fol9k4@XuaN?TGftW%|eON)agMR$JPRjyJUvD<5eQG;Ff z1Wf(D-PrkXqB*25b1pmDc-505nv-#4T1!o0Ou}Zkfcs#_&Y@I`rO@^fTj2KWe)0$u z$umi~a56nuDbsk_16cz1rox^XeR)-Q%!BJX+rUI!{fPhE+((VxAwdrylqD`)LB|6% z?#!x{Z9TD4yWg{(6Ig$7+Ajy%tAAcvcqbUJqg4H+OP(d`_gX9qnN)y((WC>7HOa<(CfkR{-hplW><6yv$G%S9}+1&J;{* zpnvvTImqL{^9-lti;hjFz3PF|@`+pPK)5Lt6j8TRMaTK0J(gvH1-)eAF-FV5~lvUVZe@*%19Lxm(K8IXCb7 F{2wS`P{sfN delta 33093 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

X7(}8Dv=}QDBPbPA*8Y%Zu4FASZ;mqWEUxIA~ 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 z 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 diff --git a/content/copilot/how-tos/copilot-sdk/auth/authenticate.md b/content/copilot/how-tos/copilot-sdk/auth/authenticate.md index d0758235e458..622a8be3ac3b 100644 --- a/content/copilot/how-tos/copilot-sdk/auth/authenticate.md +++ b/content/copilot/how-tos/copilot-sdk/auth/authenticate.md @@ -23,7 +23,8 @@ contentType: how-tos | [GitHub Signed-in User](#github-signed-in-user) | Interactive apps where users sign in with GitHub | Yes | | [OAuth GitHub App](#oauth-github-app) | Apps acting on behalf of users via OAuth | Yes | | [Environment Variables](#environment-variables) | CI/CD, automation, server-to-server | Yes | -| [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) | Using your own API keys (Azure AI Foundry, OpenAI, and more) | No | +| [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/server-to-server-tokens) | Organization-attributed automation and direct organization billing | No user subscription; organization policy required | +| [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) | Using your own API keys (Microsoft Foundry, OpenAI, and more) | No | ## GitHub signed-in user @@ -231,6 +232,8 @@ client.start().get(); For automation, CI/CD pipelines, and server-to-server scenarios, you can authenticate using environment variables. +For organization-attributed automation that should not use a user's personal access token, see [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/server-to-server-tokens). + **Supported environment variables (in priority order):** 1. `COPILOT_GITHUB_TOKEN` - Recommended for explicit Copilot usage 1. `GH_TOKEN` - GitHub CLI compatible @@ -276,16 +279,16 @@ await client.start() ## BYOK (bring your own key) -BYOK allows you to use your own API keys from model providers like Azure AI Foundry, OpenAI, or Anthropic. This bypasses GitHub Copilot authentication entirely. +BYOK allows you to use your own API keys from model providers like Microsoft Foundry, OpenAI, or Anthropic. This bypasses GitHub Copilot authentication entirely. **Key benefits:** * No GitHub Copilot subscription required * Use enterprise model deployments * Direct billing with your model provider -* Support for Azure AI Foundry, OpenAI, Anthropic, and OpenAI-compatible endpoints +* Support for Microsoft Foundry, OpenAI, Anthropic, and OpenAI-compatible endpoints **See the [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) for complete details**, including: -* Azure AI Foundry setup +* Microsoft Foundry setup * Provider configuration options * Limitations and considerations * Complete code examples diff --git a/content/copilot/how-tos/copilot-sdk/auth/byok.md b/content/copilot/how-tos/copilot-sdk/auth/byok.md index ed7cfe3ba9c7..368a3266da6f 100644 --- a/content/copilot/how-tos/copilot-sdk/auth/byok.md +++ b/content/copilot/how-tos/copilot-sdk/auth/byok.md @@ -22,15 +22,15 @@ contentType: how-tos | Provider | Type Value | Notes | |----------|------------|-------| | OpenAI | `"openai"` | OpenAI API and OpenAI-compatible endpoints | -| Azure OpenAI / Azure AI Foundry | `"azure"` | Azure-hosted models | +| Microsoft Foundry / Azure OpenAI | `"openai"` or `"azure"` | Use `"openai"` for `/openai/v1/`; use `"azure"` for native Azure endpoints | | Anthropic | `"anthropic"` | Claude models | | Ollama | `"openai"` | Local models via OpenAI-compatible API | | Microsoft Foundry Local | `"openai"` | Run AI models locally on your device via OpenAI-compatible API | | Other OpenAI-compatible | `"openai"` | vLLM, LiteLLM, etc. | -## Quick start: Azure AI Foundry +## Quick start: Microsoft Foundry -Azure AI Foundry (formerly Azure OpenAI) is a common BYOK deployment target for enterprises. Here's a complete example: +Microsoft Foundry is a common BYOK deployment target for enterprises. Here's a complete example: {% codetabs %} {% codetab python %} @@ -215,7 +215,7 @@ client.stop().get(); | `bearerToken` / `bearer_token` | string | Bearer token auth (takes precedence over apiKey) | | `bearerTokenProvider` / `bearer_token_provider` | callback | Returns a bearer token on demand (takes precedence over `apiKey` and `bearerToken`) | | `wireApi` / `wire_api` | `"completions"` \| `"responses"` | Select `"completions"` for broad model compatibility (the Chat Completions API); select `"responses"` for multi-turn state management, tool namespacing, and reasoning support (the Responses API). Anthropic models always use the Messages API regardless of this setting. | -| `azure.apiVersion` / `azure.api_version` | string | Azure API version (default: `"2024-10-21"`) | +| `azure.apiVersion` / `azure.api_version` | string | Azure API version. When set, the runtime uses the versioned deployment route; when omitted, it uses the GA versionless `v1` route. | ### Wire API format @@ -268,9 +268,9 @@ provider: { } ``` -### Azure AI Foundry (OpenAI-compatible endpoint) +### Microsoft Foundry (OpenAI-compatible endpoint) -For Azure AI Foundry deployments with `/openai/v1/` endpoints, use `type: "openai"`: +For Microsoft Foundry deployments with `/openai/v1/` endpoints, use `type: "openai"`: ```typescript provider: { @@ -494,14 +494,6 @@ Results are cached after the first call, just like the default behavior. The han ## Limitations -When using BYOK, be aware of these limitations: - -### Identity limitations - -BYOK authentication uses **static credentials only**. - -You must use an API key or static bearer token that you manage yourself. - ### Feature limitations Some Copilot features may behave differently with BYOK: @@ -515,9 +507,8 @@ Some Copilot features may behave differently with BYOK: | Provider | Limitations | |----------|-------------| -| Azure AI Foundry | No Entra ID auth; must use API keys | -| Ollama | No API key; local only; model support varies | | [Microsoft Foundry Local](https://foundrylocal.ai) | Local only; model availability depends on device hardware; no API key required | +| Ollama | No API key; local only; model support varies | | OpenAI | Subject to OpenAI rate limits and quotas | ## Troubleshooting @@ -574,7 +565,7 @@ provider: { } ``` -However, if your Azure AI Foundry deployment provides an OpenAI-compatible endpoint path (e.g., `/openai/v1/`), use `type: "openai"`: +However, if your Microsoft Foundry deployment provides an OpenAI-compatible endpoint path (for example, `/openai/v1/`), use `type: "openai"`: @@ -594,7 +585,7 @@ const session = await client.createSession({ ```typescript -// ✅ Correct: OpenAI-compatible Azure AI Foundry endpoint +// ✅ Correct: OpenAI-compatible Microsoft Foundry endpoint provider: { type: "openai", baseUrl: "https://your-resource.openai.azure.com/openai/v1/", diff --git a/content/copilot/how-tos/copilot-sdk/auth/index.md b/content/copilot/how-tos/copilot-sdk/auth/index.md index a566036877ee..688184a61eee 100644 --- a/content/copilot/how-tos/copilot-sdk/auth/index.md +++ b/content/copilot/how-tos/copilot-sdk/auth/index.md @@ -12,6 +12,7 @@ contentType: how-tos children: - /authenticate - /byok + - /server-to-server-tokens --- diff --git a/content/copilot/how-tos/copilot-sdk/auth/server-to-server-tokens.md b/content/copilot/how-tos/copilot-sdk/auth/server-to-server-tokens.md new file mode 100644 index 000000000000..a063fb2aa1ef --- /dev/null +++ b/content/copilot/how-tos/copilot-sdk/auth/server-to-server-tokens.md @@ -0,0 +1,213 @@ +--- +title: Server-to-server authentication +shortTitle: Server-to-server tokens +intro: >- + Use a short-lived installation access token when a service needs to make + Copilot requests on behalf of an organization without a user's credentials. In + GitHub Actions, use the built-in `GITHUB_TOKEN` instead. +versions: + fpt: '*' + ghec: '*' +contentType: how-tos +--- + + + + +## GitHub Actions + +For workflows in an organization-owned repository, grant the built-in token permission to make Copilot requests: + +```yaml +permissions: + contents: read + copilot-requests: write + +jobs: + copilot: + runs-on: ubuntu-latest + steps: + - uses: {% data reusables.actions.action-checkout %} + - run: your-application + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` + +The organization's **Allow use of Copilot CLI billed to the organization** policy must be enabled. This approach needs no GitHub App or stored authentication secret. For details, see [AUTOTITLE](/copilot/how-tos/copilot-cli/use-copilot-cli-in-actions). + +## Other services and CI systems + +For services outside GitHub Actions: + +1. Create a GitHub App with the **Copilot Requests** repository permission set to **Read & write**. +1. Install it on the organization that should be billed. The current Copilot permission check requires **All repositories** access. +1. [AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app) with a repository ID and the Copilot permission: + + ```json + { + "repository_ids": [123456789], + "permissions": { + "copilot_requests": "write" + } + } + ``` + +1. Pass the resulting `ghs_` token to the runtime as `COPILOT_GITHUB_TOKEN`. + +The organization must be enabled for Copilot requests from GitHub App installations. Installation tokens expire after one hour. + +> [!WARNING] +> Do not pass an installation token through the SDK's `gitHubToken`, `github_token`, or equivalent option. That option is for user tokens. Installation tokens must use the runtime environment authentication path. + +## Configure the runtime + +The following examples assume the minted token is in `INSTALLATION_TOKEN`. They pass it only to the child runtime and disable fallback to stored user credentials. + +{% codetabs %} +{% codetab typescript %} + +```typescript +import { CopilotClient, RuntimeConnection } from "@github/copilot-sdk"; + +const token = process.env.INSTALLATION_TOKEN; +if (!token) throw new Error("INSTALLATION_TOKEN is required"); + +const client = new CopilotClient({ + connection: RuntimeConnection.forStdio(), + env: { + ...process.env, + COPILOT_GITHUB_TOKEN: token, + }, + useLoggedInUser: false, +}); +``` + +{% endcodetab %} +{% codetab python %} + +```python +import os + +from copilot import CopilotClient, RuntimeConnection + +client = CopilotClient( + connection=RuntimeConnection.for_stdio(), + env={**os.environ, "COPILOT_GITHUB_TOKEN": os.environ["INSTALLATION_TOKEN"]}, + use_logged_in_user=False, +) +``` + +{% endcodetab %} +{% codetab go %} + +```golang +package main + +import ( + "log" + "os" + + copilot "github.com/github/copilot-sdk/go" +) + +func main() { + token, ok := os.LookupEnv("INSTALLATION_TOKEN") + if !ok { + log.Fatal("INSTALLATION_TOKEN is required") + } + client := copilot.NewClient(&copilot.ClientOptions{ + Connection: copilot.StdioConnection{}, + Env: append(os.Environ(), "COPILOT_GITHUB_TOKEN="+token), + UseLoggedInUser: copilot.Bool(false), + }) + _ = client +} +``` + +{% endcodetab %} +{% codetab rust %} + +```rust +use github_copilot_sdk::{ClientOptions, Transport}; + +fn main() { + let token = std::env::var("INSTALLATION_TOKEN").expect("INSTALLATION_TOKEN is required"); + let options = ClientOptions::new() + .with_transport(Transport::Stdio) + .with_env([("COPILOT_GITHUB_TOKEN", token)]) + .with_use_logged_in_user(false); + drop(options); +} +``` + +{% endcodetab %} +{% codetab dotnet %} + +```csharp +using System.Collections; +using GitHub.Copilot; + +var token = Environment.GetEnvironmentVariable("INSTALLATION_TOKEN") + ?? throw new InvalidOperationException("INSTALLATION_TOKEN is required"); +var environment = Environment.GetEnvironmentVariables() + .Cast() + .ToDictionary(entry => (string)entry.Key, entry => entry.Value?.ToString() ?? ""); +environment["COPILOT_GITHUB_TOKEN"] = token; + +await using var client = new CopilotClient(new CopilotClientOptions +{ + Connection = RuntimeConnection.ForStdio(), + Environment = environment, + UseLoggedInUser = false, +}); +``` + +{% endcodetab %} +{% codetab java %} + +```java +import com.github.copilot.CopilotClient; +import com.github.copilot.rpc.CopilotClientOptions; +import java.util.HashMap; +import java.util.Objects; + +var environment = new HashMap<>(System.getenv()); +var token = Objects.requireNonNull( + System.getenv("INSTALLATION_TOKEN"), "INSTALLATION_TOKEN is required"); +environment.put("COPILOT_GITHUB_TOKEN", token); + +try (var client = new CopilotClient(new CopilotClientOptions() + .setEnvironment(environment) + .setUseLoggedInUser(false))) { + // Use the client. +} +``` + +{% endcodetab %} +{% endcodetabs %} + +For in-process FFI, set `COPILOT_GITHUB_TOKEN` in the host environment before loading the runtime; per-client environment options are not supported. For an existing runtime URI, set it on that runtime process. + +## Refresh tokens + +Mint a new installation token before the current token expires. For a child process, restart the SDK client with the new environment. For an in-process or existing runtime, restart the host runtime with the new token. + +## Billing + +Usage is attributed and billed to the account that owns the GitHub App installation. Use an organization installation for organization billing; a user-account installation attributes usage to that user. + +## Troubleshooting + +| Symptom | Check | +|---|---| +| `401 Unauthorized` | Confirm the organization supports GitHub App installation authentication for Copilot. | +| `403 Resource not accessible by integration` or an error mentioning user information | Confirm the installation token is in `COPILOT_GITHUB_TOKEN`, not the SDK's explicit token option. | +| `403 Forbidden` from the Copilot API | Confirm the token request contains `repository_ids` and `copilot_requests: write`. | +| `403 Forbidden` with the required token request | Confirm the app installation has **All repositories** access, then mint a new token. | +| Requested model is unavailable | Confirm the organization's Copilot policy allows the model and the bundled runtime supports it. | +| Wrong account billed | Confirm the installation belongs to the intended organization. | + +## Further reading + +* [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/authenticate): other authentication methods and priority +* [AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app): GitHub App token creation diff --git a/content/copilot/how-tos/copilot-sdk/features/custom-agents.md b/content/copilot/how-tos/copilot-sdk/features/custom-agents.md index 06601dca30e5..81ba6db1155b 100644 --- a/content/copilot/how-tos/copilot-sdk/features/custom-agents.md +++ b/content/copilot/how-tos/copilot-sdk/features/custom-agents.md @@ -251,12 +251,12 @@ try (var client = new CopilotClient()) { | `infer` | `boolean` | | Whether the runtime can auto-select this agent (default: `true`) | | `skills` | `string[]` | | Skill names to preload into the agent's context at startup | | `model` | `string` | | Model identifier to use while this agent runs | -| `reasoningEffort` | `string` | | Reasoning effort to use while this agent runs. When omitted, no override is sent and the backend chooses its default | +| `reasoningEffort` | `string` | | Reasoning effort to use while this agent runs. When omitted, the SDK sends no per-agent override and the runtime resolves the effort (see note below) | > [!TIP] > A good `description` helps the runtime match user intent to the right agent. Be specific about the agent's expertise and capabilities. -Set `model` and `reasoningEffort` to override the parent session's model settings while a custom agent runs. When `reasoningEffort` is omitted, the SDK sends no per-agent override and the backend chooses its default. The parent session effort is not inherited, and the SDK does not add a per-agent default. Python uses `reasoning_effort`, .NET uses `ReasoningEffort`, Go uses `ReasoningEffort`, Java uses `setReasoningEffort`, and Rust uses `with_reasoning_effort`. +Set `model` and `reasoningEffort` to override the parent session's model settings while a custom agent runs. When `reasoningEffort` is omitted, the SDK sends no per-agent override and the runtime resolves the effort from its own precedence: a per-call client option, the resolved model's default, or the agent definition all take priority; otherwise the runtime inherits the parent session's effort only when the subagent runs the same model as the parent. When the subagent resolves to a different model, it falls back to that model's default instead of inheriting the parent's effort. Python uses `reasoning_effort`, .NET uses `ReasoningEffort`, Go uses `ReasoningEffort`, Java uses `setReasoningEffort`, and Rust uses `with_reasoning_effort`. In addition to per-agent configuration above, you can set `agent` on the **session config** itself to pre-select which custom agent is active when the session starts. See [Selecting an Agent at Session Creation](#selecting-an-agent-at-session-creation) below. @@ -440,9 +440,9 @@ Sub-agent-originated session events share the parent session stream and include | Event | Emitted when | Data | |-------|-------------|------| | `subagent.selected` | Runtime selects an agent for the task | `agentName`, `agentDisplayName`, `tools` | -| `subagent.started` | Sub-agent begins execution | `toolCallId`, `agentName`, `agentDisplayName`, `agentDescription` | -| `subagent.completed` | Sub-agent finishes successfully | `toolCallId`, `agentName`, `agentDisplayName` | -| `subagent.failed` | Sub-agent encounters an error | `toolCallId`, `agentName`, `agentDisplayName`, `error` | +| `subagent.started` | Sub-agent begins execution | `toolCallId`, `agentName`, `agentDisplayName`, `agentDescription`, `model?` | +| `subagent.completed` | Sub-agent finishes successfully | `toolCallId`, `agentName`, `agentDisplayName`, `model?`, `durationMs?`, `totalTokens?`, `totalToolCalls?` | +| `subagent.failed` | Sub-agent encounters an error | `toolCallId`, `agentName`, `agentDisplayName`, `error`, `model?`, `durationMs?`, `totalTokens?`, `totalToolCalls?` | | `subagent.deselected` | Runtime switches away from the sub-agent |—| ### Subscribing to events @@ -461,11 +461,15 @@ session.on((event) => { case "subagent.completed": console.log(`✅ Sub-agent completed: ${event.data.agentDisplayName}`); + if (event.data.durationMs !== undefined) console.log(` Duration: ${event.data.durationMs}ms`); + if (event.data.totalTokens !== undefined) console.log(` Tokens: ${event.data.totalTokens}`); + if (event.data.totalToolCalls !== undefined) console.log(` Tool calls: ${event.data.totalToolCalls}`); break; case "subagent.failed": console.log(`❌ Sub-agent failed: ${event.data.agentDisplayName}`); console.log(` Error: ${event.data.error}`); + if (event.data.durationMs !== undefined) console.log(` Duration: ${event.data.durationMs}ms`); break; case "subagent.selected": diff --git a/content/copilot/how-tos/copilot-sdk/features/fleet-mode.md b/content/copilot/how-tos/copilot-sdk/features/fleet-mode.md index 0605fd9dfedc..a576c9681e0a 100644 --- a/content/copilot/how-tos/copilot-sdk/features/fleet-mode.md +++ b/content/copilot/how-tos/copilot-sdk/features/fleet-mode.md @@ -181,7 +181,7 @@ Native typed bindings for fleet mode were verified in Node.js/TypeScript, Python Plan-mode UIs can start fleet deployment by returning the `autopilot_fleet` exit action. The generated session event types describe it as: ```typescript -type PlanModeExitAction = +type ExitPlanModeAction = | "exit_only" | "interactive" | "autopilot" diff --git a/content/copilot/how-tos/copilot-sdk/features/hooks.md b/content/copilot/how-tos/copilot-sdk/features/hooks.md index 3150f4e50db6..a4b4a9e089c0 100644 --- a/content/copilot/how-tos/copilot-sdk/features/hooks.md +++ b/content/copilot/how-tos/copilot-sdk/features/hooks.md @@ -26,12 +26,12 @@ A hook is a callback you register once when creating a session. The SDK invokes | Hook | When it fires | What you can do | | ------------------------------------------------------------------- | ----------------------------------- | ------------------------------------------ | -| [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/session-lifecycle#session-start-hook-session-start) | Session begins (new or resumed) | Inject context, load preferences | +| [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/session-lifecycle#session-start) | Session begins (new or resumed) | Inject context, load preferences | | [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/user-prompt-submitted) | User sends a message | Rewrite prompts, add context, filter input | | [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/pre-tool-use) | Before a tool executes | Allow / deny / modify the call | | [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/post-tool-use) | After a tool returns (success only) | Transform results, redact secrets, audit | | [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/post-tool-use#failure-variant) | After a tool returns a failure | Inject retry guidance, log failures | -| [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/session-lifecycle#session-end-hook-session-end) | Session ends | Clean up, record metrics | +| [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/session-lifecycle#session-end) | Session ends | Clean up, record metrics | | [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/error-handling) | An error is raised | Custom logging, retry logic, alerts | All hooks are **optional**—register only the ones you need. Returning `null` (or the language equivalent) from any hook tells the SDK to continue with default behavior. diff --git a/content/copilot/how-tos/copilot-sdk/features/index.md b/content/copilot/how-tos/copilot-sdk/features/index.md index cad1052de841..1348d6a3171f 100644 --- a/content/copilot/how-tos/copilot-sdk/features/index.md +++ b/content/copilot/how-tos/copilot-sdk/features/index.md @@ -25,6 +25,7 @@ children: - /skills - /steering-and-queueing - /streaming-events + - /usage-and-billing --- diff --git a/content/copilot/how-tos/copilot-sdk/features/remote-sessions.md b/content/copilot/how-tos/copilot-sdk/features/remote-sessions.md index d2b32e692c9c..e1833393c8c4 100644 --- a/content/copilot/how-tos/copilot-sdk/features/remote-sessions.md +++ b/content/copilot/how-tos/copilot-sdk/features/remote-sessions.md @@ -28,7 +28,7 @@ For running sessions on GitHub-hosted compute, see [AUTOTITLE](/copilot/how-tos/ ### Always-on (client-level) -Set `remote: true` when creating the client. Every session in a GitHub repo automatically gets a remote URL. +Set `enableRemoteSessions: true` when creating the client. Every session in a GitHub repo automatically gets a remote URL. @@ -39,7 +39,7 @@ Set `remote: true` when creating the client. Every session in a GitHub repo auto ```typescript import { CopilotClient } from "@github/copilot-sdk"; -const client = new CopilotClient({ remote: true }); +const client = new CopilotClient({ enableRemoteSessions: true }); const session = await client.createSession({ workingDirectory: "/path/to/github-repo", onPermissionRequest: async () => ({ allowed: true }), @@ -77,7 +77,7 @@ session.on(on_event) ```golang -client, _ := copilot.NewClient(&copilot.ClientOptions{Remote: true}) +client := copilot.NewClient(&copilot.ClientOptions{EnableRemoteSessions: true}) session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ WorkingDirectory: "/path/to/github-repo", OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) { @@ -97,7 +97,7 @@ session.On(func(event copilot.SessionEvent) { ```csharp -var client = new CopilotClient(new CopilotClientOptions { Remote = true }); +var client = new CopilotClient(new CopilotClientOptions { EnableRemoteSessions = true }); var session = await client.CreateSessionAsync(new SessionConfig { WorkingDirectory = "/path/to/github-repo", @@ -226,6 +226,6 @@ The remote URL can be rendered as a QR code for easy mobile access. The SDK prov ## Notes -* The `remote` client option only applies when the SDK spawns the CLI process. It is ignored when connecting to an external server via `cliUrl`. +* The `enableRemoteSessions` client option applies when the SDK starts the runtime, either as a child process or as an in-process host. It is ignored when connecting to an already-running runtime. * If the working directory is not a GitHub repository, remote setup is silently skipped (always-on mode) or returns an error (on-demand mode). * Remote sessions require authentication. Ensure `gitHubToken` or `useLoggedInUser` is configured. diff --git a/content/copilot/how-tos/copilot-sdk/features/session-limits.md b/content/copilot/how-tos/copilot-sdk/features/session-limits.md index 63e811411a8e..4afbb3e36253 100644 --- a/content/copilot/how-tos/copilot-sdk/features/session-limits.md +++ b/content/copilot/how-tos/copilot-sdk/features/session-limits.md @@ -138,7 +138,7 @@ let limits = SessionLimitsConfig { let session = client .create_session( - SessionConfig::new() + SessionConfig::default() .approve_all_permissions() .with_session_limits(limits.clone()), ) diff --git a/content/copilot/how-tos/copilot-sdk/features/streaming-events.md b/content/copilot/how-tos/copilot-sdk/features/streaming-events.md index 2bc4f130b6a0..7f86ac2acbc4 100644 --- a/content/copilot/how-tos/copilot-sdk/features/streaming-events.md +++ b/content/copilot/how-tos/copilot-sdk/features/streaming-events.md @@ -181,7 +181,7 @@ session.on(AssistantMessageDeltaEvent.class, event -> {% endcodetabs %} > [!TIP] -> **(Python / Go)** These SDKs use a single `Data` class/struct with all possible fields as optional/nullable. Only the fields listed in the tables below are populated for each event type—the rest will be `None` / `nil`. +> **(Python / Go)** These SDKs use separate, per-event data types (for example, `AssistantMessageDeltaData`), so only the relevant fields exist on each type. > > [!TIP] > **(.NET)** The .NET SDK uses separate, strongly-typed data classes per event (e.g., `AssistantMessageDeltaData`), so only the relevant fields exist on each type. @@ -405,12 +405,20 @@ Ephemeral. Token usage and cost information for an individual API call. | `model` | `string` | ✅ | Model identifier (e.g., `"gpt-5.4"`) | | `inputTokens` | `number` | | Input tokens consumed | | `outputTokens` | `number` | | Output tokens produced | +| `reasoningTokens` | `number` | | Output tokens used for reasoning/chain-of-thought (subset of `outputTokens`) | | `cacheReadTokens` | `number` | | Tokens read from prompt cache | | `cacheWriteTokens` | `number` | | Tokens written to prompt cache | +| `cacheExpiresAt` | `string` | | ISO 8601 timestamp when the prompt cache for this model call expires | +| `contentFilterTriggered` | `boolean` | | Whether the response was blocked or truncated by content filtering (`finish_reason === 'content_filter'`) | +| `finishReason` | `string` | | Model finish reason (e.g., `"stop"`, `"length"`, `"tool_calls"`, `"content_filter"`) | | `cost` | `number` | | Model multiplier cost for billing | | `duration` | `number` | | API call duration in milliseconds | +| `timeToFirstTokenMs` | `number` | | Time from request dispatch to first token received (streaming latency) | +| `interTokenLatencyMs` | `number` | | Average latency between consecutive tokens (streaming throughput) | +| `reasoningEffort` | `string` | | Reasoning effort level used for this call (e.g., `"low"`, `"medium"`, `"high"`) | | `initiator` | `string` | | What triggered this call (e.g., `"sub-agent"`); absent for user-initiated | | `apiCallId` | `string` | | Completion ID from the provider (e.g., `chatcmpl-abc123`) | +| `serviceRequestId` | `string` | | Copilot service request ID (`x-copilot-service-request-id`) for CAPI log correlation | | `apiEndpoint` | `"/chat/completions" \| "/v1/messages" \| "/responses" \| "ws:/responses"` | | API endpoint used for the model call; useful for observability and cost attribution. `ws:/responses` is the websocket variant of the responses API | | `providerCallId` | `string` | | GitHub request tracing ID (`x-github-request-id`) | | `parentToolCallId` | `string` | | Deprecated. Use envelope-level `agentId` for sub-agent attribution | @@ -615,7 +623,7 @@ These events are emitted when the agent needs approval or input from the user be ### `permission.requested` -Ephemeral. The agent needs permission to perform an action (run a command, write a file, etc.). +The agent needs permission to perform an action (run a command, write a file, etc.). | Data Field | Type | Required | Description | |------------|------|----------|-------------| @@ -638,7 +646,7 @@ All `kind` variants also include an optional `toolCallId` linking back to the to ### `permission.completed` -Ephemeral. A permission request was resolved. +A permission request was resolved. | Data Field | Type | Required | Description | |------------|------|----------|-------------| @@ -695,6 +703,7 @@ A custom agent was invoked as a sub-agent. | `agentName` | `string` | ✅ | Internal name of the sub-agent | | `agentDisplayName` | `string` | ✅ | Human-readable display name | | `agentDescription` | `string` | ✅ | Description of what the sub-agent does | +| `model` | `string` | | Model the sub-agent will run with, when known at start | ### `subagent.completed` @@ -705,6 +714,10 @@ A sub-agent finished successfully. | `toolCallId` | `string` | ✅ | Matches the corresponding `subagent.started` | | `agentName` | `string` | ✅ | Internal name | | `agentDisplayName` | `string` | ✅ | Display name | +| `model` | `string` | | Model used by the sub-agent | +| `durationMs` | `number` | | Wall-clock execution duration in milliseconds | +| `totalTokens` | `number` | | Total input and output tokens consumed | +| `totalToolCalls` | `number` | | Total tool calls made | ### `subagent.failed` @@ -716,6 +729,10 @@ A sub-agent encountered an error. | `agentName` | `string` | ✅ | Internal name | | `agentDisplayName` | `string` | ✅ | Display name | | `error` | `string` | ✅ | Error message | +| `model` | `string` | | Model selected for the sub-agent, when known | +| `durationMs` | `number` | | Wall-clock execution duration in milliseconds | +| `totalTokens` | `number` | | Total input and output tokens consumed before failure | +| `totalToolCalls` | `number` | | Total tool calls made before failure | ### `subagent.selected` @@ -780,7 +797,7 @@ A system or developer prompt was injected into the conversation. ### `external_tool.requested` -Ephemeral. The agent wants to invoke an external tool (one provided by the SDK consumer). +The agent wants to invoke an external tool (one provided by the SDK consumer). | Data Field | Type | Required | Description | |------------|------|----------|-------------| @@ -792,7 +809,7 @@ Ephemeral. The agent wants to invoke an external tool (one provided by the SDK c ### `external_tool.completed` -Ephemeral. An external tool request was resolved. +An external tool request was resolved. | Data Field | Type | Required | Description | |------------|------|----------|-------------| @@ -870,8 +887,8 @@ assistant.turn_start → Turn begins ├── assistant.usage → Token usage for this API call (ephemeral) │ ├── [If tools were requested:] -│ ├── permission.requested → Needs user approval (ephemeral) -│ ├── permission.completed → Approval result (ephemeral) +│ ├── permission.requested → Needs user approval +│ ├── permission.completed → Approval result │ ├── tool.execution_start → Tool begins │ ├── tool.execution_partial_result → Streaming tool output (ephemeral, repeated) │ ├── tool.execution_progress → Progress updates (ephemeral, repeated) @@ -914,23 +931,23 @@ This table lists key `data` payload fields. Common envelope fields are documente | `session.usage_checkpoint` | | Session | `totalNanoAiu`, `totalPremiumRequests?` | | `session.task_complete` | | Session | `summary?` | | `session.shutdown` | | Session | `shutdownType`, `codeChanges`, `modelMetrics` | -| `permission.requested` | ✅ | Permission | `requestId`, `permissionRequest` | -| `permission.completed` | ✅ | Permission | `requestId`, `result.kind` | +| `permission.requested` | | Permission | `requestId`, `permissionRequest` | +| `permission.completed` | | Permission | `requestId`, `result.kind` | | `user_input.requested` | ✅ | User Input | `requestId`, `question`, `choices?` | | `user_input.completed` | ✅ | User Input | `requestId` | | `elicitation.requested` | ✅ | User Input | `requestId`, `message`, `requestedSchema` | | `elicitation.completed` | ✅ | User Input | `requestId` | -| `subagent.started` | | Sub-Agent | `toolCallId`, `agentName`, `agentDisplayName` | -| `subagent.completed` | | Sub-Agent | `toolCallId`, `agentName`, `agentDisplayName` | -| `subagent.failed` | | Sub-Agent | `toolCallId`, `agentName`, `error` | +| `subagent.started` | | Sub-Agent | `toolCallId`, `agentName`, `agentDisplayName`, `model?` | +| `subagent.completed` | | Sub-Agent | `toolCallId`, `agentName`, `agentDisplayName`, `model?`, `durationMs?`, `totalTokens?`, `totalToolCalls?` | +| `subagent.failed` | | Sub-Agent | `toolCallId`, `agentName`, `error`, `model?`, `durationMs?`, `totalTokens?`, `totalToolCalls?` | | `subagent.selected` | | Sub-Agent | `agentName`, `agentDisplayName`, `tools` | | `subagent.deselected` | | Sub-Agent | *(empty)* | | `skill.invoked` | | Skill | `name`, `path`, `content`, `allowedTools?` | | `abort` | | Control | `reason` | | `user.message` | | User | `content`, `attachments?`, `agentMode?` | | `system.message` | | System | `content`, `role` | -| `external_tool.requested` | ✅ | External Tool | `requestId`, `toolName`, `arguments?` | -| `external_tool.completed` | ✅ | External Tool | `requestId` | +| `external_tool.requested` | | External Tool | `requestId`, `toolName`, `arguments?` | +| `external_tool.completed` | | External Tool | `requestId` | | `command.queued` | ✅ | Command | `requestId`, `command` | | `command.completed` | ✅ | Command | `requestId` | | `session_limits_exhausted.requested` | ✅ | Session | `requestId`, `maxAiCredits`, `usedAiCredits` | diff --git a/content/copilot/how-tos/copilot-sdk/features/usage-and-billing.md b/content/copilot/how-tos/copilot-sdk/features/usage-and-billing.md new file mode 100644 index 000000000000..e704a2fdfd5e --- /dev/null +++ b/content/copilot/how-tos/copilot-sdk/features/usage-and-billing.md @@ -0,0 +1,1271 @@ +--- +title: Usage and billing metrics +shortTitle: Usage and billing +intro: >- + This guide shows how to read token counts, context-window utilization, AI + credit cost, and account quota from a Copilot SDK application. Examples are + shown for TypeScript, Python, Go, .NET, Java, and Rust. +versions: + fpt: '*' + ghec: '*' +contentType: how-tos +--- + + + + +> [!TIP] +> Each example is functionally equivalent across languages. The TypeScript snippet is expanded by default; select your language from the collapsible blocks to see the same logic in that SDK. + +## Overview + +The SDK surfaces usage data through two complementary mechanisms: + +* **Session events**: ephemeral events the runtime emits as a turn runs. Subscribe to these for real-time, per-API-call data. +* **RPC methods**: request/response calls you make on demand. Use these to snapshot accumulated totals or look up account-level quota. + +The table below maps each signal to the API that exposes it. + +| Signal | API | Scope | Type | +|---|---|---|---| +| Per-call token counts | `assistant.usage` event | Session | Event | +| Context-window utilization | `session.usage_info` event | Session | Event | +| Context-window breakdown (on demand) | `session.metadata.contextInfo` | Session | RPC | +| Accumulated AI credit and token totals | `session.usage.getMetrics` | Session | RPC | +| Per-model AI credit pricing | `models.list` | Server | RPC | +| Account quota and premium interactions | `account.getQuota` | Server | RPC | + +> [!NOTE] +> `session.usage.getMetrics`, `session.metadata.contextInfo`, and `session.metadata.recomputeContextTokens` are marked experimental in the generated RPC surface. In .NET they raise the `GHCP001` experimental diagnostic, which you suppress with `#pragma warning disable GHCP001` or a project-level `GHCP001`. Pin both the SDK and the Copilot CLI runtime if your application depends on them. + +The field tables below list only the fields used in the examples on this page. The complete, always-current field reference is the generated SDK types plus [AUTOTITLE](/copilot/how-tos/copilot-sdk/features/streaming-events), which is regenerated from the CLI schema on every dependency bump. Treat those as the source of truth and this page as a task-oriented guide. + +## Per-call token counts + +The `assistant.usage` event is emitted once for every model API call in a turn (including calls made by sub-agents). It carries the token counts and the billing multiplier for that single call. + +The example below uses these fields. See [AUTOTITLE](/copilot/how-tos/copilot-sdk/features/streaming-events#assistantusage) for the full list, including cache, reasoning, latency, and tracing fields. + +| Field | Type | Description | +|---|---|---| +| `model` | `string` | Model identifier for this call | +| `inputTokens` | `number` | Input tokens consumed | +| `outputTokens` | `number` | Output tokens produced | +| `cost` | `number` | Premium request multiplier applied to this call | + +> [!TIP] +> `assistant.usage` is ephemeral, so it is delivered live but not replayed when you resume a session. To read accumulated totals after the fact, call `session.usage.getMetrics` (see [Accumulated AI credit and token totals](#accumulated-ai-credit-and-token-totals)). + +{% codetabs %} +{% codetab typescript %} + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +const client = new CopilotClient(); +const session = await client.createSession({ streaming: true }); + +session.on("assistant.usage", (event) => { + const { model, inputTokens, outputTokens, cost } = event.data; + console.log( + `${model}: in=${inputTokens ?? 0} out=${outputTokens ?? 0} cost=${cost ?? 0}`, + ); +}); +``` + +```typescript +session.on("assistant.usage", (event) => { + const { model, inputTokens, outputTokens, cost } = event.data; + console.log( + `${model}: in=${inputTokens ?? 0} out=${outputTokens ?? 0} cost=${cost ?? 0}`, + ); +}); +``` + +{% endcodetab %} +{% codetab python %} + +```python +from copilot import CopilotClient +from copilot.session_events import SessionEventType + +client = CopilotClient() +session = await client.create_session(streaming=True) + +def on_usage(event): + if event.type == SessionEventType.ASSISTANT_USAGE: + data = event.data + print(f"{data.model}: in={data.input_tokens or 0} out={data.output_tokens or 0} cost={data.cost or 0}") + +session.on(on_usage) +``` + +```python +def on_usage(event): + if event.type == SessionEventType.ASSISTANT_USAGE: + data = event.data + print(f"{data.model}: in={data.input_tokens or 0} out={data.output_tokens or 0} cost={data.cost or 0}") + +session.on(on_usage) +``` + +{% endcodetab %} +{% codetab go %} + +```golang +package main + +import ( + "context" + "fmt" + + copilot "github.com/github/copilot-sdk/go" + "github.com/github/copilot-sdk/go/rpc" +) + +func main() { + ctx := context.Background() + client := copilot.NewClient(nil) + client.Start(ctx) + + session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ + Streaming: copilot.Bool(true), + OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) { + return &rpc.PermissionDecisionApproveOnce{}, nil + }, + }) + + session.On(func(event copilot.SessionEvent) { + d, ok := event.Data.(*copilot.AssistantUsageData) + if !ok { + return + } + in, out, cost := int64(0), int64(0), float64(0) + if d.InputTokens != nil { + in = *d.InputTokens + } + if d.OutputTokens != nil { + out = *d.OutputTokens + } + if d.Cost != nil { + cost = *d.Cost + } + fmt.Printf("%s: in=%d out=%d cost=%g\n", d.Model, in, out, cost) + }) + _ = session +} +``` + +```golang +session.On(func(event copilot.SessionEvent) { + d, ok := event.Data.(*copilot.AssistantUsageData) + if !ok { + return + } + in, out, cost := int64(0), int64(0), float64(0) + if d.InputTokens != nil { + in = *d.InputTokens + } + if d.OutputTokens != nil { + out = *d.OutputTokens + } + if d.Cost != nil { + cost = *d.Cost + } + fmt.Printf("%s: in=%d out=%d cost=%g\n", d.Model, in, out, cost) +}) +``` + +{% endcodetab %} +{% codetab dotnet %} + +```csharp +using GitHub.Copilot; + +await using var client = new CopilotClient(); +await using var session = await client.CreateSessionAsync(new SessionConfig { Streaming = true }); + +session.On(evt => +{ + var data = evt.Data; + Console.WriteLine( + $"{data.Model}: in={data.InputTokens ?? 0} out={data.OutputTokens ?? 0} cost={data.Cost ?? 0}"); +}); +``` + +```csharp +session.On(evt => +{ + var data = evt.Data; + Console.WriteLine( + $"{data.Model}: in={data.InputTokens ?? 0} out={data.OutputTokens ?? 0} cost={data.Cost ?? 0}"); +}); +``` + +{% endcodetab %} +{% codetab java %} + + + +```java +session.on(AssistantUsageEvent.class, event -> { + var data = event.getData(); + long in = data.inputTokens() != null ? data.inputTokens() : 0; + long out = data.outputTokens() != null ? data.outputTokens() : 0; + double cost = data.cost() != null ? data.cost() : 0.0; + System.out.printf("%s: in=%d out=%d cost=%s%n", data.model(), in, out, cost); +}); +``` + +{% endcodetab %} +{% codetab rust %} + +```rust +use github_copilot_sdk::session_events::AssistantUsageData; + +let mut events = session.subscribe(); +while let Ok(event) = events.recv().await { + if event.event_type == "assistant.usage" { + if let Some(data) = event.typed_data::() { + println!( + "{}: in={} out={} cost={}", + data.model, + data.input_tokens.unwrap_or(0), + data.output_tokens.unwrap_or(0), + data.cost.unwrap_or(0.0), + ); + } + } +} +``` + +{% endcodetab %} +{% endcodetabs %} + +## Context-window utilization + +Token counts tell you what each call consumed. Context-window utilization tells you how full the model's prompt window is right now—useful for showing a progress bar or warning the user before automatic compaction kicks in. + +### Live updates with `session.usage_info` + +The runtime emits a `session.usage_info` event whenever the context-window size changes. The example uses `currentTokens` and `tokenLimit`; see [AUTOTITLE](/copilot/how-tos/copilot-sdk/features/streaming-events#sessionusage_info) for the complete payload. + +| Field | Type | Description | +|---|---|---| +| `currentTokens` | `number` | Tokens currently in the context window | +| `tokenLimit` | `number` | Maximum tokens for the model's context window | + +{% codetabs %} +{% codetab typescript %} + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +const client = new CopilotClient(); +const session = await client.createSession({ streaming: true }); + +session.on("session.usage_info", (event) => { + const { currentTokens, tokenLimit } = event.data; + const pct = Math.round((currentTokens / tokenLimit) * 100); + console.log(`Context: ${currentTokens}/${tokenLimit} (${pct}%)`); +}); +``` + +```typescript +session.on("session.usage_info", (event) => { + const { currentTokens, tokenLimit } = event.data; + const pct = Math.round((currentTokens / tokenLimit) * 100); + console.log(`Context: ${currentTokens}/${tokenLimit} (${pct}%)`); +}); +``` + +{% endcodetab %} +{% codetab python %} + +```python +from copilot import CopilotClient +from copilot.session_events import SessionEventType + +client = CopilotClient() +session = await client.create_session(streaming=True) + +def on_usage_info(event): + if event.type == SessionEventType.SESSION_USAGE_INFO: + data = event.data + pct = round(data.current_tokens / data.token_limit * 100) + print(f"Context: {data.current_tokens}/{data.token_limit} ({pct}%)") + +session.on(on_usage_info) +``` + +```python +def on_usage_info(event): + if event.type == SessionEventType.SESSION_USAGE_INFO: + data = event.data + pct = round(data.current_tokens / data.token_limit * 100) + print(f"Context: {data.current_tokens}/{data.token_limit} ({pct}%)") + +session.on(on_usage_info) +``` + +{% endcodetab %} +{% codetab go %} + +```golang +package main + +import ( + "context" + "fmt" + + copilot "github.com/github/copilot-sdk/go" + "github.com/github/copilot-sdk/go/rpc" +) + +func main() { + ctx := context.Background() + client := copilot.NewClient(nil) + client.Start(ctx) + + session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ + Streaming: copilot.Bool(true), + OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) { + return &rpc.PermissionDecisionApproveOnce{}, nil + }, + }) + + session.On(func(event copilot.SessionEvent) { + d, ok := event.Data.(*copilot.SessionUsageInfoData) + if !ok { + return + } + pct := int(float64(d.CurrentTokens) / float64(d.TokenLimit) * 100) + fmt.Printf("Context: %d/%d (%d%%)\n", d.CurrentTokens, d.TokenLimit, pct) + }) + _ = session +} +``` + +```golang +session.On(func(event copilot.SessionEvent) { + d, ok := event.Data.(*copilot.SessionUsageInfoData) + if !ok { + return + } + pct := int(float64(d.CurrentTokens) / float64(d.TokenLimit) * 100) + fmt.Printf("Context: %d/%d (%d%%)\n", d.CurrentTokens, d.TokenLimit, pct) +}) +``` + +{% endcodetab %} +{% codetab dotnet %} + +```csharp +using GitHub.Copilot; + +await using var client = new CopilotClient(); +await using var session = await client.CreateSessionAsync(new SessionConfig { Streaming = true }); + +session.On(evt => +{ + var pct = (int)Math.Round((double)evt.Data.CurrentTokens / evt.Data.TokenLimit * 100); + Console.WriteLine($"Context: {evt.Data.CurrentTokens}/{evt.Data.TokenLimit} ({pct}%)"); +}); +``` + +```csharp +session.On(evt => +{ + var pct = (int)Math.Round((double)evt.Data.CurrentTokens / evt.Data.TokenLimit * 100); + Console.WriteLine($"Context: {evt.Data.CurrentTokens}/{evt.Data.TokenLimit} ({pct}%)"); +}); +``` + +{% endcodetab %} +{% codetab java %} + + + +```java +session.on(SessionUsageInfoEvent.class, event -> { + var data = event.getData(); + long pct = Math.round((double) data.currentTokens() / data.tokenLimit() * 100); + System.out.printf("Context: %d/%d (%d%%)%n", data.currentTokens(), data.tokenLimit(), pct); +}); +``` + +{% endcodetab %} +{% codetab rust %} + +```rust +use github_copilot_sdk::session_events::SessionUsageInfoData; + +let mut events = session.subscribe(); +while let Ok(event) = events.recv().await { + if event.event_type == "session.usage_info" { + if let Some(data) = event.typed_data::() { + let pct = (data.current_tokens as f64 / data.token_limit as f64 * 100.0) as i64; + println!("Context: {}/{} ({}%)", data.current_tokens, data.token_limit, pct); + } + } +} +``` + +{% endcodetab %} +{% endcodetabs %} + +### On-demand breakdown with `session.metadata.contextInfo` + +Events only fire when the context changes. To read the current breakdown at any moment—for example, right after resuming a session—call `session.metadata.contextInfo`. Pass `0` for `promptTokenLimit` to use the runtime default; pass `0` for `outputTokenLimit` if the value is unknown. + +The result's `contextInfo` is `null` until the session has been initialized (the system prompt and tool metadata have been cached). It breaks the total down into `systemTokens`, `conversationTokens`, and `toolDefinitionsTokens`, alongside the `promptTokenLimit`. + +{% codetabs %} +{% codetab typescript %} + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +const client = new CopilotClient(); +const session = await client.createSession({}); + +const { contextInfo } = await session.rpc.metadata.contextInfo({ + promptTokenLimit: 0, + outputTokenLimit: 0, +}); + +if (contextInfo) { + console.log( + `Total ${contextInfo.totalTokens}/${contextInfo.promptTokenLimit} ` + + `(system=${contextInfo.systemTokens}, conversation=${contextInfo.conversationTokens})`, + ); +} +``` + +```typescript +const { contextInfo } = await session.rpc.metadata.contextInfo({ + promptTokenLimit: 0, + outputTokenLimit: 0, +}); + +if (contextInfo) { + console.log( + `Total ${contextInfo.totalTokens}/${contextInfo.promptTokenLimit} ` + + `(system=${contextInfo.systemTokens}, conversation=${contextInfo.conversationTokens})`, + ); +} +``` + +{% endcodetab %} +{% codetab python %} + +```python +from copilot import CopilotClient +from copilot.rpc import MetadataContextInfoRequest + +client = CopilotClient() +session = await client.create_session() + +result = await session.rpc.metadata.context_info( + MetadataContextInfoRequest(prompt_token_limit=0, output_token_limit=0) +) +info = result.context_info + +if info is not None: + print( + f"Total {info.total_tokens}/{info.prompt_token_limit} " + f"(system={info.system_tokens}, conversation={info.conversation_tokens})" + ) +``` + +```python +result = await session.rpc.metadata.context_info( + MetadataContextInfoRequest(prompt_token_limit=0, output_token_limit=0) +) +info = result.context_info + +if info is not None: + print( + f"Total {info.total_tokens}/{info.prompt_token_limit} " + f"(system={info.system_tokens}, conversation={info.conversation_tokens})" + ) +``` + +{% endcodetab %} +{% codetab go %} + +```golang +package main + +import ( + "context" + "fmt" + + copilot "github.com/github/copilot-sdk/go" + "github.com/github/copilot-sdk/go/rpc" +) + +func main() { + ctx := context.Background() + client := copilot.NewClient(nil) + client.Start(ctx) + + session, _ := client.CreateSession(ctx, &copilot.SessionConfig{}) + + result, _ := session.RPC.Metadata.ContextInfo(ctx, &rpc.MetadataContextInfoRequest{ + PromptTokenLimit: 0, + OutputTokenLimit: 0, + }) + + if info := result.ContextInfo; info != nil { + fmt.Printf("Total %d/%d (system=%d, conversation=%d)\n", + info.TotalTokens, info.PromptTokenLimit, info.SystemTokens, info.ConversationTokens) + } +} +``` + +```golang +result, _ := session.RPC.Metadata.ContextInfo(ctx, &rpc.MetadataContextInfoRequest{ + PromptTokenLimit: 0, + OutputTokenLimit: 0, +}) + +if info := result.ContextInfo; info != nil { + fmt.Printf("Total %d/%d (system=%d, conversation=%d)\n", + info.TotalTokens, info.PromptTokenLimit, info.SystemTokens, info.ConversationTokens) +} +``` + +{% endcodetab %} +{% codetab dotnet %} + +```csharp +#pragma warning disable GHCP001 +using GitHub.Copilot; + +await using var client = new CopilotClient(); +await using var session = await client.CreateSessionAsync(new SessionConfig()); + +var result = await session.Rpc.Metadata.ContextInfoAsync(promptTokenLimit: 0, outputTokenLimit: 0); +var info = result.ContextInfo; + +if (info is not null) +{ + Console.WriteLine( + $"Total {info.TotalTokens}/{info.PromptTokenLimit} " + + $"(system={info.SystemTokens}, conversation={info.ConversationTokens})"); +} +#pragma warning restore GHCP001 +``` + +```csharp +var result = await session.Rpc.Metadata.ContextInfoAsync(promptTokenLimit: 0, outputTokenLimit: 0); +var info = result.ContextInfo; + +if (info is not null) +{ + Console.WriteLine( + $"Total {info.TotalTokens}/{info.PromptTokenLimit} " + + $"(system={info.SystemTokens}, conversation={info.ConversationTokens})"); +} +``` + +{% endcodetab %} +{% codetab java %} + + + +```java +var result = session.getRpc().metadata + .contextInfo(new SessionMetadataContextInfoParams(null, 0L, 0L, null)) + .join(); +var info = result.contextInfo(); + +if (info != null) { + System.out.printf("Total %d/%d (system=%d, conversation=%d)%n", + info.totalTokens(), info.promptTokenLimit(), info.systemTokens(), info.conversationTokens()); +} +``` + +{% endcodetab %} +{% codetab rust %} + +```rust +use github_copilot_sdk::rpc::MetadataContextInfoRequest; + +let result = session + .rpc() + .metadata() + .context_info(MetadataContextInfoRequest { + prompt_token_limit: 0, + output_token_limit: 0, + selected_model: None, + }) + .await?; + +if let Some(info) = result.context_info { + println!( + "Total {}/{} (system={}, conversation={})", + info.total_tokens, info.prompt_token_limit, info.system_tokens, info.conversation_tokens, + ); +} +``` + +{% endcodetab %} +{% endcodetabs %} + +## Accumulated AI credit and token totals + +`session.usage.getMetrics` returns the running totals for the whole session in a single call. This is the cleanest way to read AI credit cost, because it aggregates every API call (main agent and sub-agents) for you. + +The example uses the fields below. The generated `UsageGetMetricsResult` type is the full reference. + +| Field | Type | Description | +|---|---|---| +| `totalNanoAiu` | `number` | Session-wide AI credit cost, in nano-AI units | +| `totalPremiumRequestCost` | `number` | Premium request cost across all models, after multipliers | +| `modelMetrics` | `Record` | Per-model breakdown; each entry has `usage.inputTokens`, `usage.outputTokens`, and `totalNanoAiu` | + +> [!NOTE] +> Cost is reported in **nano-AI units** (the field is named `totalNanoAiu`). The exact conversion to AI credits and the precise meaning of premium request accounting are defined by GitHub Copilot billing, not by the SDK—treat [GitHub's Copilot billing documentation](/copilot/managing-copilot/understanding-and-managing-copilot-usage) as the source of truth and verify before surfacing currency-like values to users. The examples divide by `1e9` as a convenience, following the SI `nano` prefix; confirm this matches current billing before relying on it. The `modelMetrics` and `tokenDetails` maps are keyed by runtime strings (model IDs and token-type names) that the SDK type system does not validate. + +{% codetabs %} +{% codetab typescript %} + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +const client = new CopilotClient(); +const session = await client.createSession({}); + +const metrics = await session.rpc.usage.getMetrics(); + +const aiCredits = (metrics.totalNanoAiu ?? 0) / 1e9; +console.log(`AI credits used: ${aiCredits.toFixed(6)}`); +console.log(`Premium requests: ${metrics.totalPremiumRequestCost}`); + +for (const [model, m] of Object.entries(metrics.modelMetrics)) { + if (!m) continue; + console.log( + `${model}: in=${m.usage.inputTokens} out=${m.usage.outputTokens} ` + + `nanoAiu=${m.totalNanoAiu ?? 0}`, + ); +} +``` + +```typescript +const metrics = await session.rpc.usage.getMetrics(); + +const aiCredits = (metrics.totalNanoAiu ?? 0) / 1e9; +console.log(`AI credits used: ${aiCredits.toFixed(6)}`); +console.log(`Premium requests: ${metrics.totalPremiumRequestCost}`); + +for (const [model, m] of Object.entries(metrics.modelMetrics)) { + if (!m) continue; + console.log( + `${model}: in=${m.usage.inputTokens} out=${m.usage.outputTokens} ` + + `nanoAiu=${m.totalNanoAiu ?? 0}`, + ); +} +``` + +{% endcodetab %} +{% codetab python %} + +```python +from copilot import CopilotClient + +client = CopilotClient() +session = await client.create_session() + +metrics = await session.rpc.usage.get_metrics() + +ai_credits = (metrics.total_nano_aiu or 0) / 1e9 +print(f"AI credits used: {ai_credits:.6f}") +print(f"Premium requests: {metrics.total_premium_request_cost}") + +for model, m in metrics.model_metrics.items(): + print(f"{model}: in={m.usage.input_tokens} out={m.usage.output_tokens} nanoAiu={m.total_nano_aiu or 0}") +``` + +```python +metrics = await session.rpc.usage.get_metrics() + +ai_credits = (metrics.total_nano_aiu or 0) / 1e9 +print(f"AI credits used: {ai_credits:.6f}") +print(f"Premium requests: {metrics.total_premium_request_cost}") + +for model, m in metrics.model_metrics.items(): + print(f"{model}: in={m.usage.input_tokens} out={m.usage.output_tokens} nanoAiu={m.total_nano_aiu or 0}") +``` + +{% endcodetab %} +{% codetab go %} + +```golang +package main + +import ( + "context" + "fmt" + + copilot "github.com/github/copilot-sdk/go" +) + +func main() { + ctx := context.Background() + client := copilot.NewClient(nil) + client.Start(ctx) + + session, _ := client.CreateSession(ctx, &copilot.SessionConfig{}) + + metrics, _ := session.RPC.Usage.GetMetrics(ctx) + + aiCredits := float64(0) + if metrics.TotalNanoAiu != nil { + aiCredits = *metrics.TotalNanoAiu / 1e9 + } + fmt.Printf("AI credits used: %.6f\n", aiCredits) + fmt.Printf("Premium requests: %v\n", metrics.TotalPremiumRequestCost) + + for model, m := range metrics.ModelMetrics { + nanoAiu := float64(0) + if m.TotalNanoAiu != nil { + nanoAiu = *m.TotalNanoAiu + } + fmt.Printf("%s: in=%d out=%d nanoAiu=%v\n", model, m.Usage.InputTokens, m.Usage.OutputTokens, nanoAiu) + } +} +``` + +```golang +metrics, _ := session.RPC.Usage.GetMetrics(ctx) + +aiCredits := float64(0) +if metrics.TotalNanoAiu != nil { + aiCredits = *metrics.TotalNanoAiu / 1e9 +} +fmt.Printf("AI credits used: %.6f\n", aiCredits) +fmt.Printf("Premium requests: %v\n", metrics.TotalPremiumRequestCost) + +for model, m := range metrics.ModelMetrics { + nanoAiu := float64(0) + if m.TotalNanoAiu != nil { + nanoAiu = *m.TotalNanoAiu + } + fmt.Printf("%s: in=%d out=%d nanoAiu=%v\n", model, m.Usage.InputTokens, m.Usage.OutputTokens, nanoAiu) +} +``` + +{% endcodetab %} +{% codetab dotnet %} + +```csharp +#pragma warning disable GHCP001 +using GitHub.Copilot; + +await using var client = new CopilotClient(); +await using var session = await client.CreateSessionAsync(new SessionConfig()); + +var metrics = await session.Rpc.Usage.GetMetricsAsync(); + +var aiCredits = (metrics.TotalNanoAiu ?? 0) / 1e9; +Console.WriteLine($"AI credits used: {aiCredits:F6}"); +Console.WriteLine($"Premium requests: {metrics.TotalPremiumRequestCost}"); + +foreach (var (model, m) in metrics.ModelMetrics) +{ + Console.WriteLine( + $"{model}: in={m.Usage.InputTokens} out={m.Usage.OutputTokens} nanoAiu={m.TotalNanoAiu ?? 0}"); +} +#pragma warning restore GHCP001 +``` + +```csharp +var metrics = await session.Rpc.Usage.GetMetricsAsync(); + +var aiCredits = (metrics.TotalNanoAiu ?? 0) / 1e9; +Console.WriteLine($"AI credits used: {aiCredits:F6}"); +Console.WriteLine($"Premium requests: {metrics.TotalPremiumRequestCost}"); + +foreach (var (model, m) in metrics.ModelMetrics) +{ + Console.WriteLine( + $"{model}: in={m.Usage.InputTokens} out={m.Usage.OutputTokens} nanoAiu={m.TotalNanoAiu ?? 0}"); +} +``` + +{% endcodetab %} +{% codetab java %} + + + +```java +var metrics = session.getRpc().usage.getMetrics().join(); + +double aiCredits = metrics.totalNanoAiu() != null ? metrics.totalNanoAiu() / 1e9 : 0; +System.out.printf("AI credits used: %.6f%n", aiCredits); +System.out.printf("Premium requests: %s%n", metrics.totalPremiumRequestCost()); + +metrics.modelMetrics().forEach((model, m) -> { + double nanoAiu = m.totalNanoAiu() != null ? m.totalNanoAiu() : 0; + System.out.printf("%s: in=%d out=%d nanoAiu=%s%n", + model, m.usage().inputTokens(), m.usage().outputTokens(), nanoAiu); +}); +``` + +{% endcodetab %} +{% codetab rust %} + +```rust +let metrics = session.rpc().usage().get_metrics().await?; + +let ai_credits = metrics.total_nano_aiu.unwrap_or(0.0) / 1e9; +println!("AI credits used: {ai_credits:.6}"); +println!("Premium requests: {}", metrics.total_premium_request_cost); + +for (model, m) in &metrics.model_metrics { + let nano_aiu = m.total_nano_aiu.unwrap_or(0.0); + println!( + "{model}: in={} out={} nanoAiu={nano_aiu}", + m.usage.input_tokens, m.usage.output_tokens, + ); +} +``` + +{% endcodetab %} +{% endcodetabs %} + +## Per-model AI credit pricing + +To estimate cost before you run a turn, read each model's token prices from `models.list`. This is a server-scoped call on the client, so it does not need a session. Prices are expressed in AI credits per billing batch of tokens. The generated `ModelBillingTokenPrices` type lists every field, including `cachePrice`. + +| Field | Type | Description | +|---|---|---| +| `billing.multiplier` | `number` | Premium request cost multiplier relative to the base rate | +| `billing.tokenPrices.inputPrice` | `number` | AI credit cost per batch of input tokens | +| `billing.tokenPrices.outputPrice` | `number` | AI credit cost per batch of output tokens | +| `billing.tokenPrices.batchSize` | `number` | Number of tokens per billing batch | + +> [!NOTE] +> Price values change as plans and models evolve. Read them at runtime as shown below; never hard-code the numbers into your application. + +{% codetabs %} +{% codetab typescript %} + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +const client = new CopilotClient(); + +const { models } = await client.rpc.models.list({}); + +for (const model of models) { + const prices = model.billing?.tokenPrices; + if (!prices) continue; + console.log( + `${model.id}: input=${prices.inputPrice} output=${prices.outputPrice} ` + + `per ${prices.batchSize} tokens (x${model.billing?.multiplier ?? 1})`, + ); +} +``` + +```typescript +const { models } = await client.rpc.models.list({}); + +for (const model of models) { + const prices = model.billing?.tokenPrices; + if (!prices) continue; + console.log( + `${model.id}: input=${prices.inputPrice} output=${prices.outputPrice} ` + + `per ${prices.batchSize} tokens (x${model.billing?.multiplier ?? 1})`, + ); +} +``` + +{% endcodetab %} +{% codetab python %} + +```python +from copilot import CopilotClient +from copilot.rpc import ModelsListRequest + +client = CopilotClient() + +result = await client.rpc.models.list(ModelsListRequest()) + +for model in result.models: + prices = model.billing.token_prices if model.billing else None + if prices is None: + continue + multiplier = model.billing.multiplier if model.billing else 1 + print( + f"{model.id}: input={prices.input_price} output={prices.output_price} " + f"per {prices.batch_size} tokens (x{multiplier})" + ) +``` + +```python +result = await client.rpc.models.list(ModelsListRequest()) + +for model in result.models: + prices = model.billing.token_prices if model.billing else None + if prices is None: + continue + multiplier = model.billing.multiplier if model.billing else 1 + print( + f"{model.id}: input={prices.input_price} output={prices.output_price} " + f"per {prices.batch_size} tokens (x{multiplier})" + ) +``` + +{% endcodetab %} +{% codetab go %} + +```golang +package main + +import ( + "context" + "fmt" + + copilot "github.com/github/copilot-sdk/go" + "github.com/github/copilot-sdk/go/rpc" +) + +func main() { + ctx := context.Background() + client := copilot.NewClient(nil) + client.Start(ctx) + + list, _ := client.RPC.Models.List(ctx, &rpc.ModelsListRequest{}) + + for _, model := range list.Models { + if model.Billing == nil || model.Billing.TokenPrices == nil { + continue + } + prices := model.Billing.TokenPrices + multiplier := 1.0 + if model.Billing.Multiplier != nil { + multiplier = *model.Billing.Multiplier + } + in, out := 0.0, 0.0 + if prices.InputPrice != nil { + in = *prices.InputPrice + } + if prices.OutputPrice != nil { + out = *prices.OutputPrice + } + batch := int64(0) + if prices.BatchSize != nil { + batch = *prices.BatchSize + } + fmt.Printf("%s: input=%v output=%v per %d tokens (x%v)\n", model.ID, in, out, batch, multiplier) + } +} +``` + +```golang +list, _ := client.RPC.Models.List(ctx, &rpc.ModelsListRequest{}) + +for _, model := range list.Models { + if model.Billing == nil || model.Billing.TokenPrices == nil { + continue + } + prices := model.Billing.TokenPrices + multiplier := 1.0 + if model.Billing.Multiplier != nil { + multiplier = *model.Billing.Multiplier + } + in, out := 0.0, 0.0 + if prices.InputPrice != nil { + in = *prices.InputPrice + } + if prices.OutputPrice != nil { + out = *prices.OutputPrice + } + batch := int64(0) + if prices.BatchSize != nil { + batch = *prices.BatchSize + } + fmt.Printf("%s: input=%v output=%v per %d tokens (x%v)\n", model.ID, in, out, batch, multiplier) +} +``` + +{% endcodetab %} +{% codetab dotnet %} + +```csharp +using GitHub.Copilot; + +await using var client = new CopilotClient(); + +var list = await client.Rpc.Models.ListAsync(); + +foreach (var model in list.Models) +{ + var prices = model.Billing?.TokenPrices; + if (prices is null) continue; + Console.WriteLine( + $"{model.Id}: input={prices.InputPrice} output={prices.OutputPrice} " + + $"per {prices.BatchSize} tokens (x{model.Billing?.Multiplier ?? 1})"); +} +``` + +```csharp +var list = await client.Rpc.Models.ListAsync(); + +foreach (var model in list.Models) +{ + var prices = model.Billing?.TokenPrices; + if (prices is null) continue; + Console.WriteLine( + $"{model.Id}: input={prices.InputPrice} output={prices.OutputPrice} " + + $"per {prices.BatchSize} tokens (x{model.Billing?.Multiplier ?? 1})"); +} +``` + +{% endcodetab %} +{% codetab java %} + + + +```java +var list = client.getRpc().models.list().join(); + +for (var model : list.models()) { + var billing = model.billing(); + if (billing == null || billing.tokenPrices() == null) { + continue; + } + var prices = billing.tokenPrices(); + double multiplier = billing.multiplier() != null ? billing.multiplier() : 1; + System.out.printf("%s: input=%s output=%s per %d tokens (x%s)%n", + model.id(), prices.inputPrice(), prices.outputPrice(), prices.batchSize(), multiplier); +} +``` + +{% endcodetab %} +{% codetab rust %} + +```rust +let list = client.rpc().models().list().await?; + +for model in &list.models { + let Some(billing) = &model.billing else { continue }; + let Some(prices) = &billing.token_prices else { continue }; + let multiplier = billing.multiplier.unwrap_or(1.0); + println!( + "{}: input={} output={} per {} tokens (x{multiplier})", + model.id, + prices.input_price.unwrap_or(0.0), + prices.output_price.unwrap_or(0.0), + prices.batch_size.unwrap_or(0), + ); +} +``` + +{% endcodetab %} +{% endcodetabs %} + +## Account quota and premium interactions + +`account.getQuota` reports the authenticated user's remaining Copilot entitlement. The result's `quotaSnapshots` map is keyed by quota type—commonly `premium_interactions`, `chat`, and `completions`. Use it to show users how much of their monthly allowance is left, or to gate work before they hit a limit. + +The example uses the fields below; the generated `AccountQuotaSnapshot` type is the full reference. The `quotaSnapshots` keys are runtime strings that the SDK type system does not validate, so guard your lookups. + +| Field | Type | Description | +|---|---|---| +| `entitlementRequests` | `number` | Requests included in the entitlement, or `-1` for unlimited | +| `usedRequests` | `number` | Requests used so far this period | +| `remainingPercentage` | `number` | Percentage of the entitlement remaining | +| `resetDate` | `string` | ISO 8601 date when the quota resets | + +> [!TIP] +> To read quota for a specific user rather than the connection's global auth context (for example, in a multi-tenant backend), pass that user's GitHub token to `getQuota`. See [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/multi-tenancy). + +{% codetabs %} +{% codetab typescript %} + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +const client = new CopilotClient(); + +const { quotaSnapshots } = await client.rpc.account.getQuota({}); +const premium = quotaSnapshots["premium_interactions"]; + +if (premium) { + console.log( + `Premium interactions: ${premium.usedRequests}/${premium.entitlementRequests} ` + + `(${premium.remainingPercentage.toFixed(1)}% left, resets ${premium.resetDate ?? "n/a"})`, + ); +} +``` + +```typescript +const { quotaSnapshots } = await client.rpc.account.getQuota({}); +const premium = quotaSnapshots["premium_interactions"]; + +if (premium) { + console.log( + `Premium interactions: ${premium.usedRequests}/${premium.entitlementRequests} ` + + `(${premium.remainingPercentage.toFixed(1)}% left, resets ${premium.resetDate ?? "n/a"})`, + ); +} +``` + +{% endcodetab %} +{% codetab python %} + +```python +from copilot import CopilotClient +from copilot.rpc import AccountGetQuotaRequest + +client = CopilotClient() + +result = await client.rpc.account.get_quota(AccountGetQuotaRequest()) +premium = result.quota_snapshots.get("premium_interactions") + +if premium is not None: + print( + f"Premium interactions: {premium.used_requests}/{premium.entitlement_requests} " + f"({premium.remaining_percentage:.1f}% left, resets {premium.reset_date or 'n/a'})" + ) +``` + +```python +result = await client.rpc.account.get_quota(AccountGetQuotaRequest()) +premium = result.quota_snapshots.get("premium_interactions") + +if premium is not None: + print( + f"Premium interactions: {premium.used_requests}/{premium.entitlement_requests} " + f"({premium.remaining_percentage:.1f}% left, resets {premium.reset_date or 'n/a'})" + ) +``` + +{% endcodetab %} +{% codetab go %} + +```golang +package main + +import ( + "context" + "fmt" + "time" + + copilot "github.com/github/copilot-sdk/go" + "github.com/github/copilot-sdk/go/rpc" +) + +func main() { + ctx := context.Background() + client := copilot.NewClient(nil) + client.Start(ctx) + + result, _ := client.RPC.Account.GetQuota(ctx, &rpc.AccountGetQuotaRequest{}) + + if premium, ok := result.QuotaSnapshots["premium_interactions"]; ok { + resets := "n/a" + if premium.ResetDate != nil { + resets = premium.ResetDate.Format(time.RFC3339) + } + fmt.Printf("Premium interactions: %d/%d (%.1f%% left, resets %s)\n", + premium.UsedRequests, premium.EntitlementRequests, premium.RemainingPercentage, resets) + } +} +``` + +```golang +result, _ := client.RPC.Account.GetQuota(ctx, &rpc.AccountGetQuotaRequest{}) + +if premium, ok := result.QuotaSnapshots["premium_interactions"]; ok { + resets := "n/a" + if premium.ResetDate != nil { + resets = premium.ResetDate.Format(time.RFC3339) + } + fmt.Printf("Premium interactions: %d/%d (%.1f%% left, resets %s)\n", + premium.UsedRequests, premium.EntitlementRequests, premium.RemainingPercentage, resets) +} +``` + +{% endcodetab %} +{% codetab dotnet %} + +```csharp +using GitHub.Copilot; + +await using var client = new CopilotClient(); + +var result = await client.Rpc.Account.GetQuotaAsync(); + +if (result.QuotaSnapshots.TryGetValue("premium_interactions", out var premium)) +{ + Console.WriteLine( + $"Premium interactions: {premium.UsedRequests}/{premium.EntitlementRequests} " + + $"({premium.RemainingPercentage:F1}% left, resets {premium.ResetDate?.ToString("o") ?? "n/a"})"); +} +``` + +```csharp +var result = await client.Rpc.Account.GetQuotaAsync(); + +if (result.QuotaSnapshots.TryGetValue("premium_interactions", out var premium)) +{ + Console.WriteLine( + $"Premium interactions: {premium.UsedRequests}/{premium.EntitlementRequests} " + + $"({premium.RemainingPercentage:F1}% left, resets {premium.ResetDate?.ToString("o") ?? "n/a"})"); +} +``` + +{% endcodetab %} +{% codetab java %} + + + +```java +var result = client.getRpc().account.getQuota().join(); +var premium = result.quotaSnapshots().get("premium_interactions"); + +if (premium != null) { + System.out.printf("Premium interactions: %d/%d (%.1f%% left, resets %s)%n", + premium.usedRequests(), premium.entitlementRequests(), + premium.remainingPercentage(), premium.resetDate()); +} +``` + +{% endcodetab %} +{% codetab rust %} + +```rust +let result = client.rpc().account().get_quota().await?; + +if let Some(premium) = result.quota_snapshots.get("premium_interactions") { + let resets = premium.reset_date.as_deref().unwrap_or("n/a"); + println!( + "Premium interactions: {}/{} ({:.1}% left, resets {resets})", + premium.used_requests, premium.entitlement_requests, premium.remaining_percentage, + ); +} +``` + +{% endcodetab %} +{% endcodetabs %} + +## Choosing the right API + +Use this summary to decide which API fits your use case: + +* **Render a live cost or token meter as a turn runs**: subscribe to `assistant.usage` and `session.usage_info`. +* **Show a final cost summary after a turn or session**: call `session.usage.getMetrics`. +* **Display context-window usage on resume, before any new turn**: call `session.metadata.contextInfo`. +* **Estimate cost before running work**: read `models.list` token prices. +* **Warn users before they exhaust their plan**: call `account.getQuota`. + +## Further reading + +* [AUTOTITLE](/copilot/how-tos/copilot-sdk/features/streaming-events): full field-level reference for `assistant.usage`, `session.usage_info`, and every other session event +* [AUTOTITLE](/copilot/how-tos/copilot-sdk/observability): export usage data to OpenTelemetry for cost attribution +* [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/multi-tenancy): resolve per-user quota and models with a GitHub token diff --git a/content/copilot/how-tos/copilot-sdk/getting-started.md b/content/copilot/how-tos/copilot-sdk/getting-started.md index 666c71ee5114..228cd563d0d1 100644 --- a/content/copilot/how-tos/copilot-sdk/getting-started.md +++ b/content/copilot/how-tos/copilot-sdk/getting-started.md @@ -2004,6 +2004,7 @@ let mut options = ClientOptions::default(); options.transport = Transport::External { host: "localhost".to_string(), port: 4321, + connection_token: None, }; let client = Client::start(options).await?; @@ -2105,7 +2106,7 @@ Install with telemetry extras: `pip install copilot-sdk[telemetry]` (provides `o ```golang -client, err := copilot.NewClient(copilot.ClientOptions{ +client := copilot.NewClient(&copilot.ClientOptions{ Telemetry: &copilot.TelemetryConfig{ OTLPEndpoint: "http://localhost:4318", }, @@ -2209,7 +2210,7 @@ Trace context is propagated automatically—no manual instrumentation is needed: ## Learn more * [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/authenticate) - GitHub OAuth, environment variables, and BYOK -* [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) - Use your own API keys from Azure AI Foundry, OpenAI, etc. +* [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) - Use your own API keys from Microsoft Foundry, OpenAI, etc. * [Node.js SDK Reference](https://github.com/github/copilot-sdk/tree/main/nodejs/README.md) * [Python SDK Reference](https://github.com/github/copilot-sdk/tree/main/python/README.md) * [Go SDK Reference](https://github.com/github/copilot-sdk/tree/main/go/README.md) diff --git a/content/copilot/how-tos/copilot-sdk/hooks/hooks-overview.md b/content/copilot/how-tos/copilot-sdk/hooks/hooks-overview.md index 9ed8d728099e..074ce5d30e8c 100644 --- a/content/copilot/how-tos/copilot-sdk/hooks/hooks-overview.md +++ b/content/copilot/how-tos/copilot-sdk/hooks/hooks-overview.md @@ -29,9 +29,10 @@ contentType: how-tos | [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/post-tool-use) | After a tool executes (success only) | Result transformation, logging | | [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/post-tool-use#failure-variant) | After a tool execution whose result was a failure | Inject retry guidance, log failures | | [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/user-prompt-submitted) | When user sends a message | Prompt modification, filtering | -| [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/session-lifecycle#session-start-hook-session-start) | Session begins | Add context, configure session | -| [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/session-lifecycle#session-end-hook-session-end) | Session ends | Cleanup, analytics | +| [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/session-lifecycle#session-start) | Session begins | Add context, configure session | +| [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/session-lifecycle#session-end) | Session ends | Cleanup, analytics | | [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/error-handling) | Error happens | Custom error handling | +| [AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/session-lifecycle#agent-stop) | Top-level agent naturally stops | Validate completion or request another turn | ## Quick start @@ -269,6 +270,7 @@ const session = await client.createSession({ * **[AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/post-tool-use)** - Transform tool results * **[AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/user-prompt-submitted)** - Modify user prompts * **[AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/session-lifecycle)** - Session start and end +* **[AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/session-lifecycle#agent-stop)** - Validate completion before the agent stops * **[AUTOTITLE](/copilot/how-tos/copilot-sdk/hooks/error-handling)** - Custom error handling ## See also diff --git a/content/copilot/how-tos/copilot-sdk/hooks/session-lifecycle.md b/content/copilot/how-tos/copilot-sdk/hooks/session-lifecycle.md index 0c9b2eaf8463..586a900e6511 100644 --- a/content/copilot/how-tos/copilot-sdk/hooks/session-lifecycle.md +++ b/content/copilot/how-tos/copilot-sdk/hooks/session-lifecycle.md @@ -529,6 +529,42 @@ Session Summary: }); ``` +## Agent stop hook {#agent-stop} + +The agent stop hook runs when the top-level agent naturally reaches the end of a turn. It is separate from `onSessionEnd`: the session remains active, and the hook can request another agent turn. + +| Language | Handler | +|----------|---------| +| Node.js / TypeScript | `onAgentStop` | +| Python | `on_agent_stop` | +| Go | `OnAgentStop` | +| .NET | `OnAgentStop` | +| Rust | `on_agent_stop` | +| Java | `setOnAgentStop` | + +### Input + +The public member names follow each language's casing conventions: + +| Meaning | Node.js / Python | Go / .NET | Rust | Java | +|---------|------------------|-----------|------|------| +| Why the agent stopped, such as `end_turn` | `stopReason` | `StopReason` | `stop_reason` | `getStopReason()` | +| Path to the on-disk session transcript | `transcriptPath` | `TranscriptPath` | `transcript_path` | `getTranscriptPath()` | +| Whether an earlier block decision already forced this continuation | `stopHookActive` | `StopHookActive` | `stop_hook_active` | `getStopHookActive()` | + +### Output + +Return no output to let the agent stop. Return a block decision to enqueue another user message and continue: + +```json +{ + "decision": "block", + "reason": "Run the final validation and fix any failures." +} +``` + +Use the active-stop member listed above to avoid repeatedly blocking an agent that has already continued because of this hook. The runtime also caps consecutive block decisions. + ## Best practices 1. **Keep `onSessionStart` fast** - Users are waiting for the session to be ready. diff --git a/content/copilot/how-tos/copilot-sdk/hooks/user-prompt-submitted.md b/content/copilot/how-tos/copilot-sdk/hooks/user-prompt-submitted.md index 9fa0979bf9c1..e13c7017f645 100644 --- a/content/copilot/how-tos/copilot-sdk/hooks/user-prompt-submitted.md +++ b/content/copilot/how-tos/copilot-sdk/hooks/user-prompt-submitted.md @@ -408,11 +408,11 @@ const session = await client.createSession({ }); ``` -### Rate limiting +### Usage threshold notices ```typescript const promptTimestamps: number[] = []; -const RATE_LIMIT = 10; // prompts +const NOTICE_THRESHOLD = 10; // prompts const RATE_WINDOW = 60000; // 1 minute const session = await client.createSession({ @@ -424,15 +424,16 @@ const session = await client.createSession({ while (promptTimestamps.length > 0 && promptTimestamps[0] < now - RATE_WINDOW) { promptTimestamps.shift(); } - - if (promptTimestamps.length >= RATE_LIMIT) { + + promptTimestamps.push(now); + if (promptTimestamps.length >= NOTICE_THRESHOLD) { + // This is advisory context for the model, not an enforced rate limit. + // Enforce hard limits before calling session.send(). return { - reject: true, - rejectReason: `Rate limit exceeded. Please wait before sending more prompts.`, + additionalContext: `The user has sent ${promptTimestamps.length} prompts in the last minute. Suggest waiting before sending more.`, }; } - - promptTimestamps.push(now); + return null; }, }, @@ -483,7 +484,7 @@ const session = await client.createSession({ 1. **Use `additionalContext` over `modifiedPrompt`** - Adding context is less intrusive than rewriting the prompt. -1. **Provide clear rejection reasons** - When rejecting prompts, explain why and how to fix it. +1. **Use `additionalContext` for advisory guidance**: This hook cannot reject a prompt or enforce policy. Enforce hard limits before calling `session.send()`. 1. **Keep processing fast** - This hook runs on every user message. Avoid slow operations. diff --git a/content/copilot/how-tos/copilot-sdk/integrations/microsoft-agent-framework.md b/content/copilot/how-tos/copilot-sdk/integrations/microsoft-agent-framework.md index bf97add8a1f3..2bc40d5b2b24 100644 --- a/content/copilot/how-tos/copilot-sdk/integrations/microsoft-agent-framework.md +++ b/content/copilot/how-tos/copilot-sdk/integrations/microsoft-agent-framework.md @@ -217,13 +217,19 @@ You can also use Copilot SDK's native tool definition alongside MAF tools: {% codetab typescript %} ```typescript -import { CopilotClient, DefineTool } from "@github/copilot-sdk"; +import { CopilotClient, defineTool } from "@github/copilot-sdk"; -const getWeather = DefineTool({ - name: "GetWeather", +const getWeather = defineTool("GetWeather", { description: "Get the current weather for a given location.", - parameters: { location: { type: "string", description: "City name" } }, - execute: async ({ location }) => `The weather in ${location} is sunny, 25°C.`, + parameters: { + type: "object", + properties: { + location: { type: "string", description: "City name" }, + }, + required: ["location"], + }, + handler: async ({ location }: { location: string }) => + `The weather in ${location} is sunny, 25°C.`, }); const client = new CopilotClient(); @@ -536,7 +542,7 @@ const session = await client.createSession({ }); session.on("assistant.message_delta", (event) => { - process.stdout.write(event.data.delta ?? ""); + process.stdout.write(event.data.deltaContent ?? ""); }); await session.sendAndWait({ prompt: "Write a quicksort implementation in TypeScript" }); diff --git a/content/copilot/how-tos/copilot-sdk/observability/opentelemetry.md b/content/copilot/how-tos/copilot-sdk/observability/opentelemetry.md index 39d471e5c8f6..23dfdd3c4b96 100644 --- a/content/copilot/how-tos/copilot-sdk/observability/opentelemetry.md +++ b/content/copilot/how-tos/copilot-sdk/observability/opentelemetry.md @@ -53,7 +53,7 @@ client = CopilotClient( ```golang -client, err := copilot.NewClient(copilot.ClientOptions{ +client := copilot.NewClient(&copilot.ClientOptions{ Telemetry: &copilot.TelemetryConfig{ OTLPEndpoint: "http://localhost:4318", }, diff --git a/content/copilot/how-tos/copilot-sdk/setup/bundled-cli.md b/content/copilot/how-tos/copilot-sdk/setup/bundled-cli.md index 252d20e94496..6f783d478388 100644 --- a/content/copilot/how-tos/copilot-sdk/setup/bundled-cli.md +++ b/content/copilot/how-tos/copilot-sdk/setup/bundled-cli.md @@ -76,7 +76,7 @@ await client.stop() {% codetab go %} > [!NOTE] -> The Go SDK does not bundle the CLI. You must install the CLI separately or set `Connection` to point to an existing binary. See [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/local-cli) for details. +> Unlike Node.js, Python, and .NET, the Go SDK does not include a CLI as an automatic dependency. With no explicit path, `NewClient(nil)` uses an embedded CLI when available, then falls back to `copilot` on `PATH`. To embed a CLI, run the [bundler tool](https://github.com/github/copilot-sdk/tree/main/go/README.md#distributing-your-application-with-an-embedded-github-copilot-cli) at build time. You can also set `COPILOT_CLI_PATH` or point a `Connection` at an existing binary. See [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/local-cli) for details. ```golang package main @@ -136,7 +136,7 @@ Console.WriteLine(response?.Data.Content); {% codetab java %} > [!NOTE] -> The Java SDK does not bundle or embed the Copilot CLI. You must install the CLI separately and configure its path via `Connection` or the `COPILOT_CLI_PATH` environment variable. +> The Java SDK does not bundle or embed the Copilot CLI. Install the CLI separately and either make `copilot` available on your `PATH` or set its location with `setCliPath(...)` (or connect to a running CLI server with `setCliUrl(...)`). ```java import com.github.copilot.CopilotClient; diff --git a/content/copilot/how-tos/copilot-sdk/setup/local-cli.md b/content/copilot/how-tos/copilot-sdk/setup/local-cli.md index ff7d85572dfe..6d6757c7951a 100644 --- a/content/copilot/how-tos/copilot-sdk/setup/local-cli.md +++ b/content/copilot/how-tos/copilot-sdk/setup/local-cli.md @@ -16,7 +16,7 @@ contentType: how-tos -**Use when:** You need to pin a specific CLI version, or work with the Go SDK (which does not bundle a CLI). +**Use when:** You need to pin a specific CLI version, or work with the Go SDK (which does not include a CLI automatically). ## How it works @@ -78,7 +78,7 @@ await client.stop() {% codetab go %} > [!NOTE] -> The Go SDK does not bundle a CLI, so you must always provide `Connection`. +> The Go SDK does not ship a CLI automatically. Install `copilot` on `PATH`, set the `COPILOT_CLI_PATH` environment variable, embed a CLI with the [bundler tool](https://github.com/github/copilot-sdk/tree/main/go/README.md#distributing-your-application-with-an-embedded-github-copilot-cli), or point `StdioConnection.Path` at an installed binary. ```golang package main diff --git a/content/copilot/how-tos/copilot-sdk/setup/scaling.md b/content/copilot/how-tos/copilot-sdk/setup/scaling.md index 0339a9df91b2..c74fc90b6fb6 100644 --- a/content/copilot/how-tos/copilot-sdk/setup/scaling.md +++ b/content/copilot/how-tos/copilot-sdk/setup/scaling.md @@ -8,6 +8,8 @@ intro: >- versions: fpt: '*' ghec: '*' +redirect_from: + - /copilot/how-tos/copilot-sdk/set-up-copilot-sdk/scaling contentType: how-tos --- diff --git a/content/copilot/how-tos/copilot-sdk/troubleshooting/compatibility.md b/content/copilot/how-tos/copilot-sdk/troubleshooting/compatibility.md index 60cd2712584f..1308a1bf5201 100644 --- a/content/copilot/how-tos/copilot-sdk/troubleshooting/compatibility.md +++ b/content/copilot/how-tos/copilot-sdk/troubleshooting/compatibility.md @@ -7,6 +7,8 @@ intro: >- versions: fpt: '*' ghec: '*' +redirect_from: + - /copilot/how-tos/copilot-sdk/troubleshooting/sdk-and-cli-compatibility contentType: how-tos --- From b38dc0ca1963ac50c54c65999396d3a71938a89f Mon Sep 17 00:00:00 2001 From: Laura Coursen Date: Mon, 3 Aug 2026 11:17:54 +0100 Subject: [PATCH 02/11] Add CTA-placement guidance to always-on content instructions (#62376) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8fa5a1bb-46b5-4ceb-8219-795bf8209e1f --- .github/instructions/content-guidelines.instructions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/instructions/content-guidelines.instructions.md b/.github/instructions/content-guidelines.instructions.md index 112bfc56d9c4..4effecc5042a 100644 --- a/.github/instructions/content-guidelines.instructions.md +++ b/.github/instructions/content-guidelines.instructions.md @@ -54,6 +54,7 @@ Examples of strong intros by content type: * Only include a CTA link when it genuinely makes the reader's task easier, for example by saving them the time of navigating to a settings page themselves. Do not force a CTA; if none would genuinely help the reader, do not add one. Avoid turning articles into clickbait. * A CTA can take several forms, for example a direct link to the relevant product or feature, a Copilot prompt the reader can run, or a link to start a free trial. * Only link to a URL that is the same for everyone on that version. Do not add a CTA when the in-product URL must include an enterprise, organization, or repository name (for example, `https://github.com/ORG/REPO/settings/copilot/code_review`), because the link cannot be made to work for all readers. +* Place a CTA as close as possible to the step where the reader completes the task it supports. A CTA near the final step measurably reduces time-to-task, while a CTA at an early step (for example, at the start of a multi-step setup) does not change whether or how quickly readers finish. * Procedural articles: include a CTA wherever one genuinely helps, as directly as possible. * Conceptual articles: point the reader to exactly one clear next step, usually a link to the related procedure (for example, an "About pull requests" article points to "Creating a pull request"). Place it where the reader is ready to act, typically at the end of the article. From fb1c7762ab4857cd790de4968d46733b75b19ac1 Mon Sep 17 00:00:00 2001 From: Ben Ahmady <32935794+subatoi@users.noreply.github.com> Date: Mon, 3 Aug 2026 13:15:36 +0100 Subject: [PATCH 03/11] Clarify use of alerts in reusable content (#62590) --- .../style-guide-summary.instructions.md | 8 ++++++++ .../style-guide-and-content-model/style-guide.md | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/.github/instructions/style-guide-summary.instructions.md b/.github/instructions/style-guide-summary.instructions.md index 6e46a0ab1f11..6deb843f1ec2 100644 --- a/.github/instructions/style-guide-summary.instructions.md +++ b/.github/instructions/style-guide-summary.instructions.md @@ -50,6 +50,14 @@ For Liquid variable usage, reusables, linking conventions, bullet-list markers, * Keep alerts concise (a couple of sentences max). * Use Markdown syntax: `> [!NOTE]`, `> [!TIP]`, `> [!WARNING]`, `> [!CAUTION]`, `> [!IMPORTANT]`. +Call reusable content inside alert environments, rather than placing alert environments inside reusable Markdown files. For example: + +``` +> [!CAUTION] +> {% data reusables.foo.bar %} +> Here is some additional optional text. +``` + ## Links * Introduce links with "For more information, see" or "See" when context is clear. diff --git a/content/contributing/style-guide-and-content-model/style-guide.md b/content/contributing/style-guide-and-content-model/style-guide.md index 2a84ac50d36f..917c43f98565 100644 --- a/content/contributing/style-guide-and-content-model/style-guide.md +++ b/content/contributing/style-guide-and-content-model/style-guide.md @@ -138,6 +138,20 @@ Liquid syntax for alerts is still supported and may still appear in older articl For more information on formatting alerts, see “Alerts” in [AUTOTITLE](/contributing/writing-for-github-docs/using-markdown-and-liquid-in-github-docs#alerts). +### Using alerts with reusable text + +Alerts frequently form part of reusable content (see [AUTOTITLE](/contributing/writing-for-github-docs/creating-reusable-content)). + +Call reusable content inside alert environments, rather than placing alert environments inside reusable Markdown files. + +For example: + +```markdown +> [!CAUTION] +> {% raw %}{% data reusables.foo.bar %}{% endraw %} +> Here is some additional optional text. +``` + ## Call to action (CTA) A CTA is a link or button prompting users to take the next step in their journey. It will send a user to a different location. From 63c353a9efd76597252dc7977b9958920a5e84a2 Mon Sep 17 00:00:00 2001 From: astropedrito <96799026+astropedrito@users.noreply.github.com> Date: Mon, 3 Aug 2026 15:22:36 +0200 Subject: [PATCH 04/11] Clarify ruleset effects on branch operations (#62384) Co-authored-by: Laura Coursen --- .../managing-rulesets/available-rules-for-rulesets.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets.md b/content/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets.md index 39aebe5ab9fe..0905c1564a40 100644 --- a/content/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets.md +++ b/content/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets.md @@ -188,6 +188,8 @@ You can prevent users from force pushing to the targeted branches or tags. This If someone force pushes to a branch or tag, commits that other collaborators have based their work on may be removed from the history of the branch or tag. This may lead to merge conflicts or corrupted pull requests. Force pushing can also be used to delete branches or point a branch to commits that were not approved in a pull request. +> [!NOTE] If force pushes are blocked, organization owners or repository administrators will be unable to change or rename the default branch unless they are authorized to bypass the ruleset. + Enabling force pushes will not override any other rules. For example, if a branch requires a linear commit history, you cannot force push merge commits to that branch. {% ifversion ghes %}You cannot enable force pushes for a branch if a site administrator has blocked force pushes to all branches in your repository. For more information, see [AUTOTITLE](/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise). From 51862433536cfe7f41c3f36f586cbba5b8737b72 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 3 Aug 2026 14:40:49 +0100 Subject: [PATCH 05/11] Code Scanning: Document `github-codeql-config-file` and `github-codeql-tools` (#62466) Co-authored-by: Sophie <29382425+sophietheking@users.noreply.github.com> --- .../code-scanning/repository-properties.md | 33 +++++++++++++++++ .../concepts/code-scanning/setup-types.md | 10 ++++-- .../edit-default-setup.md | 35 ++++++++++++++++++- .../code-scanning-at-scale.md | 6 ++++ .../giving-org-access-private-registries.md | 6 ++++ .../workflow-configuration-options.md | 8 ++++- data/features/codeql-config-property.yml | 5 +++ .../config-file-merged-with-default-setup.md | 1 + .../custom-configuration-file.md | 2 +- .../remote-config-file-registry.md | 1 + 10 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 data/features/codeql-config-property.yml create mode 100644 data/reusables/code-scanning/config-file-merged-with-default-setup.md create mode 100644 data/reusables/code-scanning/remote-config-file-registry.md diff --git a/content/code-security/concepts/code-scanning/repository-properties.md b/content/code-security/concepts/code-scanning/repository-properties.md index 9c27a70b14f8..4fb3488b2c1e 100644 --- a/content/code-security/concepts/code-scanning/repository-properties.md +++ b/content/code-security/concepts/code-scanning/repository-properties.md @@ -15,6 +15,12 @@ For the repository properties described here to have an effect, you need to have Repository properties which affect {% data variables.product.prodname_code_scanning %} must be created manually for your organization. You can then set values for them that apply to your entire organization or allow them to be configured differently for each repository. See [AUTOTITLE](/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization). +## Testing changes before applying them + +You may wish to test that configurations applied through repository properties have the desired effects before rolling them out to your entire organization. Repository properties can be set to specific values for individual repositories. If you are configuring a repository property for the first time, create it for your organization but do not set it to a value. Instead, set it to a value for a specific test repository where you can validate the change first. Once validated on a test repository, you can then set the value for your entire organization or the desired repositories. + +When changing the value of a supported repository property, you can use the same approach. Override or set the value of the repository property for a test repository, validate the change, and then roll it out to your organization or other repositories. + ## Supported repository properties for {% data variables.product.prodname_code_scanning %} Some {% data variables.product.prodname_code_scanning %} functionality can be configured using repository properties. Organizations can use repository properties to both enforce configurations across all repositories and for individual repositories. If {% data variables.product.prodname_code_scanning %} is customized using repository properties, the customization applies to all setup types. @@ -23,17 +29,44 @@ The following is an overview of repository properties you can set up which affec | Name | Type | |------|------| +| {% ifversion codeql-config-property %} | +| `github-codeql-config-file` | Text | +| {% endif %} | | `github-codeql-extra-queries` | Text | | `github-codeql-disable-overlay` | True/false | | `github-codeql-file-coverage-on-prs` | True/false | +| {% ifversion codeql-config-property %} | +| `github-codeql-tools` | Text | +| {% endif %} | > [!NOTE] > The repository properties which are supported depend on the version of the [github/codeql-action](https://github.com/github/codeql-action/) that is used by your {% data variables.product.prodname_code_scanning %} analyses. For {% data variables.product.prodname_code_scanning %} advanced setup, check that your workflow is referencing the latest major version. {% data variables.product.prodname_code_scanning_caps %} default setup automatically uses the latest version.{% ifversion ghes %} If the server on which you are running {% data variables.product.prodname_ghe_server %} is not connected to the internet, you may need to use the {% data variables.product.prodname_codeql %} action sync tool. See [AUTOTITLE](/code-security/how-tos/secure-at-scale/configure-enterprise-security/configure-specific-tools/configuring-code-scanning-for-your-appliance#configuring-codeql-analysis-on-a-server-without-internet-access).{% endif %} +{% ifversion codeql-config-property %} + +### Custom configuration files + +You can set the `github-codeql-config-file` property to the local or remote path of a configuration file. Accepted values for this property are the same as for the `config-file` parameter of the `codeql-action/init` action. For more information about accepted path formats and possible contents of configuration files, see [AUTOTITLE](/code-security/reference/code-scanning/workflow-configuration-options#custom-configuration-files). + +A value specified for the `github-codeql-config-file` property will apply to both {% data variables.product.prodname_code_scanning %} default setup and {% data variables.product.prodname_code_scanning %} advanced setup. If an advanced setup workflow specifies an explicit input for the `config-file` parameter of the `codeql-action/init` action, then that input will take precedence over the value configured in the repository property. This allows advanced workflows to use different configurations than those applied to default setup workflows, if desired. + +{% data reusables.code-scanning.config-file-merged-with-default-setup %} See [AUTOTITLE](/code-security/concepts/code-scanning/setup-types#configuration-options) for more information about available configuration options in {% data variables.product.prodname_code_scanning %} default setup. + +### Other analysis customization +{% else %} ### Analysis customization +{% endif %} The `github-codeql-extra-queries` property allows you to configure additional queries that should be run. This is useful to add queries to all relevant analyses in your organization without needing to modify individual workflows or switch to an advanced setup. This accepts the same values as the `queries` input of the [github/codeql-action](https://github.com/github/codeql-action/). See [AUTOTITLE](/code-security/reference/code-scanning/workflow-configuration-options). +{% ifversion codeql-config-property %} + +By default, {% data variables.product.prodname_code_scanning %} analyses use the latest released version of CodeQL. It is not generally recommended to change this, unless you are running into a specific issue that is resolved by switching to a different version. If you do need to change this, the `github-codeql-tools` property allows you to specify a different version. + +If an advanced setup workflow specifies an explicit input for the `tools` parameter of the `codeql-action/init` action, then that input will take precedence over the value configured in the repository property. This allows advanced workflows to use different configurations than those applied to default setup workflows, if desired. To enforce the value of the repository property to advanced setup workflows even if they have an explicit `tools` input, add a `!` prefix to the value of the repository property. For example, `!nightly` enforces that all workflows use the latest `nightly` release. + +{% endif %} + ### Enabling or disabling features You can disable improved incremental analysis by setting the `github-codeql-disable-overlay` property to `true`. This may be useful if improved incremental analysis is failing because of increased hardware requirements. diff --git a/content/code-security/concepts/code-scanning/setup-types.md b/content/code-security/concepts/code-scanning/setup-types.md index fc09ca69ac34..f8fc580736ae 100644 --- a/content/code-security/concepts/code-scanning/setup-types.md +++ b/content/code-security/concepts/code-scanning/setup-types.md @@ -13,7 +13,7 @@ category: ## About default setup -Default setup for {% data variables.product.prodname_code_scanning %} is the quickest, easiest, most low-maintenance way to enable {% data variables.product.prodname_code_scanning %} for your repository. Based on the code in your repository, default setup will automatically create a custom {% data variables.product.prodname_code_scanning %} configuration. After enabling default setup, the code written in {% data variables.product.prodname_codeql %}-supported languages in your repository will be scanned using {% data variables.product.prodname_codeql %}: +Default setup for {% data variables.product.prodname_code_scanning %} is the quickest, easiest, most low-maintenance way to enable {% data variables.product.prodname_code_scanning %} for your repository. Based on the code in your repository, default setup will automatically create a custom {% data variables.product.prodname_code_scanning %} configuration. You can also customize this configuration, including at scale across your organization, without creating or maintaining a workflow file. See [Customization of default setup](#customization-of-default-setup). After enabling default setup, the code written in {% data variables.product.prodname_codeql %}-supported languages in your repository will be scanned using {% data variables.product.prodname_codeql %}: * On each push to the repository's default branch, or any protected branch. For more information on protected branches, see [AUTOTITLE](/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches). * When creating or committing to a pull request based against the repository's default branch, or any protected branch, excluding pull requests from forks. @@ -39,6 +39,12 @@ For existing configurations of default setup, you can edit: If your codebase depends on a library or framework that is not recognized by the standard libraries included with {% data variables.product.prodname_codeql %}, you can also extend the {% data variables.product.prodname_codeql %} coverage in default setup using {% data variables.product.prodname_codeql %} model packs. For more information, see [Extending CodeQL coverage with CodeQL model packs in default setup](/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/edit-default-setup#extending-codeql-coverage-with-codeql-model-packs-in-default-setup). +{% ifversion codeql-config-property %} + +You can also apply a custom {% data variables.product.prodname_codeql %} configuration file to default setup across your organization at once, or for a single repository, by setting the `github-codeql-config-file` repository property. {% data reusables.code-scanning.config-file-merged-with-default-setup %} This lets you meet customization needs that previously required advanced setup, while keeping the low-maintenance benefits of default setup. See [AUTOTITLE](/code-security/concepts/code-scanning/repository-properties#custom-configuration-files) and [AUTOTITLE](/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/edit-default-setup#customizing-default-setup-with-a-configuration-file). + +{% endif %} + {% ifversion codeql-custom-properties %} Additional configuration options that are shared between all {% data variables.product.prodname_code_scanning %} setup types are available. See [AUTOTITLE](/code-security/concepts/code-scanning/repository-properties). @@ -59,7 +65,7 @@ Unless you have a specific use case, we recommend that you only assign runners w ## About advanced setup -If you need more granular control over your {% data variables.product.prodname_code_scanning %} configuration, you should instead configure advanced setup. Advanced setup for {% data variables.product.prodname_code_scanning %} is helpful when you need to customize your {% data variables.product.prodname_code_scanning %}. You can set up {% data variables.product.prodname_code_scanning %} with {% data variables.product.prodname_actions %} or an external continuous integration or continuous delivery/deployment (CI/CD) system. +{% ifversion codeql-config-property %}If the customization options available for default setup, including a custom configuration file, don't meet your needs{% else %}If you need more granular control over your {% data variables.product.prodname_code_scanning %} configuration{% endif %}, you should instead configure advanced setup. Advanced setup for {% data variables.product.prodname_code_scanning %} is helpful when you need to define your own {% data variables.product.prodname_actions %} workflow, for example to build compiled languages, use a matrix build, or change the analysis schedule. You can set up {% data variables.product.prodname_code_scanning %} with {% data variables.product.prodname_actions %} or an external continuous integration or continuous delivery/deployment (CI/CD) system. {% data reusables.code-scanning.about-multiple-configurations-link %} diff --git a/content/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/edit-default-setup.md b/content/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/edit-default-setup.md index fc2fa27e9231..dcc556aa5f64 100644 --- a/content/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/edit-default-setup.md +++ b/content/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/edit-default-setup.md @@ -16,7 +16,7 @@ category: - Find and fix code vulnerabilities --- -After running an initial analysis of your code with default setup, you can make changes to your configuration to better meet your needs. See [AUTOTITLE](/code-security/concepts/code-scanning/setup-types){% ifversion codeql-custom-properties %} and [AUTOTITLE](/code-security/concepts/code-scanning/repository-properties){% endif %}. +After running an initial analysis of your code with default setup, you can make changes to your configuration to better meet your needs. You can customize your configuration in the user interface{% ifversion codeql-custom-properties %}, or using repository properties to add custom queries{% ifversion codeql-config-property %} or apply a custom configuration file{% endif %}{% endif %}. See [AUTOTITLE](/code-security/concepts/code-scanning/setup-types){% ifversion codeql-custom-properties %} and [AUTOTITLE](/code-security/concepts/code-scanning/repository-properties){% endif %}. ## Customizing your existing configuration of default setup @@ -83,6 +83,39 @@ For more information about {% data variables.product.prodname_codeql %} model pa 1. The model packs will be automatically detected and used when {% data variables.product.prodname_code_scanning %} runs on any repository in the organization with default setup enabled. +{% ifversion codeql-config-property %} + +## Customizing default setup with a configuration file + +You can further customize default setup by applying a {% data variables.product.prodname_codeql %} configuration file, using the `github-codeql-config-file` repository property. The configuration in the file is merged with the configuration default setup generates automatically, so you can, for example, add extra queries or exclude paths without needing to switch to advanced setup. For more information about what you can configure in a {% data variables.product.prodname_codeql %} configuration file, and how it's merged with default setup, see [AUTOTITLE](/code-security/concepts/code-scanning/repository-properties#custom-configuration-files). + +### Applying a configuration file to all repositories in an organization + +The recommended way to customize default setup at scale is to set an organization-wide default value for the `github-codeql-config-file` repository property, so that you don't need to update individual repositories as you add more of them to your organization. + +1. Create a {% data variables.product.prodname_codeql %} configuration file in a central repository. You can either create a new repository for this purpose or add the file to an existing one. Your organization-wide configuration can then be maintained in one place. For information about the format of the configuration files, see [AUTOTITLE](/code-security/reference/code-scanning/workflow-configuration-options#custom-configuration-files). + + {% data reusables.code-scanning.remote-config-file-registry %} + +1. Create a `github-codeql-config-file` repository property for your organization and set its default value to the path of the configuration file. For example, if you have committed your configuration file as `codeql.yml` to the `main` branch of `octo-org/config`, you would set the value of the repository property to `remote=octo-org/config@main:codeql.yml`. + + We recommend testing the configuration file on a single repository before setting the organization-wide default. See [AUTOTITLE](/code-security/concepts/code-scanning/repository-properties#testing-changes-before-applying-them). + +1. The configuration file will be automatically detected and merged with the configuration default setup generates the next time {% data variables.product.prodname_code_scanning %} runs on each repository in the organization. Repositories that already have an explicit value set for the `github-codeql-config-file` property continue to use that value instead of the organization-wide default. For more information about how default and explicit repository property values interact, see [AUTOTITLE](/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization#adding-custom-properties). + +### Applying a configuration file to a repository + +If you only need to customize default setup for a single repository, or to test a configuration before rolling it out to your organization, you can set the property directly on that repository instead. + +1. Create a {% data variables.product.prodname_codeql %} configuration file. This can be a file within the repository being analyzed, or a file in a separate repository. For information about the format of the configuration files, see [AUTOTITLE](/code-security/reference/code-scanning/workflow-configuration-options#custom-configuration-files). + + {% data reusables.code-scanning.remote-config-file-registry %} + +1. Set the `github-codeql-config-file` repository property for the repository to the local or remote path of the configuration file. See [AUTOTITLE](/code-security/concepts/code-scanning/repository-properties#custom-configuration-files) for more information about acceptable values for this property, and [AUTOTITLE](/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization#setting-values-for-repositories-in-your-organization) for how to set a repository property value. +1. The configuration file will be automatically detected and merged with the configuration default setup generates the next time {% data variables.product.prodname_code_scanning %} runs on the repository. + +{% endif %} + {% ifversion code-scanning-inactive-repos %} ## Continuing scans on inactive repositories diff --git a/content/code-security/how-tos/secure-at-scale/configure-organization-security/configure-specific-tools/code-scanning-at-scale.md b/content/code-security/how-tos/secure-at-scale/configure-organization-security/configure-specific-tools/code-scanning-at-scale.md index 9fbd7ed8ef8e..4042076ac98e 100644 --- a/content/code-security/how-tos/secure-at-scale/configure-organization-security/configure-specific-tools/code-scanning-at-scale.md +++ b/content/code-security/how-tos/secure-at-scale/configure-organization-security/configure-specific-tools/code-scanning-at-scale.md @@ -37,6 +37,12 @@ You can enable default setup for all eligible repositories in your organization. Through your organization's security settings page, you can customize default setup for all eligible repositories, such as extending coverage using model packs. See [AUTOTITLE](/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/edit-default-setup). +{% ifversion codeql-config-property %} + +You can also apply a custom {% data variables.product.prodname_codeql %} configuration file across your organization by requiring the `github-codeql-config-file` repository property for your organization and setting its default value. This lets you customize analysis at scale without maintaining workflow files. See [AUTOTITLE](/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/edit-default-setup#applying-a-configuration-file-to-all-repositories-in-an-organization) for step-by-step instructions. + +{% endif %} + ## Configuring default setup for a subset of repositories in an organization You can filter for specific repositories you would like to configure default setup for. For more information, see [AUTOTITLE](/code-security/how-tos/secure-at-scale/configure-organization-security/establish-complete-coverage/apply-custom-configuration). diff --git a/content/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries.md b/content/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries.md index 745f5e96847a..53a28a0d85d0 100644 --- a/content/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries.md +++ b/content/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries.md @@ -30,6 +30,12 @@ When you configure access to the private registries used in your organization, { | Go | GOPROXY server, Git Source | | Java | Maven Repository | +{% ifversion codeql-config-property %} + +Additionally, _Git Source_ registries are supported for granting {% data variables.product.prodname_code_scanning %} access to configuration files in private repositories. For more information about customizing {% data variables.product.prodname_code_scanning %} using custom configuration files, see [AUTOTITLE](/code-security/reference/code-scanning/workflow-configuration-options#custom-configuration-files). + +{% endif %} + > [!TIP] > You can define one of each type of registry for each organization. If the codebases in your organization use more than one registry of a given type, you should set up a unified access point or define access to the most important registry for the codebases in that organization. diff --git a/content/code-security/reference/code-scanning/workflow-configuration-options.md b/content/code-security/reference/code-scanning/workflow-configuration-options.md index cf34f0666872..004b20ee4017 100644 --- a/content/code-security/reference/code-scanning/workflow-configuration-options.md +++ b/content/code-security/reference/code-scanning/workflow-configuration-options.md @@ -369,7 +369,13 @@ In the workflow file, use the `config-file` parameter of the `init` action to sp {% data reusables.code-scanning.custom-configuration-file %} -If the configuration file is located in an external private repository, use the `external-repository-token` parameter of the `init` action to specify a token that has access to the private repository. +{% ifversion codeql-config-property %} + +If the configuration file is located in an external private repository and you want to use it for a {% data variables.product.prodname_code_scanning %} default setup analysis, you can set up a _Git Source_ private registry configuration for your organization with credentials that allow access to the private repository containing the configuration file. For information about how to set up a private registry configuration, see [AUTOTITLE](/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries). + +{% endif %} + +If the configuration file is located in an external private repository and you are using {% data variables.product.prodname_code_scanning %} advanced setup, use the `external-repository-token` parameter of the `init` action to specify a token that has access to the private repository. ```yaml copy - uses: {% data reusables.actions.action-codeql-action-init %} diff --git a/data/features/codeql-config-property.yml b/data/features/codeql-config-property.yml new file mode 100644 index 000000000000..e98b6326d502 --- /dev/null +++ b/data/features/codeql-config-property.yml @@ -0,0 +1,5 @@ +# Allows the CodeQL Action to use the `github-codeql-config-file` property. +versions: + fpt: '*' + ghec: '*' + ghes: '>= 3.23' diff --git a/data/reusables/code-scanning/config-file-merged-with-default-setup.md b/data/reusables/code-scanning/config-file-merged-with-default-setup.md new file mode 100644 index 000000000000..da1d4331155e --- /dev/null +++ b/data/reusables/code-scanning/config-file-merged-with-default-setup.md @@ -0,0 +1 @@ +A custom configuration file applied using the `github-codeql-config-file` property is merged with the configuration that {% data variables.product.prodname_code_scanning %} default setup generates automatically. {% data variables.product.prodname_code_scanning_caps %} default setup allows you to customize some analysis settings in the user interface, such as which threat models or {% data variables.product.prodname_codeql %} model packs to use. These selections are kept in the merged configuration: the threat models selected in the default setup UI are combined with any threat models specified in the configuration file, and model packs configured in the UI are kept. diff --git a/data/reusables/code-scanning/custom-configuration-file.md b/data/reusables/code-scanning/custom-configuration-file.md index de7fe127baf4..f86fcc044de7 100644 --- a/data/reusables/code-scanning/custom-configuration-file.md +++ b/data/reusables/code-scanning/custom-configuration-file.md @@ -1 +1 @@ -The configuration file can be located within the repository you are analyzing, or in an external repository. Using an external repository allows you to specify configuration options for multiple repositories in a single place. When you reference a configuration file located in an external repository, you can use the _OWNER/REPOSITORY/FILENAME@BRANCH_ syntax. For example, _octo-org/shared/codeql-config.yml@main_. +The configuration file can be located within the repository you are analyzing, or in an external repository. Using an external repository allows you to specify configuration options for multiple repositories in a single place. When you reference a configuration file located in an external repository, {% ifversion codeql-config-property %}you can use the `remote=OWNER/REPOSITORY@REF:FILEPATH` syntax. For example, `remote=octo-org/shared@main:codeql-config.yml` will use `codeql-config.yml` from the `main` branch of the `octo-org/shared` repository. All components of this syntax, except for the repository name, are optional. For example, `remote=shared` will use `.github/codeql-action.yaml` from the `main` branch of the `shared` repository in the same organization as the repository being analyzed.{% else %}you can use the `OWNER/REPOSITORY/FILEPATH@REF` syntax. For example, `octo-org/shared/codeql-config.yml@main`.{% endif %} diff --git a/data/reusables/code-scanning/remote-config-file-registry.md b/data/reusables/code-scanning/remote-config-file-registry.md new file mode 100644 index 000000000000..4920f2d2231c --- /dev/null +++ b/data/reusables/code-scanning/remote-config-file-registry.md @@ -0,0 +1 @@ +If the configuration file is stored in a private repository other than the one being analyzed, you also need to set up a _Git Source_ private registry configuration so that default setup can access it from other repositories. See [AUTOTITLE](/code-security/how-tos/secure-at-scale/configure-organization-security/manage-usage-and-access/giving-org-access-private-registries). From 09c4f0af7e9bf4add962526a211b0357381cd3e0 Mon Sep 17 00:00:00 2001 From: Sophie <29382425+sophietheking@users.noreply.github.com> Date: Mon, 3 Aug 2026 16:41:52 +0200 Subject: [PATCH 06/11] =?UTF-8?q?Consolidate=20=E2=80=9CSetting=20up=20an?= =?UTF-8?q?=20organization=E2=80=9D=20(#62447)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: hubwriter --- ...adding-organizations-to-your-enterprise.md | 15 ++--- .../index.md | 1 + content/enterprise-onboarding/index.md | 3 +- .../index.md | 12 ---- .../setting-up-an-organization.md | 56 ------------------- .../about-sponsorships-fees-and-taxes.md | 19 ++++++- 6 files changed, 22 insertions(+), 84 deletions(-) delete mode 100644 content/enterprise-onboarding/setting-up-organizations-and-teams/index.md delete mode 100644 content/enterprise-onboarding/setting-up-organizations-and-teams/setting-up-an-organization.md diff --git a/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/adding-organizations-to-your-enterprise.md b/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/adding-organizations-to-your-enterprise.md index 35eb19e6ae8d..a1799d3f5e6a 100644 --- a/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/adding-organizations-to-your-enterprise.md +++ b/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/adding-organizations-to-your-enterprise.md @@ -1,12 +1,14 @@ --- title: Adding organizations to your enterprise -intro: Learn how to add organizations to your enterprise using three different methods. +intro: Add organizations to your enterprise using three different methods. redirect_from: - /github/setting-up-and-managing-your-enterprise/managing-organizations-in-your-enterprise-account/adding-organizations-to-your-enterprise-account - /articles/adding-organizations-to-your-enterprise-account - /github/setting-up-and-managing-your-enterprise-account/adding-organizations-to-your-enterprise-account - /github/setting-up-and-managing-your-enterprise/adding-organizations-to-your-enterprise-account - /admin/user-management/managing-organizations-in-your-enterprise/adding-organizations-to-your-enterprise + - /enterprise-onboarding/setting-up-organizations-and-teams/setting-up-an-organization + - /enterprise-onboarding/setting-up-organizations-and-teams/managing-your-organizations versions: ghec: '*' shortTitle: Add organizations @@ -58,18 +60,9 @@ After you add an existing organization to your enterprise, the organization's re * If your enterprise is billed via invoice, contact the app vendor and pay directly. * If your enterprise is billed via credit card or PayPal, billing continues automatically. To transfer an existing organization with billed apps between enterprise accounts, first remove the billed apps and then re-add the apps after the transfer is complete. -* **Sponsorships:** Any sponsorships by the organization will be canceled. +* **Sponsorships:** Any sponsorships by the organization will be canceled. Additionally, if your enterprise uses Azure metered billing, you will need to create a separate "shell" organization to continue using {% data variables.product.prodname_sponsors %}. See [AUTOTITLE](/sponsors/sponsoring-open-source-contributors/about-sponsorships-fees-and-taxes#github-sponsors-when-adding-an-organization-to-an-enterprise). * **Coupons:** Any coupons will be removed from the organization. To reapply the coupon, [contact our sales team](https://github.com/enterprise/contact). -## Handling {% data variables.product.prodname_sponsors %} with Azure billing - -If your organization is added to an enterprise account with Azure metered billing, any active {% data variables.product.prodname_sponsors %} sponsorships will be canceled. While your organization remains under enterprise billing through Azure, you will not be able to reactivate these sponsorships, as sponsoring is not currently supported for organizations billed through Azure. - -To continue using {% data variables.product.prodname_sponsors %}, create a new, separate "shell" organization that is not linked to your enterprise account or Azure billing. You can use this shell organization to manage sponsorships independently. - -> [!NOTE] -> After you create a shell organization, update any public references or documentation to point sponsors to the new organization. - ## Creating a new organization New organizations you create within your enterprise account settings are included in your enterprise account's {% data variables.product.prodname_ghe_cloud %} subscription. diff --git a/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/index.md b/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/index.md index 93fd9975b3e6..81927949c96f 100644 --- a/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/index.md +++ b/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/index.md @@ -13,6 +13,7 @@ redirect_from: - /github/setting-up-and-managing-your-enterprise-account/managing-unowned-organizations-in-your-enterprise-account - /github/setting-up-and-managing-your-enterprise/managing-unowned-organizations-in-your-enterprise-account - /admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/continuous-integration-using-jenkins + - /enterprise-onboarding/setting-up-organizations-and-teams intro: You can use organizations to group users within your company, such as divisions or groups working on similar projects, and manage access to repositories. versions: ghec: '*' diff --git a/content/enterprise-onboarding/index.md b/content/enterprise-onboarding/index.md index 526e16afb6de..49e3bd8c0943 100644 --- a/content/enterprise-onboarding/index.md +++ b/content/enterprise-onboarding/index.md @@ -17,7 +17,7 @@ journeyTracks: description: 'Organize work effectively and ensure people have the access they need to resources and administrative settings.' guides: - href: '/admin/concepts/enterprise-best-practices/organize-work' - - href: '/enterprise-onboarding/setting-up-organizations-and-teams/setting-up-an-organization' + - href: '/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/adding-organizations-to-your-enterprise' - href: '/admin/concepts/enterprise-fundamentals/roles-in-an-enterprise' - href: '/admin/managing-accounts-and-repositories/managing-roles-in-your-enterprise/identify-role-requirements' - href: '/admin/managing-accounts-and-repositories/managing-roles-in-your-enterprise/create-custom-roles' @@ -45,7 +45,6 @@ versions: ghec: '*' children: - /getting-started-with-your-enterprise - - /setting-up-organizations-and-teams - /govern-people-and-repositories redirect_from: - /enterprise-onboarding/feature-enhancements diff --git a/content/enterprise-onboarding/setting-up-organizations-and-teams/index.md b/content/enterprise-onboarding/setting-up-organizations-and-teams/index.md deleted file mode 100644 index 1b897ca84b39..000000000000 --- a/content/enterprise-onboarding/setting-up-organizations-and-teams/index.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Setting up organizations and teams in your enterprise -intro: Add and manage organizations and teams in your enterprise. -versions: - ghec: '*' -shortTitle: Set up organizations and teams -children: - - /setting-up-an-organization -docsTeamMetrics: - - enterprise-onboarding ---- - 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 deleted file mode 100644 index d86bc2b310bc..000000000000 --- a/content/enterprise-onboarding/setting-up-organizations-and-teams/setting-up-an-organization.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: Setting up an organization -intro: Set up an organization in your enterprise. -versions: - ghec: '*' -shortTitle: Set up an organization -redirect_from: - - /enterprise-onboarding/setting-up-organizations-and-teams/managing-your-organizations -contentType: how-tos -docsTeamMetrics: - - enterprise-onboarding ---- - -During a trial, there are two ways to add organizations to your enterprise. - -* **Create** a new organization in your enterprise, or -* **Invite** an existing organization to join your enterprise. - -If you chose an enterprise with {% data variables.product.prodname_emus %}, it is not possible to invite an existing organization. - -## Creating a new organization - -New organizations you create within your enterprise account settings are included in your enterprise account's {% data variables.product.prodname_ghe_cloud %} subscription. - -Enterprise owners who create an organization owned by the enterprise account automatically become organization owners. - -During a trial of {% data variables.product.prodname_ghe_cloud %}, you can create up to three new organizations in your enterprise. - -{% data reusables.enterprise-accounts.access-enterprise %} -{% data reusables.enterprise-accounts.click-organizations-tab %} -1. Above the list of organizations, click **New organization**. -1. Under "Organization name", type a name for your organization. -1. Click **Create organization**. -1. Optionally, under "Invite owners", type the username of a person you'd like to invite to become an organization owner, then click **Invite**. -1. Click **Finish**. - -## Inviting an existing organization - -Enterprise owners can invite existing organizations to join their enterprise account. - -During a trial of {% data variables.product.prodname_ghe_cloud %}, you can invite organizations to join your trial enterprise. You can invite organizations that are not currently owned by another enterprise. If an organization you want to invite is already owned by another enterprise, you cannot invite it to your trial enterprise. - -After you invite the organization, and before an owner approves the invitation, you can cancel or resend the invitation at any time. - -{% data reusables.enterprise-accounts.access-enterprise %} -{% data reusables.enterprise-accounts.click-organizations-tab %} -1. Above the list of organizations, click **Invite organization**. -1. Under "Organization name", start typing the name of the organization you want to invite and select it when it appears in the dropdown list. -1. Click **Invite organization**. The organization owners will receive an email inviting them to join the enterprise. -1. After an organization owner has approved the invitation, navigate back to the **Organizations** tab of the enterprise settings. -1. Under "Organizations", click **X pending**. -1. To complete the transfer, next to the organization name, click **Approve**. - -## Next steps - -After you have created the organizations you need, learn how to manage people's access to your enterprise and organizations' settings and resources. See [AUTOTITLE](/enterprise-onboarding/setting-up-organizations-and-teams/about-roles-in-an-enterprise). diff --git a/content/sponsors/sponsoring-open-source-contributors/about-sponsorships-fees-and-taxes.md b/content/sponsors/sponsoring-open-source-contributors/about-sponsorships-fees-and-taxes.md index 8add254f589e..cd637c52ca24 100644 --- a/content/sponsors/sponsoring-open-source-contributors/about-sponsorships-fees-and-taxes.md +++ b/content/sponsors/sponsoring-open-source-contributors/about-sponsorships-fees-and-taxes.md @@ -34,18 +34,18 @@ If the account you want to sponsor does not have a profile on {% data variables. ## About billing and sponsorship payments -GitHub provides the following billing methods to show love and support to your fellow collaborators and developers in the form of paid sponsorships: debit or credit cards, and Patreon. +{% data variables.product.github %} provides the following billing methods to show love and support to your fellow collaborators and developers in the form of paid sponsorships: debit or credit cards, and Patreon. For additional information on your preferred funding, please click on any of the links below. * [AUTOTITLE](/sponsors/sponsoring-open-source-contributors/sponsoring-an-open-source-contributor-through-github) * [AUTOTITLE](/sponsors/sponsoring-open-source-contributors/sponsoring-an-open-source-contributor-through-patreon) > [!NOTE] -> When sponsoring through GitHub, your balance will be charged effective immediately. {% data reusables.sponsors.prorated-sponsorship %} +> When sponsoring through {% data variables.product.github %}, your balance will be charged effective immediately. {% data reusables.sponsors.prorated-sponsorship %} Alternatively, enterprises and organizations may opt to be billed by recurring invoice. For additional information on billing by invoice, please click on [AUTOTITLE](/sponsors/sponsoring-open-source-contributors/paying-for-github-sponsors-by-invoice). > [!WARNING] -> As of February 23, 2023, GitHub Sponsors does not support PayPal. While this only affects GitHub Sponsors, please note that it is still possible to fund GitHub Pro, GitHub Copilot, Actions and Packages, Storage, Codespaces and Git LFS Data using PayPal. +> As of February 23, 2023, {% data variables.product.prodname_sponsors %} does not support PayPal. While this only affects {% data variables.product.prodname_sponsors %}, please note that it is still possible to fund {% data variables.product.prodname_pro %}, {% data variables.product.prodname_copilot %}, Actions, {% data variables.product.prodname_registry %}, Storage, {% data variables.product.prodname_codespaces %} and Git LFS Data using PayPal. {% ifversion enterprise-orgs-sponsors-with-cc %} @@ -81,3 +81,16 @@ As a sponsor, you acknowledge that we may disclose to the owner of each account This information is necessary to enable payment and reporting of any taxes arising from such sponsorship payments. {% data reusables.sponsors.sponsorships-not-tax-deductible %} + +{% ifversion ghec %} + +## {% data variables.product.prodname_sponsors %} when adding an organization to an enterprise + +If you add an organization to an enterprise account with Azure metered billing, any active {% data variables.product.prodname_sponsors %} sponsorships will be canceled. While your organization remains under enterprise billing through Azure, you will not be able to reactivate these sponsorships, as sponsoring is not currently supported for organizations billed through Azure. + +To continue using {% data variables.product.prodname_sponsors %}, create a new, separate "shell" organization that is not linked to your enterprise account or Azure billing. You can use this shell organization to manage sponsorships independently. See [AUTOTITLE](/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/adding-organizations-to-your-enterprise). + +> [!NOTE] +> After you create a shell organization, update any public references or documentation to point sponsors to the new organization. + +{% endif %} From f39067c7efc580a117d7a823f58e54982456531b Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 3 Aug 2026 07:42:00 -0700 Subject: [PATCH 07/11] Fix redirected and broken internal links from weekly report (#62526) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Vanessa Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Copilot-Session: 43e24a9a-9b98-4db1-9289-149fc95eed31 --- content/README.md | 4 ++-- ...-started-with-self-hosted-runners-for-your-enterprise.md | 4 ++-- .../making-a-github-app-public-or-private.md | 2 +- .../concepts/enterprise-billing/combined-enterprise-use.md | 2 +- content/billing/reference/github-license-users.md | 4 ++-- .../concepts/code-scanning/autofix-for-code-scanning.md | 2 +- .../getting-started/github-security-features.md | 4 ++-- .../integrate-with-existing-tools/upload-sarif-file.md | 2 +- .../manage-code-scanning-alerts/resolve-alerts.md | 6 +++--- .../enabling-secret-scanning-for-generic-patterns.md | 2 +- .../style-guide-and-content-model/style-guide.md | 2 +- .../writing-for-github-docs/creating-reusable-content.md | 2 +- .../deploying-your-website-automatically.md | 4 ++-- content/get-started/using-github/github-mobile.md | 2 +- content/migrations/overview/about-locked-repositories.md | 4 ++-- ...ooting-your-migration-with-github-enterprise-importer.md | 4 ++-- content/migrations/using-ghe-migrator/about-ghe-migrator.md | 4 ++-- ...xporting-migration-data-from-github-enterprise-server.md | 2 +- ...m-github-enterprise-server-to-github-enterprise-cloud.md | 2 +- .../setting-permissions-for-adding-outside-collaborators.md | 2 +- ...ing-an-organization-member-to-an-outside-collaborator.md | 2 +- content/pull-requests/reference/status-checks.md | 4 ++-- .../blob-storage-management-console.md | 4 ++-- data/reusables/enterprise_installation/ssh-into-instance.md | 2 +- .../enterprise_installation/ssh-into-target-instance.md | 2 +- 25 files changed, 37 insertions(+), 37 deletions(-) diff --git a/content/README.md b/content/README.md index e158ddcba32f..d813e7caddc8 100644 --- a/content/README.md +++ b/content/README.md @@ -2,7 +2,7 @@ The `/content` directory is where all the site's (English) Markdown content lives! -See the [markup reference guide](https://docs.github.com/en/contributing/syntax-and-versioning-for-github-docs/using-markdown-and-liquid-in-github-docs) for more information about supported Markdown features. +See the [markup reference guide](https://docs.github.com/en/contributing/writing-for-github-docs/using-markdown-and-liquid-in-github-docs) for more information about supported Markdown features. See the [contributing docs](https://docs.github.com/en/contributing) for general information about working with the docs. @@ -385,7 +385,7 @@ and when viewed on GitHub Enterprise Server docs, the version is included as wel ### Using AUTOTITLE for internal links -If you create an internal link, you can use the AUTOTITLE keyword to generate an article's title in the rendered link. See the [markup reference](https://docs.github.com/en/contributing/syntax-and-versioning-for-github-docs/using-markdown-and-liquid-in-github-docs#internal-links-with-autotitle) for details. +If you create an internal link, you can use the AUTOTITLE keyword to generate an article's title in the rendered link. See the [markup reference](https://docs.github.com/en/contributing/writing-for-github-docs/using-markdown-and-liquid-in-github-docs#internal-links-with-autotitle) for details. ### Linking to the current article in a different version of the docs diff --git a/content/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-self-hosted-runners-for-your-enterprise.md b/content/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-self-hosted-runners-for-your-enterprise.md index da793e2853bd..a09dc6250adf 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-self-hosted-runners-for-your-enterprise.md +++ b/content/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-self-hosted-runners-for-your-enterprise.md @@ -113,8 +113,8 @@ Optionally, you can use {% data variables.product.prodname_actions_runner_contro * {% ifversion ghec %}If you use {% data variables.product.prodname_ghe_server %}, you{% elsif ghes %}You{% endif %} can manually sync repositories on {% data variables.product.prodname_dotcom_the_website %} containing actions to your enterprise on {% data variables.product.prodname_ghe_server %}. Alternatively, you can allow members of your enterprise to automatically access actions from {% data variables.product.prodname_dotcom_the_website %} by using {% data variables.product.prodname_github_connect %}. For more information, see the following. - * [AUTOTITLE]({% ifversion ghec %}/enterprise-server@latest{% endif %}/admin/github-actions/managing-access-to-actions-from-githubcom/manually-syncing-actions-from-githubcom){% ifversion ghec %} in the {% data variables.product.prodname_ghe_server %} documentation{% endif %} - * [AUTOTITLE]({% ifversion ghec %}/enterprise-server@latest{% endif %}/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect){% ifversion ghec %} in the {% data variables.product.prodname_ghe_server %} documentation{% endif %} + * [AUTOTITLE]({% ifversion ghec %}/enterprise-server@latest{% endif %}/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/manually-syncing-actions-from-githubcom){% ifversion ghec %} in the {% data variables.product.prodname_ghe_server %} documentation{% endif %} + * [AUTOTITLE]({% ifversion ghec %}/enterprise-server@latest{% endif %}/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect){% ifversion ghec %} in the {% data variables.product.prodname_ghe_server %} documentation{% endif %} * You can customize the software available on your self-hosted runner machines, or configure your runners to run software similar to {% data variables.product.company_short %}-hosted runners{% ifversion ghes %} available for customers using {% data variables.product.prodname_dotcom_the_website %}{% endif %}. The software that powers runner machines for {% data variables.product.prodname_actions %} is open source. For more information, see the [`actions/runner`](https://github.com/actions/runner) and [`actions/runner-images`](https://github.com/actions/runner-images) repositories. diff --git a/content/apps/creating-github-apps/registering-a-github-app/making-a-github-app-public-or-private.md b/content/apps/creating-github-apps/registering-a-github-app/making-a-github-app-public-or-private.md index eb503b3d0a13..24ce1d7d80c2 100644 --- a/content/apps/creating-github-apps/registering-a-github-app/making-a-github-app-public-or-private.md +++ b/content/apps/creating-github-apps/registering-a-github-app/making-a-github-app-public-or-private.md @@ -34,7 +34,7 @@ If you want your organization-owned application to be installed on your enterpri If you want your {% data variables.product.prodname_github_app %} to be available to organizations in a {% data variables.product.prodname_ghe_server %} instance that you are not part of, then you need to take additional steps. For more information, see [AUTOTITLE](/apps/sharing-github-apps/making-your-github-app-available-for-github-enterprise-server). -If it is important for {% ifversion ghes %}other {% endif %}{% data variables.product.prodname_ghe_server %} users to be able to use your tool, consider using {% data variables.product.prodname_actions %} instead of a {% data variables.product.prodname_github_app %}. Public actions are available on {% data variables.product.prodname_ghe_server %} instances with GitHub Connect. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect) and [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/github-actions/getting-started-with-github-actions-for-your-enterprise/about-github-actions-for-enterprises){% ifversion ghes %}.{% else %} in the {% data variables.product.prodname_ghe_server %} documentation.{% endif %} +If it is important for {% ifversion ghes %}other {% endif %}{% data variables.product.prodname_ghe_server %} users to be able to use your tool, consider using {% data variables.product.prodname_actions %} instead of a {% data variables.product.prodname_github_app %}. Public actions are available on {% data variables.product.prodname_ghe_server %} instances with GitHub Connect. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect) and [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/about-github-actions-for-enterprises){% ifversion ghes %}.{% else %} in the {% data variables.product.prodname_ghe_server %} documentation.{% endif %} For information about changing the visibility of a {% data variables.product.prodname_github_app %} registration, see [AUTOTITLE](/apps/maintaining-github-apps/modifying-a-github-app-registration). diff --git a/content/billing/concepts/enterprise-billing/combined-enterprise-use.md b/content/billing/concepts/enterprise-billing/combined-enterprise-use.md index 312ac020aa51..7798ac748b05 100644 --- a/content/billing/concepts/enterprise-billing/combined-enterprise-use.md +++ b/content/billing/concepts/enterprise-billing/combined-enterprise-use.md @@ -72,5 +72,5 @@ There are two types of {% data variables.product.prodname_enterprise %} (GHE) li * [AUTOTITLE](/billing/reference/github-license-users) * [Pricing](https://github.com/pricing) * [AUTOTITLE](/billing/concepts/enterprise-billing/billing-for-enterprises) -* [AUTOTITLE]({% ifversion fpt or ghec %}/enterprise-server@latest{% endif %}/admin/installation/setting-up-a-github-enterprise-server-instance) +* [AUTOTITLE]({% ifversion fpt or ghec %}/enterprise-server@latest{% endif %}/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance) * The [{% data variables.product.prodname_enterprise %} Releases](https://enterprise.github.com/releases/) website diff --git a/content/billing/reference/github-license-users.md b/content/billing/reference/github-license-users.md index 6d7a61f7ba3c..254495069636 100644 --- a/content/billing/reference/github-license-users.md +++ b/content/billing/reference/github-license-users.md @@ -77,11 +77,11 @@ If your enterprise does not use {% data variables.product.prodname_emus %} or us ## Organizations on {% data variables.product.prodname_ghe_server %} * Any active user who has successfully authenticated to your {% data variables.product.prodname_ghe_server %} instance -* Dormant users (administrators can suspend dormant users to free licenses, see [Managing dormant users]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/user-management/managing-users-in-your-enterprise/managing-dormant-users){% ifversion not ghes %} in the {% data variables.product.prodname_ghe_server %} documentation{% endif %}) +* Dormant users (administrators can suspend dormant users to free licenses, see [Managing dormant users]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/managing-dormant-users){% ifversion not ghes %} in the {% data variables.product.prodname_ghe_server %} documentation{% endif %}) ### People who don't consume a license -* Suspended users (see [Suspending and unsuspending users]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/user-management/managing-users-in-your-enterprise/suspending-and-unsuspending-users){% ifversion not ghes %} in the {% data variables.product.prodname_ghe_server %} documentation{% else %}.{% endif %}) +* Suspended users (see [Suspending and unsuspending users]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/suspending-and-unsuspending-users){% ifversion not ghes %} in the {% data variables.product.prodname_ghe_server %} documentation{% else %}.{% endif %}) * If you have enabled SCIM on your {% data variables.product.prodname_ghe_server %} instance, the built-in setup user you create, provided you use the `scim-admin` username. * Users who already consume a license on {% data variables.product.prodname_ghe_cloud %}, provided you sync license usage between environments. See [AUTOTITLE](/billing/concepts/enterprise-billing/combined-enterprise-use). diff --git a/content/code-security/concepts/code-scanning/autofix-for-code-scanning.md b/content/code-security/concepts/code-scanning/autofix-for-code-scanning.md index 02ee8f5d6526..ecc7faf7b435 100644 --- a/content/code-security/concepts/code-scanning/autofix-for-code-scanning.md +++ b/content/code-security/concepts/code-scanning/autofix-for-code-scanning.md @@ -40,6 +40,6 @@ Keep the following in mind: You do not need a subscription to {% data variables.product.prodname_copilot %} to use {% data variables.copilot.copilot_autofix %}, and it does not consume {% data variables.product.prodname_ai_credits_short %}. {% data variables.copilot.copilot_autofix_short %} is available to all public repositories on {% data variables.product.prodname_dotcom_the_website %}, as well as internal or private repositories owned by organizations and enterprises that have a license for {% data variables.product.prodname_GH_code_security %}. -{% data variables.copilot.copilot_autofix_short %} is allowed by default and enabled for every repository that uses {% data variables.product.prodname_codeql %}, regardless of whether it uses default or advanced setup for {% data variables.product.prodname_code_scanning %}. There is no separate step to enable {% data variables.copilot.copilot_autofix_short %}: enabling {% data variables.product.prodname_code_scanning %} with {% data variables.product.prodname_codeql %} is sufficient. See [AUTOTITLE](/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning). +{% data variables.copilot.copilot_autofix_short %} is allowed by default and enabled for every repository that uses {% data variables.product.prodname_codeql %}, regardless of whether it uses default or advanced setup for {% data variables.product.prodname_code_scanning %}. There is no separate step to enable {% data variables.copilot.copilot_autofix_short %}: enabling {% data variables.product.prodname_code_scanning %} with {% data variables.product.prodname_codeql %} is sufficient. See [AUTOTITLE](/code-security/how-tos/find-and-fix-code-vulnerabilities/configure-code-scanning/configure-code-scanning). Administrators at the enterprise, organization, and repository levels can choose to disable {% data variables.copilot.copilot_autofix_short %}. If {% data variables.copilot.copilot_autofix_short %} has been disabled at your level, you can re-enable it by following the same steps used to disable it and selecting the option to allow {% data variables.copilot.copilot_autofix_short %}. To learn how to manage {% data variables.copilot.copilot_autofix_short %} at each level, see [AUTOTITLE](/code-security/how-tos/manage-security-alerts/manage-code-scanning-alerts/disabling-autofix-for-code-scanning). diff --git a/content/code-security/getting-started/github-security-features.md b/content/code-security/getting-started/github-security-features.md index c94567a4ac79..a18c2d910ebe 100644 --- a/content/code-security/getting-started/github-security-features.md +++ b/content/code-security/getting-started/github-security-features.md @@ -125,7 +125,7 @@ For accounts on {% ifversion fpt or ghec %}{% data variables.product.prodname_te These features are available for all repository types. {% ifversion fpt or ghec %}Some of these features are available for public repositories free of charge, meaning that you don't need to purchase {% data variables.product.prodname_GH_secret_protection %} to enable the feature on a public repository.{% endif %} ### {% data variables.secret-scanning.user_alerts_caps %} @@ -177,7 +177,7 @@ These features are available for all repository types. {% ifversion fpt or ghec diff --git a/content/code-security/how-tos/find-and-fix-code-vulnerabilities/integrate-with-existing-tools/upload-sarif-file.md b/content/code-security/how-tos/find-and-fix-code-vulnerabilities/integrate-with-existing-tools/upload-sarif-file.md index 1796e54704ac..44a547821782 100644 --- a/content/code-security/how-tos/find-and-fix-code-vulnerabilities/integrate-with-existing-tools/upload-sarif-file.md +++ b/content/code-security/how-tos/find-and-fix-code-vulnerabilities/integrate-with-existing-tools/upload-sarif-file.md @@ -151,7 +151,7 @@ When you upload multiple SARIF files for a commit, you must indicate a "category * Using the {% data variables.product.prodname_codeql_cli %} directly, pass the `--sarif-category` argument to the `codeql database analyze` command when you generate SARIF files. For more information, see [AUTOTITLE](/code-security/concepts/code-scanning/codeql/codeql-cli#about-generating-code-scanning-results-with-the-codeql-cli). * Using {% data variables.product.prodname_actions %} with `codeql-action/analyze`, the category is set automatically from the workflow name and any matrix variables (typically, `language`). You can override this by specifying a `category` input for the action, which is useful when you analyze different sections of a monorepo in a single workflow. * Using {% data variables.product.prodname_actions %} to upload results from other static analysis tools, then you must specify a `category` input if you upload more than one file of results for the same tool in one workflow. For more information, see [AUTOTITLE](/code-security/how-tos/find-and-fix-code-vulnerabilities/integrate-with-existing-tools/upload-sarif-file#uploading-a-code-scanning-analysis-with-github-actions). -* If you are not using either of these approaches, you must specify a unique `runAutomationDetails.id` in each SARIF file to upload. For more information about this property, see [AUTOTITLE](/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning#runautomationdetails-object). +* If you are not using either of these approaches, you must specify a unique `runAutomationDetails.id` in each SARIF file to upload. For more information about this property, see [AUTOTITLE](/code-security/reference/code-scanning/sarif-files/sarif-support#runautomationdetails-object). If you upload a second SARIF file for a commit with the same category and from the same tool, the earlier results are overwritten. However, if you try to upload multiple SARIF files for the same tool and category in a single {% data variables.product.prodname_actions %} workflow run, the misconfiguration is detected and the run will fail. diff --git a/content/code-security/how-tos/manage-security-alerts/manage-code-scanning-alerts/resolve-alerts.md b/content/code-security/how-tos/manage-security-alerts/manage-code-scanning-alerts/resolve-alerts.md index 217db18a4e1a..cfb03c193deb 100644 --- a/content/code-security/how-tos/manage-security-alerts/manage-code-scanning-alerts/resolve-alerts.md +++ b/content/code-security/how-tos/manage-security-alerts/manage-code-scanning-alerts/resolve-alerts.md @@ -156,7 +156,7 @@ You may have multiple {% data variables.product.prodname_code_scanning %} config 1. Once you have removed any unwanted configurations and confirmed the expected configurations are displayed, click **Save changes**. - If you save your changes after accidentally deleting a configuration, re-run the configuration to update the alert. For more information on re-running configurations that use {% data variables.product.prodname_actions %}, see [AUTOTITLE](/actions/managing-workflow-runs/re-running-workflows-and-jobs#re-running-all-the-jobs-in-a-workflow). + If you save your changes after accidentally deleting a configuration, re-run the configuration to update the alert. For more information on re-running configurations that use {% data variables.product.prodname_actions %}, see [AUTOTITLE](/actions/how-tos/manage-workflow-runs/re-run-workflows-and-jobs#re-running-all-the-jobs-in-a-workflow). > [!NOTE] > * If you remove all {% data variables.product.prodname_code_scanning %} configurations for the default branch of your repository, the default branch will remain in the "Affected branches" sidebar, but it will not be analyzed by any configurations. @@ -165,5 +165,5 @@ You may have multiple {% data variables.product.prodname_code_scanning %} config ## Further reading * [AUTOTITLE](/code-security/how-tos/manage-security-alerts/manage-code-scanning-alerts/triage-alerts-in-pull-requests) -* [AUTOTITLE](/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning) -* [AUTOTITLE](/code-security/code-scanning/integrating-with-code-scanning/about-integration-with-code-scanning) +* [AUTOTITLE](/code-security/how-tos/find-and-fix-code-vulnerabilities/configure-code-scanning/configure-code-scanning) +* [AUTOTITLE](/code-security/concepts/code-scanning/integration-with-code-scanning) diff --git a/content/code-security/how-tos/secure-your-secrets/detect-secret-leaks/enabling-secret-scanning-for-generic-patterns.md b/content/code-security/how-tos/secure-your-secrets/detect-secret-leaks/enabling-secret-scanning-for-generic-patterns.md index 2e5a7986efd5..4fd29fe9092b 100644 --- a/content/code-security/how-tos/secure-your-secrets/detect-secret-leaks/enabling-secret-scanning-for-generic-patterns.md +++ b/content/code-security/how-tos/secure-your-secrets/detect-secret-leaks/enabling-secret-scanning-for-generic-patterns.md @@ -33,4 +33,4 @@ For more information about generic patterns, see "{% ifversion fpt or ghec %}[AU ### Enabling detection of generic patterns for an organization -You can enable scanning for generic patterns at the organization level by applying a custom security configuration. For more information, see [AUTOTITLE](/code-security/securing-your-organization/enabling-security-features-in-your-organization/creating-a-custom-security-configuration). +You can enable scanning for generic patterns at the organization level by applying a custom security configuration. For more information, see [AUTOTITLE](/code-security/how-tos/secure-at-scale/configure-organization-security/establish-complete-coverage/create-custom-configuration). diff --git a/content/contributing/style-guide-and-content-model/style-guide.md b/content/contributing/style-guide-and-content-model/style-guide.md index 917c43f98565..605438e3340d 100644 --- a/content/contributing/style-guide-and-content-model/style-guide.md +++ b/content/contributing/style-guide-and-content-model/style-guide.md @@ -1054,7 +1054,7 @@ A release note for a feature answers the following questions. * > Site administrators can increase the security of the Management Console by configuring the rate limit for sign-in attempts, as well as the lockout duration after exceeding the rate limit. For more information, see [Configuring rate limits](/enterprise-server@3.7/admin/configuration/configuring-your-enterprise/configuring-rate-limits#configuring-rate-limits-for-authentication-to-the-management-console). -* > Enterprise owners can control where users can fork repositories. Forking can be limited to preset combinations of organizations, the same organization as the parent repository, user accounts, or everywhere. For more information, see [Enforcing repository management policies in your enterprise](/enterprise-server@3.7/admin/policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise#enforcing-a-policy-for-forking-private-or-internal-repositories). +* > Enterprise owners can control where users can fork repositories. Forking can be limited to preset combinations of organizations, the same organization as the parent repository, user accounts, or everywhere. For more information, see [Enforcing repository management policies in your enterprise](/enterprise-server@3.7/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise#enforcing-a-policy-for-forking-private-or-internal-repositories). * > Users can create files with geoJSON, topoJSON, and STL diagrams and render the diagrams in the web interface. For more information, see [Working with non-code files](/enterprise-server@3.7/repositories/working-with-files/using-files/working-with-non-code-files). diff --git a/content/contributing/writing-for-github-docs/creating-reusable-content.md b/content/contributing/writing-for-github-docs/creating-reusable-content.md index 093e3283bf1d..4f8424015ffc 100644 --- a/content/contributing/writing-for-github-docs/creating-reusable-content.md +++ b/content/contributing/writing-for-github-docs/creating-reusable-content.md @@ -24,7 +24,7 @@ Reusable files are divided generally into directories by task. For example, if y ### Applying versioning to reusables -Reusables can include Liquid conditionals to conditionally render content depending on the current version being viewed. +Reusables can include Liquid conditionals to conditionally render content depending on the current version being viewed. ## About variables diff --git a/content/get-started/start-your-journey/deploying-your-website-automatically.md b/content/get-started/start-your-journey/deploying-your-website-automatically.md index 674f3b289a63..02554b5ce38b 100644 --- a/content/get-started/start-your-journey/deploying-your-website-automatically.md +++ b/content/get-started/start-your-journey/deploying-your-website-automatically.md @@ -123,7 +123,7 @@ Across this series, you built a complete software project and practiced the {% d ## Next steps * Expand your understanding of Git and {% data variables.product.github %}. For more information, see [AUTOTITLE](/get-started/start-your-journey/git-and-github-learning-resources). -* Explore {% data variables.copilot.copilot_chat_short %} to learn faster and get help as you code. For more information, see [AUTOTITLE]({% ifversion ghes %}/enterprise-cloud@latest/{% endif %}/copilot/concepts/chat){% ifversion ghes %} in the {% data variables.product.prodname_ghe_cloud %} documentation{% endif %}. -* Go deeper with AI and learn how agents can act like a practical coding partner by turning ideas into small actionable steps, generating examples, and handling repetitive work. For more information, see [AUTOTITLE]({% ifversion ghes %}/enterprise-cloud@latest/{% endif %}/copilot/concepts/agents){% ifversion ghes %} in the {% data variables.product.prodname_ghe_cloud %} documentation{% endif %}. +* Explore {% data variables.copilot.copilot_chat_short %} to learn faster and get help as you code. For more information, see [AUTOTITLE]({% ifversion ghes %}/enterprise-cloud@latest{% endif %}/copilot/concepts/chat){% ifversion ghes %} in the {% data variables.product.prodname_ghe_cloud %} documentation{% endif %}. +* Go deeper with AI and learn how agents can act like a practical coding partner by turning ideas into small actionable steps, generating examples, and handling repetitive work. For more information, see [AUTOTITLE]({% ifversion ghes %}/enterprise-cloud@latest{% endif %}/copilot/concepts/agents){% ifversion ghes %} in the {% data variables.product.prodname_ghe_cloud %} documentation{% endif %}. * Learn more about automating your software projects with {% data variables.product.prodname_actions %} workflows. For more information, see [AUTOTITLE](/actions/get-started/understand-github-actions). * Add a custom domain or explore more ways to publish your website with {% data variables.product.prodname_pages %}. For more information, see [AUTOTITLE](/pages/getting-started-with-github-pages). diff --git a/content/get-started/using-github/github-mobile.md b/content/get-started/using-github/github-mobile.md index 30ae02c3f658..be5d79bc5f9b 100644 --- a/content/get-started/using-github/github-mobile.md +++ b/content/get-started/using-github/github-mobile.md @@ -67,7 +67,7 @@ To access accounts on {% data variables.enterprise.data_residency %} using {% da You must install {% data variables.product.prodname_mobile %} 1.4 or later on your device to use {% data variables.product.prodname_mobile %} with {% data variables.product.prodname_ghe_server %}. -To use {% data variables.product.prodname_mobile %} with {% data variables.product.prodname_ghe_server %}, {% data variables.product.prodname_dotcom %} must be version 3.0 or greater, and your enterprise owner must enable mobile support for your enterprise. For more information, see {% ifversion ghes %}[AUTOTITLE](/admin/release-notes) and {% endif %}[Managing {% data variables.product.prodname_mobile %} for your enterprise]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/configuration/configuring-your-enterprise/managing-github-mobile-for-your-enterprise){% ifversion not ghes %} in the {% data variables.product.prodname_ghe_server %} documentation.{% else %}.{% endif %} +To use {% data variables.product.prodname_mobile %} with {% data variables.product.prodname_ghe_server %}, {% data variables.product.prodname_dotcom %} must be version 3.0 or greater, and your enterprise owner must enable mobile support for your enterprise. For more information, see {% ifversion ghes %}[AUTOTITLE](/admin/release-notes) and {% endif %}[Managing {% data variables.product.prodname_mobile %} for your enterprise]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/configuring-settings/configuring-user-applications-for-your-enterprise/managing-github-mobile-for-your-enterprise){% ifversion not ghes %} in the {% data variables.product.prodname_ghe_server %} documentation.{% else %}.{% endif %} During the {% data variables.release-phases.public_preview %} for {% data variables.product.prodname_mobile %} with {% data variables.product.prodname_ghe_server %}, you must be signed in with a personal account on {% data variables.product.prodname_dotcom_the_website %}. diff --git a/content/migrations/overview/about-locked-repositories.md b/content/migrations/overview/about-locked-repositories.md index 92a2f8297e54..5b33648055a6 100644 --- a/content/migrations/overview/about-locked-repositories.md +++ b/content/migrations/overview/about-locked-repositories.md @@ -47,7 +47,7 @@ When you call the [Start an organization migration](/rest/migrations/orgs#start- If you lock a repository via this endpoint, you can unlock the repository using the [Unlock an organization repository](/rest/migrations/orgs#unlock-an-organization-repository) endpoint. -If the repository is stored on {% data variables.product.prodname_ghe_server %}, a site administrator can also unlock the repository using the site admin dashboard. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/user-management/managing-repositories-in-your-enterprise/locking-a-repository){% ifversion ghes %}.{% else %} in the {% data variables.product.prodname_ghe_server %} documentation.{% endif %} +If the repository is stored on {% data variables.product.prodname_ghe_server %}, a site administrator can also unlock the repository using the site admin dashboard. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/managing-accounts-and-repositories/managing-repositories-in-your-enterprise/locking-a-repository){% ifversion ghes %}.{% else %} in the {% data variables.product.prodname_ghe_server %} documentation.{% endif %} ## Repositories locked by `ghe-migrator` @@ -57,7 +57,7 @@ If the import succeeded, you can unlock the repository with the `ghe-migrator un If the import failed, not all of your data has been migrated, and we recommend deleting the repository and retrying the migration, to prevent data loss. -If you're sure you want to use the repository, a site administrator can unlock the repository using the site admin dashboard. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/user-management/managing-repositories-in-your-enterprise/locking-a-repository){% ifversion ghes %}.{% else %} in the {% data variables.product.prodname_ghe_server %} documentation.{% endif %} +If you're sure you want to use the repository, a site administrator can unlock the repository using the site admin dashboard. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/managing-accounts-and-repositories/managing-repositories-in-your-enterprise/locking-a-repository){% ifversion ghes %}.{% else %} in the {% data variables.product.prodname_ghe_server %} documentation.{% endif %} The source repository is not locked by default, only if the `--lock` argument is specified when preparing the repository for export with the `ghe-migrator add` command. To unlock the repository, use the `ghe-migrator unlock` command. For more information, see [AUTOTITLE](/migrations/using-ghe-migrator/migrating-data-to-github-enterprise-server#unlocking-repositories-on-the-source). diff --git a/content/migrations/troubleshooting/troubleshooting-your-migration-with-github-enterprise-importer.md b/content/migrations/troubleshooting/troubleshooting-your-migration-with-github-enterprise-importer.md index 53591a5b1829..e3c519534a13 100644 --- a/content/migrations/troubleshooting/troubleshooting-your-migration-with-github-enterprise-importer.md +++ b/content/migrations/troubleshooting/troubleshooting-your-migration-with-github-enterprise-importer.md @@ -86,7 +86,7 @@ First, try excluding releases from the migration by using the `--skip-releases` If that doesn't work, we'd recommend upgrading to {% data variables.product.prodname_ghe_server %} 3.8.0 or later. If you're unable to upgrade, another option is to generate your repository archives manually using `ghe-migrator`: -1. Generate a migration archive for your repository. You must only export one repository at a time. For instructions, see [Exporting migration data from your enterprise]({% ifversion fpt or ghec %}/enterprise-server@latest{% endif %}/admin/user-management/migrating-data-to-and-from-your-enterprise/exporting-migration-data-from-your-enterprise){% ifversion ghes %}.{% else %} in the {% data variables.product.prodname_ghe_server %} documentation.{% endif %} +1. Generate a migration archive for your repository. You must only export one repository at a time. For instructions, see [Exporting migration data from your enterprise]({% ifversion fpt or ghec %}/enterprise-server@latest{% endif %}/migrations/using-ghe-migrator/exporting-migration-data-from-github-enterprise-server){% ifversion ghes %}.{% else %} in the {% data variables.product.prodname_ghe_server %} documentation.{% endif %} 1. Upload your migration archive to your choice of blob storage provider. 1. Generate a short-lived URL for your migration archive which is accessible to {% data variables.product.prodname_dotcom %}, such as an AWS S3 pre-signed URL or Azure Blob Storage SAS URL. 1. Call the `migrate-repo` command with the `--git-archive-url` and `--metadata-archive-url` flags both set to the URL of your archive from the previous step. @@ -192,7 +192,7 @@ After a migration, you may find that your source or destination repositories are The process for unlocking a repository depends on the {% data variables.product.prodname_dotcom %} product where the repository is stored. -* If the locked repository is on {% data variables.product.prodname_ghe_server %}, a site administrator can unlock the repository using the site admin dashboard. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/user-management/managing-repositories-in-your-enterprise/locking-a-repository){% ifversion ghes %}.{% else %} in the {% data variables.product.prodname_ghe_server %} documentation.{% endif %} +* If the locked repository is on {% data variables.product.prodname_ghe_server %}, a site administrator can unlock the repository using the site admin dashboard. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/managing-accounts-and-repositories/managing-repositories-in-your-enterprise/locking-a-repository){% ifversion ghes %}.{% else %} in the {% data variables.product.prodname_ghe_server %} documentation.{% endif %} * If the locked repository is on {% data variables.product.prodname_dotcom_the_website %}, you can contact {% data variables.contact.contact_support %} to unlock the repository. > [!NOTE] diff --git a/content/migrations/using-ghe-migrator/about-ghe-migrator.md b/content/migrations/using-ghe-migrator/about-ghe-migrator.md index 95e1cfa8fcb0..4bc36a472b21 100644 --- a/content/migrations/using-ghe-migrator/about-ghe-migrator.md +++ b/content/migrations/using-ghe-migrator/about-ghe-migrator.md @@ -21,10 +21,10 @@ There are three types of migrations you can perform: * A migration from a {% data variables.product.prodname_ghe_server %} instance to another existing {% data variables.product.prodname_ghe_server %} instance. You can migrate any number of repositories owned by any user or organization on the instance. Before performing a migration, you must have site administrator access to both instances. * A migration from a {% data variables.product.prodname_dotcom_the_website %} organization to a {% data variables.product.prodname_ghe_server %} instance. You can migrate any number of repositories owned by the organization. Before performing a migration, you must have [administrative access](/organizations/managing-peoples-access-to-your-organization-with-roles/roles-in-an-organization) to the {% data variables.product.prodname_dotcom_the_website %} organization as well as site administrator access to the target instance. -* _Trial runs_ are migrations that import data to a [staging instance]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/installation/setting-up-a-github-enterprise-server-instance/setting-up-a-staging-instance). These can be useful to see what _would_ happen if a migration were applied to {% data variables.location.product_location %}. **We strongly recommend that you perform a trial run on a staging instance before importing data to your production instance.** +* _Trial runs_ are migrations that import data to a [staging instance]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/setting-up-a-staging-instance). These can be useful to see what _would_ happen if a migration were applied to {% data variables.location.product_location %}. **We strongly recommend that you perform a trial run on a staging instance before importing data to your production instance.** > [!NOTE] -> The use of ghe-migrator is **not recommended** for transferring a {% data variables.product.prodname_ghe_server %} instance between hypervisors. Instead, we suggest either backing up and restoring to the new location with {% data variables.product.prodname_enterprise_backup_utilities %}, or creating a replica in the new location and then failing over to the replica appliance. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/configuration/configuring-your-enterprise/configuring-backups-on-your-appliance), [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/enterprise-management/configuring-high-availability/creating-a-high-availability-replica) and [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/enterprise-management/configuring-high-availability/initiating-a-failover-to-your-replica-appliance). +> The use of ghe-migrator is **not recommended** for transferring a {% data variables.product.prodname_ghe_server %} instance between hypervisors. Instead, we suggest either backing up and restoring to the new location with {% data variables.product.prodname_enterprise_backup_utilities %}, or creating a replica in the new location and then failing over to the replica appliance. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/backing-up-and-restoring-your-instance/configuring-backups-on-your-instance), [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/monitoring-and-managing-your-instance/configuring-high-availability/creating-a-high-availability-replica) and [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/monitoring-and-managing-your-instance/configuring-high-availability/initiating-a-failover-to-your-replica-appliance). ## Migrated data diff --git a/content/migrations/using-ghe-migrator/exporting-migration-data-from-github-enterprise-server.md b/content/migrations/using-ghe-migrator/exporting-migration-data-from-github-enterprise-server.md index 2b7bac8d7cf1..d1189ad37b63 100644 --- a/content/migrations/using-ghe-migrator/exporting-migration-data-from-github-enterprise-server.md +++ b/content/migrations/using-ghe-migrator/exporting-migration-data-from-github-enterprise-server.md @@ -23,7 +23,7 @@ category: ## Preparing the {% data variables.product.prodname_ghe_server %} source instance -1. Verify that you are a site administrator on the {% data variables.product.prodname_ghe_server %} source. The best way to do this is to verify that you can [SSH into the instance]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/configuration/configuring-your-enterprise/accessing-the-administrative-shell-ssh). +1. Verify that you are a site administrator on the {% data variables.product.prodname_ghe_server %} source. The best way to do this is to verify that you can [SSH into the instance]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/administering-your-instance/administering-your-instance-from-the-command-line/accessing-the-administrative-shell-ssh). 1. {% data reusables.enterprise_migrations.token-generation %} on the {% data variables.product.prodname_ghe_server %} source instance. diff --git a/content/migrations/using-github-enterprise-importer/migrating-between-github-products/migrating-repositories-from-github-enterprise-server-to-github-enterprise-cloud.md b/content/migrations/using-github-enterprise-importer/migrating-between-github-products/migrating-repositories-from-github-enterprise-server-to-github-enterprise-cloud.md index ac65b204a5cc..49a20210f01b 100644 --- a/content/migrations/using-github-enterprise-importer/migrating-between-github-products/migrating-repositories-from-github-enterprise-server-to-github-enterprise-cloud.md +++ b/content/migrations/using-github-enterprise-importer/migrating-between-github-products/migrating-repositories-from-github-enterprise-server-to-github-enterprise-cloud.md @@ -226,7 +226,7 @@ For more information, see [Get an organization migration status](/rest/migration > [!NOTE] > If your migration moves to the `failed` state rather than the `exported` state, try starting the migration again. If the migration fails repeatedly, we recommend generating the archives using `ghe-migrator` instead of the API. > ->Follow the steps in [Exporting migration data from your enterprise]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/user-management/migrating-data-to-and-from-your-enterprise/exporting-migration-data-from-your-enterprise), adding only one repository to the migration. At the end of the process, you will have a single migration archive with your Git source and metadata, and you can move to step 6 in this article. +>Follow the steps in [Exporting migration data from your enterprise]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/migrations/using-ghe-migrator/exporting-migration-data-from-github-enterprise-server), adding only one repository to the migration. At the end of the process, you will have a single migration archive with your Git source and metadata, and you can move to step 6 in this article. After the `state` of a migration moves to `exported`, you can fetch the migration's URL using the "Download an organization migration archive" API. diff --git a/content/organizations/managing-organization-settings/setting-permissions-for-adding-outside-collaborators.md b/content/organizations/managing-organization-settings/setting-permissions-for-adding-outside-collaborators.md index 2bd8e2a760e4..8eaec0ff4978 100644 --- a/content/organizations/managing-organization-settings/setting-permissions-for-adding-outside-collaborators.md +++ b/content/organizations/managing-organization-settings/setting-permissions-for-adding-outside-collaborators.md @@ -26,7 +26,7 @@ By default, anyone with admin access to a repository can invite outside collabor {% endif %} -{% ifversion ghec %}If your organization is owned by an enterprise account, you{% else %}You{% endif %} may not be able to configure this setting for your organization, if an enterprise owner has set a policy at the enterprise level. For more information, see [Enforcing repository management policies in your enterprise]{% ifversion ghec %}(/admin/policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise#enforcing-a-policy-for-inviting-collaborators-to-repositories){% else %}(/admin/policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise#enforcing-a-policy-for-inviting-outside-collaborators-to-repositories){% endif %}. +{% ifversion ghec %}If your organization is owned by an enterprise account, you{% else %}You{% endif %} may not be able to configure this setting for your organization, if an enterprise owner has set a policy at the enterprise level. For more information, see [Enforcing repository management policies in your enterprise]{% ifversion ghec %}(/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise#enforcing-a-policy-for-inviting-outside-collaborators-to-repositories){% else %}(/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise#enforcing-a-policy-for-inviting-collaborators-to-repositories){% endif %}. {% data reusables.organizations.outside-collaborators-use-seats %} diff --git a/content/organizations/managing-user-access-to-your-organizations-repositories/managing-outside-collaborators/converting-an-organization-member-to-an-outside-collaborator.md b/content/organizations/managing-user-access-to-your-organizations-repositories/managing-outside-collaborators/converting-an-organization-member-to-an-outside-collaborator.md index 845d5a024d6b..b5fa2b381d9a 100644 --- a/content/organizations/managing-user-access-to-your-organizations-repositories/managing-outside-collaborators/converting-an-organization-member-to-an-outside-collaborator.md +++ b/content/organizations/managing-user-access-to-your-organizations-repositories/managing-outside-collaborators/converting-an-organization-member-to-an-outside-collaborator.md @@ -22,7 +22,7 @@ category: You can convert a member of an organization to an outside collaborator. For more information about outside collaborators, see [AUTOTITLE](/organizations/managing-user-access-to-your-organizations-repositories/managing-outside-collaborators/adding-outside-collaborators-to-repositories-in-your-organization). -{% ifversion fpt or ghec %}If the organization is owned by an enterprise, converting{% elsif ghes %}Converting{% endif %} an organization member to an outside collaborator may be restricted. For more information, see [Enforcing repository management policies in your enterprise]({% ifversion fpt %}/enterprise-cloud@latest{% endif %}/admin/policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise#enforcing-a-policy-for-inviting-{% ifversion fpt or ghec %}outside-{% endif %}collaborators-to-repositories){% ifversion ghec or ghes %}.{% elsif fpt %} in the {% data variables.product.prodname_ghe_cloud %} documentation.{% endif %} +{% ifversion fpt or ghec %}If the organization is owned by an enterprise, converting{% elsif ghes %}Converting{% endif %} an organization member to an outside collaborator may be restricted. For more information, see [Enforcing repository management policies in your enterprise]({% ifversion fpt %}/enterprise-cloud@latest{% endif %}/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise#enforcing-a-policy-for-inviting-{% ifversion fpt or ghec %}outside-{% endif %}collaborators-to-repositories){% ifversion ghec or ghes %}.{% elsif fpt %} in the {% data variables.product.prodname_ghe_cloud %} documentation.{% endif %} {% data reusables.organizations.outside-collaborators-use-seats %} {% data reusables.organizations.outside_collaborator_forks %} diff --git a/content/pull-requests/reference/status-checks.md b/content/pull-requests/reference/status-checks.md index 312ce0482c70..92f685001858 100644 --- a/content/pull-requests/reference/status-checks.md +++ b/content/pull-requests/reference/status-checks.md @@ -93,11 +93,11 @@ Checks move through statuses as they run, then receive a conclusion when they fi | `expected` | The check run is waiting for a status to be reported. | Yes | | `failure` | The check run failed. | No | | `in_progress` | The check run is in progress. | No | -| `pending` | The check run is at the front of the queue but the [group-based concurrency](/actions/writing-workflows/choosing-what-your-workflow-does/control-the-concurrency-of-workflows-and-jobs) limit has been reached. | Yes | +| `pending` | The check run is at the front of the queue but the [group-based concurrency](/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency) limit has been reached. | Yes | | `queued` | The check run has been queued. | No | | `requested` | The check run has been created but has not been queued. | Yes | | `startup_failure` | The check suite failed during startup. This status is not applicable to check runs. | Yes | -| `waiting` | The check run is waiting for a [deployment protection rule](/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment) to be satisfied. | Yes | +| `waiting` | The check run is waiting for a [deployment protection rule](/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments) to be satisfied. | Yes | When a check has a status of `completed`, it has a conclusion. A successful conclusion usually means the check does not block merging. A failure, timeout, or action-required conclusion usually means someone must review the details before the pull request can merge. diff --git a/data/reusables/enterprise-migration-tool/blob-storage-management-console.md b/data/reusables/enterprise-migration-tool/blob-storage-management-console.md index fc0db11e20f1..04cd6b0704a1 100644 --- a/data/reusables/enterprise-migration-tool/blob-storage-management-console.md +++ b/data/reusables/enterprise-migration-tool/blob-storage-management-console.md @@ -1,4 +1,4 @@ -After you set up an AWS S3 storage bucket or Azure Blob Storage storage account, configure blob storage in the {% data variables.enterprise.management_console %} of {% data variables.location.product_location_enterprise %}. For more information about the {% data variables.enterprise.management_console %}, see [Administering your instance from the Management Console]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/configuration/administering-your-instance-from-the-management-console). +After you set up an AWS S3 storage bucket or Azure Blob Storage storage account, configure blob storage in the {% data variables.enterprise.management_console %} of {% data variables.location.product_location_enterprise %}. For more information about the {% data variables.enterprise.management_console %}, see [Administering your instance from the Management Console]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/administering-your-instance/administering-your-instance-from-the-web-ui). 1. From an administrative account on {% data variables.product.prodname_ghe_server %}, in the upper-right corner of any page, click {% octicon "rocket" aria-label="Site admin" %}. 1. If you're not already on the "Site admin" page, in the upper-left corner, click **Site admin**. @@ -6,7 +6,7 @@ After you set up an AWS S3 storage bucket or Azure Blob Storage storage account, 1. Log into the {% data variables.enterprise.management_console %}. 1. In the top navigation bar, click **Settings**. 1. Under **Migrations**, click **Enable {% data variables.product.company_short %} Migrations**. -1. Optionally, to import storage settings you configured for {% data variables.product.prodname_actions %}, select **Copy Storage settings from Actions**. For more information see, [Enabling GitHub Actions with Azure Blob storage]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/github-actions/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-azure-blob-storage) and [Enabling GitHub Actions with Amazon S3 storage]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/github-actions/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-amazon-s3-storage). +1. Optionally, to import storage settings you configured for {% data variables.product.prodname_actions %}, select **Copy Storage settings from Actions**. For more information see, [Enabling GitHub Actions with Azure Blob storage]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-azure-blob-storage) and [Enabling GitHub Actions with Amazon S3 storage]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-amazon-s3-storage). > [!NOTE] > After copying your storage settings, you may still need to update the configuration of your cloud storage account to work with {% data variables.product.prodname_importer_proper_name %}. In particular, you must ensure that {% data variables.product.prodname_dotcom %}'s IP addresses are allowlisted. For more information, see [AUTOTITLE](/migrations/using-github-enterprise-importer/migrating-between-github-products/managing-access-for-a-migration-between-github-products#configuring-ip-allow-lists-for-migrations). diff --git a/data/reusables/enterprise_installation/ssh-into-instance.md b/data/reusables/enterprise_installation/ssh-into-instance.md index c5d59428cac0..f317a18975f6 100644 --- a/data/reusables/enterprise_installation/ssh-into-instance.md +++ b/data/reusables/enterprise_installation/ssh-into-instance.md @@ -1,4 +1,4 @@ -1. SSH into {% data variables.location.product_location %}. If your instance comprises multiple nodes, for example if high availability or geo-replication are configured, SSH into the primary node. If you use a cluster, you can SSH into any node. Replace HOSTNAME with the hostname for your instance, or the hostname or IP address of a node. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/configuration/configuring-your-enterprise/accessing-the-administrative-shell-ssh). +1. SSH into {% data variables.location.product_location %}. If your instance comprises multiple nodes, for example if high availability or geo-replication are configured, SSH into the primary node. If you use a cluster, you can SSH into any node. Replace HOSTNAME with the hostname for your instance, or the hostname or IP address of a node. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/administering-your-instance/administering-your-instance-from-the-command-line/accessing-the-administrative-shell-ssh). ```shell copy ssh -p 122 admin@HOSTNAME diff --git a/data/reusables/enterprise_installation/ssh-into-target-instance.md b/data/reusables/enterprise_installation/ssh-into-target-instance.md index 560bd1a8c7bc..aafa0b885753 100644 --- a/data/reusables/enterprise_installation/ssh-into-target-instance.md +++ b/data/reusables/enterprise_installation/ssh-into-target-instance.md @@ -1,4 +1,4 @@ -1. SSH into your target {% data variables.product.prodname_ghe_server %} instance. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/configuration/configuring-your-enterprise/accessing-the-administrative-shell-ssh). +1. SSH into your target {% data variables.product.prodname_ghe_server %} instance. For more information, see [AUTOTITLE]({% ifversion not ghes %}/enterprise-server@latest{% endif %}/admin/administering-your-instance/administering-your-instance-from-the-command-line/accessing-the-administrative-shell-ssh). ```shell ssh -p 122 admin@HOSTNAME From cfafc1034262af4cf1d33d8f421e3f447e1db894 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 3 Aug 2026 07:42:48 -0700 Subject: [PATCH 08/11] Double-purge changed content keys to clear the origin shield (#62478) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 11731c34-4a31-4175-ba46-4970237ba75d --- src/workflows/purge-fastly-changed-content.ts | 64 +++++++++++++---- .../tests/purge-fastly-changed-content.ts | 72 +++++++++++++++---- 2 files changed, 108 insertions(+), 28 deletions(-) diff --git a/src/workflows/purge-fastly-changed-content.ts b/src/workflows/purge-fastly-changed-content.ts index d5477c405c9e..14cffcd80c26 100644 --- a/src/workflows/purge-fastly-changed-content.ts +++ b/src/workflows/purge-fastly-changed-content.ts @@ -31,6 +31,24 @@ const COMPARE_FILE_LIMIT = 300 // giving up on it. const PURGE_MAX_RATE_LIMIT_RETRIES = 5 +// Every key is purged twice because of Fastly shielding. A purge doesn't reach +// every POP at the same instant, so a request arriving in between can repopulate +// an already-purged edge node from the not-yet-purged shield, leaving the edge +// holding pre-deploy content again. The second pass evicts that copy. Same +// reasoning as the double purge in purge-fastly.ts; see the "Race conditions" +// section of +// https://www.fastly.com/documentation/guides/concepts/cache/purging#race-conditions +const PURGE_PASSES = 2 + +// How long to wait before the second pass. It has to be long enough that any +// re-populated edge copy already exists, otherwise the second purge runs too +// early and the re-population happens after it. purge-fastly.ts uses the same +// 20s for the same reason: Fastly suggests ~2s, but that has been too short in +// practice. Unlike purge-fastly.ts we don't stagger keys within a pass, because +// that spacing exists to keep whole-language purges from stampeding the backend +// and we only purge the handful of pages that actually changed. +const DELAY_BEFORE_SECOND_PURGE = 20 * 1000 + // Jitter ceiling (ms) added to each backoff so retries that saw the same reset // timestamp don't wake in lockstep and re-burst. const PURGE_JITTER_MS = 150 @@ -248,30 +266,50 @@ async function hardPurgeKeyBatch( } } -// Hard-purge every key in batches of <= 256, one batch at a time. Collects -// failures so one bad batch doesn't drop the rest, then throws at the end if any -// failed so the workflow's failure alerting fires. +// Hard-purge every key in batches of <= 256, one batch at a time, then do it all +// again after a delay to clear anything the origin shield re-populated (see +// PURGE_PASSES). Collects failures so one bad batch doesn't drop the rest, then +// throws at the end if any failed so the workflow's failure alerting fires. export async function hardPurgeSurrogateKeys( keys: string[], fastlyToken: string, serviceId: string, rateLimitDelayFn: (response: Response, attempt: number) => number = rateLimitDelayMs, + sleepFn: (ms: number) => Promise = sleep, ): Promise { const batches = chunk(keys, MAX_KEYS_PER_PURGE) const errors: Error[] = [] - for (const [index, batch] of batches.entries()) { - const label = `batch ${index + 1}/${batches.length} (${batch.length} key(s))` - try { - console.log(`Hard-purging ${label}...`) - await hardPurgeKeyBatch(batch, fastlyToken, serviceId, rateLimitDelayFn) - console.log(`Hard-purged ${label}.`) - } catch (error) { - console.error(error) - errors.push(error instanceof Error ? error : new Error(String(error))) + let attempts = 0 + + const purgeAllBatches = async (pass: number): Promise => { + for (const [index, batch] of batches.entries()) { + const label = + `pass ${pass}/${PURGE_PASSES}, batch ${index + 1}/${batches.length} ` + + `(${batch.length} key(s))` + attempts++ + try { + console.log(`Hard-purging ${label}...`) + await hardPurgeKeyBatch(batch, fastlyToken, serviceId, rateLimitDelayFn) + console.log(`Hard-purged ${label}.`) + } catch (error) { + console.error(error) + errors.push(error instanceof Error ? error : new Error(String(error))) + } + } + } + + for (let pass = 1; pass <= PURGE_PASSES; pass++) { + // A failed first pass still gets a second one: the later attempt may well + // succeed, and giving up here would guarantee stale content. + if (pass > 1) { + console.log(`Waiting ${DELAY_BEFORE_SECOND_PURGE}ms before pass ${pass}...`) + await sleepFn(DELAY_BEFORE_SECOND_PURGE) } + await purgeAllBatches(pass) } + if (errors.length) { - throw new Error(`${errors.length} of ${batches.length} batch purge(s) failed`) + throw new Error(`${errors.length} of ${attempts} batch purge(s) failed`) } } diff --git a/src/workflows/tests/purge-fastly-changed-content.ts b/src/workflows/tests/purge-fastly-changed-content.ts index 0062419ee628..5198938e7492 100644 --- a/src/workflows/tests/purge-fastly-changed-content.ts +++ b/src/workflows/tests/purge-fastly-changed-content.ts @@ -154,6 +154,9 @@ describe('chunk', () => { }) describe('hardPurgeSurrogateKeys', () => { + // Skips the between-pass delay so tests don't wait 20 real seconds. + const noSleep = async () => {} + // A minimal stand-in for a fetch Response, with a case-insensitive headers.get. function fakeResponse( status: number, @@ -170,14 +173,16 @@ describe('hardPurgeSurrogateKeys', () => { } } - test('sends one hard batch purge with a surrogate_keys body (no soft header)', async () => { + test('sends one hard batch purge per pass with a surrogate_keys body (no soft header)', async () => { fetchWithRetry.mockResolvedValue({ ok: true }) await hardPurgeSurrogateKeys( ['language:en,path:a.md', 'language:en,path:b.md'], 'token-123', 'svc-1', + undefined, + noSleep, ) - expect(fetchWithRetry).toHaveBeenCalledTimes(1) + expect(fetchWithRetry).toHaveBeenCalledTimes(2) const [url, init] = fetchWithRetry.mock.calls[0] expect(url).toBe('https://api.fastly.com/service/svc-1/purge') expect(init.method).toBe('POST') @@ -186,46 +191,83 @@ describe('hardPurgeSurrogateKeys', () => { expect(JSON.parse(init.body)).toEqual({ surrogate_keys: ['language:en,path:a.md', 'language:en,path:b.md'], }) + // The second pass repeats the identical batch. + expect(fetchWithRetry.mock.calls[1][1].body).toBe(init.body) + }) + + test('waits between the two passes to let the shield re-populate first', async () => { + fetchWithRetry.mockResolvedValue({ ok: true }) + const waits: number[] = [] + await hardPurgeSurrogateKeys( + ['language:en,path:a.md'], + 'tok', + 'svc', + undefined, + async (ms: number) => { + waits.push(ms) + }, + ) + expect(waits).toEqual([20_000]) }) - test('splits more than 256 keys into multiple batches', async () => { + test('splits more than 256 keys into multiple batches, per pass', async () => { fetchWithRetry.mockResolvedValue({ ok: true }) const keys = Array.from({ length: 257 }, (_unused, i) => `language:en,path:p${i}.md`) - await hardPurgeSurrogateKeys(keys, 'tok', 'svc') - expect(fetchWithRetry).toHaveBeenCalledTimes(2) + await hardPurgeSurrogateKeys(keys, 'tok', 'svc', undefined, noSleep) + // 2 batches x 2 passes. + expect(fetchWithRetry).toHaveBeenCalledTimes(4) expect(JSON.parse(fetchWithRetry.mock.calls[0][1].body).surrogate_keys).toHaveLength(256) expect(JSON.parse(fetchWithRetry.mock.calls[1][1].body).surrogate_keys).toHaveLength(1) + expect(JSON.parse(fetchWithRetry.mock.calls[2][1].body).surrogate_keys).toHaveLength(256) + expect(JSON.parse(fetchWithRetry.mock.calls[3][1].body).surrogate_keys).toHaveLength(1) }) - test('throws if any batch fails, after attempting all of them', async () => { + test('throws if any batch fails, after attempting all of them in both passes', async () => { fetchWithRetry.mockResolvedValueOnce({ ok: true }).mockResolvedValueOnce({ ok: false, status: 500, statusText: 'err', text: async () => 'boom', }) + fetchWithRetry.mockResolvedValue({ ok: true }) const keys = Array.from({ length: 300 }, (_unused, i) => `language:en,path:p${i}.md`) - await expect(hardPurgeSurrogateKeys(keys, 'tok', 'svc')).rejects.toThrow( - /1 of 2 batch purge\(s\) failed/, + await expect(hardPurgeSurrogateKeys(keys, 'tok', 'svc', undefined, noSleep)).rejects.toThrow( + /1 of 4 batch purge\(s\) failed/, ) + expect(fetchWithRetry).toHaveBeenCalledTimes(4) + }) + + test('still runs the second pass when the first one fails outright', async () => { + fetchWithRetry + .mockResolvedValueOnce({ + ok: false, + status: 500, + statusText: 'err', + text: async () => 'boom', + }) + .mockResolvedValue({ ok: true }) + await expect( + hardPurgeSurrogateKeys(['language:en,path:a.md'], 'tok', 'svc', undefined, noSleep), + ).rejects.toThrow(/1 of 2 batch purge\(s\) failed/) expect(fetchWithRetry).toHaveBeenCalledTimes(2) }) test('retries a 429, honoring the hint, then succeeds', async () => { fetchWithRetry .mockResolvedValueOnce(fakeResponse(429, { headers: { 'retry-after': '0' } })) - .mockResolvedValueOnce(fakeResponse(200, { ok: true })) - await hardPurgeSurrogateKeys(['language:en,path:a.md'], 'tok', 'svc', () => 0) - expect(fetchWithRetry).toHaveBeenCalledTimes(2) + .mockResolvedValue(fakeResponse(200, { ok: true })) + await hardPurgeSurrogateKeys(['language:en,path:a.md'], 'tok', 'svc', () => 0, noSleep) + // 429 + retry on the first pass, then one call for the second pass. + expect(fetchWithRetry).toHaveBeenCalledTimes(3) }) test('gives up after the retry budget and reports the batch as failed', async () => { fetchWithRetry.mockResolvedValue(fakeResponse(429, { headers: { 'retry-after': '0' } })) await expect( - hardPurgeSurrogateKeys(['language:en,path:a.md'], 'tok', 'svc', () => 0), - ).rejects.toThrow(/1 of 1 batch purge\(s\) failed/) - // Initial attempt + 5 retries. - expect(fetchWithRetry).toHaveBeenCalledTimes(6) + hardPurgeSurrogateKeys(['language:en,path:a.md'], 'tok', 'svc', () => 0, noSleep), + ).rejects.toThrow(/2 of 2 batch purge\(s\) failed/) + // (Initial attempt + 5 retries) x 2 passes. + expect(fetchWithRetry).toHaveBeenCalledTimes(12) }) }) From c2c2761c62e571f4ce768d6a055dcd4df13e59d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 16:47:15 +0200 Subject: [PATCH 09/11] Bump github/gh-base-image/gh-base-noble from 20260729-114251-gd3f68777e to 20260731-094650-g61ba3829f in the baseimages group (#62583) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1a5ee7b1c2d3..916adde6da48 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ # --------------------------------------------------------------- # To update the sha: # https://github.com/github/gh-base-image/pkgs/container/gh-base-image%2Fgh-base-noble -FROM ghcr.io/github/gh-base-image/gh-base-noble:20260729-114251-gd3f68777e@sha256:3bcf6ef4f1eb8e94d6c61c02b82a78140cc2d3c558ba83c68b8f168c5d678a16 AS base +FROM ghcr.io/github/gh-base-image/gh-base-noble:20260731-094650-g61ba3829f@sha256:965152ebc8311c75bc9db9fc1c178a8c04718ca5d5521c30f55ba40ef229ff4d AS base # Install curl for Node install and determining the early access branch # Install git for cloning docs-early-access & translations repos From 435ad1ba8519b2a8028914c0e07eb2c311879726 Mon Sep 17 00:00:00 2001 From: Logan Rosen Date: Mon, 3 Aug 2026 10:47:52 -0400 Subject: [PATCH 10/11] Prevent Copilot Code Review setup failure (#62577) Copilot-Session: a4cb1484-1fe1-4bb2-a119-e8ec6f1ee6a4 --- .github/workflows/copilot-code-review.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/copilot-code-review.yml diff --git a/.github/workflows/copilot-code-review.yml b/.github/workflows/copilot-code-review.yml new file mode 100644 index 000000000000..b61a8bebbf92 --- /dev/null +++ b/.github/workflows/copilot-code-review.yml @@ -0,0 +1,20 @@ +# Copilot Code Review setup steps +# +# Code Review cannot access the private early-access repository, so it uses +# this secret-free setup instead of the cloud agent setup workflow. + +name: 'Copilot Code Review Setup Steps' + +on: + workflow_dispatch: + +jobs: + copilot-setup-steps: + runs-on: ubuntu-latest + if: github.repository == 'github/docs-internal' || github.repository == 'github/docs' + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: ./.github/actions/node-npm-setup From 992c29abf5222a4c307fd522f709480f9c199f35 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Mon, 3 Aug 2026 16:01:03 +0100 Subject: [PATCH 11/11] Update default filepath for remote addresses (#62593) --- data/reusables/code-scanning/custom-configuration-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/reusables/code-scanning/custom-configuration-file.md b/data/reusables/code-scanning/custom-configuration-file.md index f86fcc044de7..33fe461d96fc 100644 --- a/data/reusables/code-scanning/custom-configuration-file.md +++ b/data/reusables/code-scanning/custom-configuration-file.md @@ -1 +1 @@ -The configuration file can be located within the repository you are analyzing, or in an external repository. Using an external repository allows you to specify configuration options for multiple repositories in a single place. When you reference a configuration file located in an external repository, {% ifversion codeql-config-property %}you can use the `remote=OWNER/REPOSITORY@REF:FILEPATH` syntax. For example, `remote=octo-org/shared@main:codeql-config.yml` will use `codeql-config.yml` from the `main` branch of the `octo-org/shared` repository. All components of this syntax, except for the repository name, are optional. For example, `remote=shared` will use `.github/codeql-action.yaml` from the `main` branch of the `shared` repository in the same organization as the repository being analyzed.{% else %}you can use the `OWNER/REPOSITORY/FILEPATH@REF` syntax. For example, `octo-org/shared/codeql-config.yml@main`.{% endif %} +The configuration file can be located within the repository you are analyzing, or in an external repository. Using an external repository allows you to specify configuration options for multiple repositories in a single place. When you reference a configuration file located in an external repository, {% ifversion codeql-config-property %}you can use the `remote=OWNER/REPOSITORY@REF:FILEPATH` syntax. For example, `remote=octo-org/shared@main:codeql-config.yml` will use `codeql-config.yml` from the `main` branch of the `octo-org/shared` repository. All components of this syntax, except for the repository name, are optional. For example, `remote=shared` will use `.github/codeql-config.yml` from the `main` branch of the `shared` repository in the same organization as the repository being analyzed.{% else %}you can use the `OWNER/REPOSITORY/FILEPATH@REF` syntax. For example, `octo-org/shared/codeql-config.yml@main`.{% endif %}