diff --git a/config/moda/deployment.yaml b/config/moda/deployment.yaml
index b5fc0b0d1d52..c5799ad65f86 100644
--- a/config/moda/deployment.yaml
+++ b/config/moda/deployment.yaml
@@ -15,6 +15,7 @@ environments:
secret_environment: production
required_review_tasks: []
auto_deploy: true
+ skip_auto_merge: true
cluster_selector:
profile: general
region: iad
@@ -26,6 +27,7 @@ environments:
secret_environment: production
required_review_tasks: []
auto_deploy: true
+ skip_auto_merge: true
cluster_selector:
profile: general
region: iad
@@ -37,6 +39,7 @@ environments:
secret_environment: production
required_review_tasks: []
auto_deploy: true
+ skip_auto_merge: true
cluster_selector:
profile: general
region: iad
@@ -48,6 +51,7 @@ environments:
secret_environment: production
required_review_tasks: []
auto_deploy: true
+ skip_auto_merge: true
cluster_selector:
profile: general
region: iad
@@ -59,6 +63,7 @@ environments:
secret_environment: production
required_review_tasks: []
auto_deploy: true
+ skip_auto_merge: true
cluster_selector:
profile: general
region: iad
@@ -70,6 +75,7 @@ environments:
secret_environment: production
required_review_tasks: []
auto_deploy: true
+ skip_auto_merge: true
cluster_selector:
profile: general
region: iad
@@ -81,6 +87,7 @@ environments:
secret_environment: production
required_review_tasks: []
auto_deploy: true
+ skip_auto_merge: true
cluster_selector:
profile: general
region: iad
@@ -92,6 +99,7 @@ environments:
secret_environment: production
required_review_tasks: []
auto_deploy: true
+ skip_auto_merge: true
cluster_selector:
profile: general
region: iad
@@ -103,6 +111,7 @@ environments:
secret_environment: production
required_review_tasks: []
auto_deploy: true
+ skip_auto_merge: true
cluster_selector:
profile: general
region: iad
@@ -114,6 +123,7 @@ environments:
secret_environment: production
required_review_tasks: []
auto_deploy: true
+ skip_auto_merge: true
cluster_selector:
profile: general
region: iad
@@ -125,6 +135,7 @@ environments:
secret_environment: production
required_review_tasks: []
auto_deploy: true
+ skip_auto_merge: true
cluster_selector:
profile: general
region: iad
@@ -136,6 +147,7 @@ environments:
secret_environment: production
required_review_tasks: []
auto_deploy: true
+ skip_auto_merge: true
cluster_selector:
profile: general
region: iad
diff --git a/content/actions/guides.md b/content/actions/guides.md
index 599bc9c98252..3fddd853502d 100644
--- a/content/actions/guides.md
+++ b/content/actions/guides.md
@@ -16,7 +16,7 @@ learningTracks:
includeGuides:
- /actions/writing-workflows/quickstart
- /actions/about-github-actions/understanding-github-actions
- - /actions/sharing-automations/creating-actions/creating-a-docker-container-action
+ - /actions/tutorials/creating-a-docker-container-action
- /actions/writing-workflows/using-workflow-templates
- /actions/use-cases-and-examples/building-and-testing/building-and-testing-python
- /actions/use-cases-and-examples/building-and-testing/building-and-testing-nodejs
@@ -42,8 +42,8 @@ includeGuides:
- /actions/use-cases-and-examples/deploying/deploying-to-amazon-elastic-container-service
- /actions/use-cases-and-examples/deploying/deploying-to-google-kubernetes-engine
- /actions/concepts/workflows-and-actions/about-custom-actions
- - /actions/sharing-automations/creating-actions/creating-a-javascript-action
- - /actions/sharing-automations/creating-actions/creating-a-composite-action
+ - /actions/tutorials/creating-a-javascript-action
+ - /actions/tutorials/creating-a-composite-action
- /actions/migrating-to-github-actions/manually-migrating-to-github-actions/migrating-from-azure-pipelines-to-github-actions
- /actions/migrating-to-github-actions/manually-migrating-to-github-actions/migrating-from-circleci-to-github-actions
- /actions/migrating-to-github-actions/manually-migrating-to-github-actions/migrating-from-gitlab-cicd-to-github-actions
diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/index.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/index.md
index 078c821c3a6c..32cdef956363 100644
--- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/index.md
+++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/index.md
@@ -9,7 +9,6 @@ versions:
topics:
- Actions Runner Controller
children:
- - /quickstart-for-actions-runner-controller
- /authenticating-to-the-github-api
- /deploying-runner-scale-sets-with-actions-runner-controller
- /using-actions-runner-controller-runners-in-a-workflow
diff --git a/content/actions/index.md b/content/actions/index.md
index b26b4ba1cd5c..8e1888ce8646 100644
--- a/content/actions/index.md
+++ b/content/actions/index.md
@@ -49,6 +49,7 @@ children:
- /migrating-to-github-actions
- /administering-github-actions
- /reference
+ - /tutorials
- /guides
---
diff --git a/content/actions/sharing-automations/creating-actions/index.md b/content/actions/sharing-automations/creating-actions/index.md
index ba3a2a9d72f9..1250b3764f61 100644
--- a/content/actions/sharing-automations/creating-actions/index.md
+++ b/content/actions/sharing-automations/creating-actions/index.md
@@ -7,9 +7,6 @@ versions:
ghes: '*'
ghec: '*'
children:
- - /creating-a-docker-container-action
- - /creating-a-javascript-action
- - /creating-a-composite-action
- /setting-exit-codes-for-actions
- /releasing-and-maintaining-actions
- /publishing-actions-in-github-marketplace
diff --git a/content/actions/sharing-automations/creating-actions/creating-a-composite-action.md b/content/actions/tutorials/creating-a-composite-action.md
similarity index 98%
rename from content/actions/sharing-automations/creating-actions/creating-a-composite-action.md
rename to content/actions/tutorials/creating-a-composite-action.md
index c764bdced8db..00e1f64902be 100644
--- a/content/actions/sharing-automations/creating-actions/creating-a-composite-action.md
+++ b/content/actions/tutorials/creating-a-composite-action.md
@@ -1,10 +1,11 @@
---
title: Creating a composite action
shortTitle: Create a composite action
-intro: 'In this guide, you''ll learn how to build a composite action.'
+intro: 'In this tutorial, you''ll learn how to build a composite action.'
redirect_from:
- /actions/creating-actions/creating-a-composite-run-steps-action
- /actions/creating-actions/creating-a-composite-action
+ - /actions/sharing-automations/creating-actions/creating-a-composite-action
versions:
fpt: '*'
ghes: '*'
diff --git a/content/actions/sharing-automations/creating-actions/creating-a-docker-container-action.md b/content/actions/tutorials/creating-a-docker-container-action.md
similarity index 98%
rename from content/actions/sharing-automations/creating-actions/creating-a-docker-container-action.md
rename to content/actions/tutorials/creating-a-docker-container-action.md
index 2b1c11eb34cd..91986b4120a5 100644
--- a/content/actions/sharing-automations/creating-actions/creating-a-docker-container-action.md
+++ b/content/actions/tutorials/creating-a-docker-container-action.md
@@ -1,13 +1,14 @@
---
title: Creating a Docker container action
shortTitle: Create a Docker container action
-intro: 'This guide shows you the minimal steps required to build a Docker container action. '
+intro: 'In this tutorial, you''ll learn how to build a Docker container action.'
redirect_from:
- /articles/creating-a-docker-container-action
- /github/automating-your-workflow-with-github-actions/creating-a-docker-container-action
- /actions/automating-your-workflow-with-github-actions/creating-a-docker-container-action
- /actions/building-actions/creating-a-docker-container-action
- /actions/creating-actions/creating-a-docker-container-action
+ - /actions/sharing-automations/creating-actions/creating-a-docker-container-action
versions:
fpt: '*'
ghes: '*'
diff --git a/content/actions/sharing-automations/creating-actions/creating-a-javascript-action.md b/content/actions/tutorials/creating-a-javascript-action.md
similarity index 98%
rename from content/actions/sharing-automations/creating-actions/creating-a-javascript-action.md
rename to content/actions/tutorials/creating-a-javascript-action.md
index b660ce92fc13..0a22baa7ac0e 100644
--- a/content/actions/sharing-automations/creating-actions/creating-a-javascript-action.md
+++ b/content/actions/tutorials/creating-a-javascript-action.md
@@ -1,13 +1,14 @@
---
title: Creating a JavaScript action
shortTitle: Create a JavaScript action
-intro: 'In this guide, you''ll learn how to build a JavaScript action using the actions toolkit.'
+intro: 'In this tutorial, you''ll learn how to build a JavaScript action using the actions toolkit.'
redirect_from:
- /articles/creating-a-javascript-action
- /github/automating-your-workflow-with-github-actions/creating-a-javascript-action
- /actions/automating-your-workflow-with-github-actions/creating-a-javascript-action
- /actions/building-actions/creating-a-javascript-action
- /actions/creating-actions/creating-a-javascript-action
+ - /actions/sharing-automations/creating-actions/creating-a-javascript-action
versions:
fpt: '*'
ghes: '*'
diff --git a/content/actions/use-cases-and-examples/creating-an-example-workflow.md b/content/actions/tutorials/creating-an-example-workflow.md
similarity index 71%
rename from content/actions/use-cases-and-examples/creating-an-example-workflow.md
rename to content/actions/tutorials/creating-an-example-workflow.md
index c7ca97960bd8..14d6efbf6ddf 100644
--- a/content/actions/use-cases-and-examples/creating-an-example-workflow.md
+++ b/content/actions/tutorials/creating-an-example-workflow.md
@@ -1,6 +1,6 @@
---
title: Creating an example workflow
-intro: Learn how to create a basic workflow that is triggered by a push event.
+intro: 'In this tutorial, you''ll learn how to create a basic workflow that is triggered by a push event.'
versions:
fpt: '*'
ghes: '*'
@@ -11,6 +11,8 @@ topics:
- Workflows
shortTitle: Create an example workflow
layout: inline
+redirect_from:
+ - /actions/use-cases-and-examples/creating-an-example-workflow
---
## Introduction
diff --git a/content/actions/tutorials/index.md b/content/actions/tutorials/index.md
new file mode 100644
index 000000000000..dbb0e567074f
--- /dev/null
+++ b/content/actions/tutorials/index.md
@@ -0,0 +1,14 @@
+---
+title: Tutorials
+intro: "Build skills and knowledge about GitHub Actions through hands-on activities."
+versions:
+ fpt: '*'
+ ghes: '*'
+ ghec: '*'
+children:
+ - /creating-an-example-workflow
+ - /creating-a-docker-container-action
+ - /creating-a-javascript-action
+ - /creating-a-composite-action
+ - /quickstart-for-actions-runner-controller
+---
diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/quickstart-for-actions-runner-controller.md b/content/actions/tutorials/quickstart-for-actions-runner-controller.md
similarity index 96%
rename from content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/quickstart-for-actions-runner-controller.md
rename to content/actions/tutorials/quickstart-for-actions-runner-controller.md
index 071888dd62a1..b7a42996897a 100644
--- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/quickstart-for-actions-runner-controller.md
+++ b/content/actions/tutorials/quickstart-for-actions-runner-controller.md
@@ -1,7 +1,7 @@
---
title: Quickstart for Actions Runner Controller
-shortTitle: Quickstart
-intro: 'Try out {% data variables.product.prodname_actions_runner_controller %} in 5 minutes.'
+shortTitle: Actions Runner Controller
+intro: 'In this tutorial, you''ll try out the basics of {% data variables.product.prodname_actions_runner_controller %}.'
versions:
fpt: '*'
ghec: '*'
@@ -10,6 +10,8 @@ type: quick_start
topics:
- Actions Runner Controller
defaultPlatform: linux
+redirect_from:
+ - /actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/quickstart-for-actions-runner-controller
---
[Legal notice](#legal-notice)
diff --git a/content/actions/use-cases-and-examples/index.md b/content/actions/use-cases-and-examples/index.md
index e8892369bef3..d70d541df699 100644
--- a/content/actions/use-cases-and-examples/index.md
+++ b/content/actions/use-cases-and-examples/index.md
@@ -12,7 +12,6 @@ redirect_from:
- /actions/deployment/deploying-to-your-cloud-provider
- /actions/deployment/deploying-to-your-cloud-provider/deploying-to-azure
children:
- - creating-an-example-workflow
- building-and-testing
- deploying
- publishing-packages
diff --git a/content/admin/data-residency/feature-overview-for-github-enterprise-cloud-with-data-residency.md b/content/admin/data-residency/feature-overview-for-github-enterprise-cloud-with-data-residency.md
index 6e59724b5d47..dd02f1299c03 100644
--- a/content/admin/data-residency/feature-overview-for-github-enterprise-cloud-with-data-residency.md
+++ b/content/admin/data-residency/feature-overview-for-github-enterprise-cloud-with-data-residency.md
@@ -30,6 +30,7 @@ The following features are currently unavailable on {% data variables.enterprise
| {% data variables.product.prodname_copilot_short %} Metrics API | Currently unavailable | [AUTOTITLE](/rest/copilot/copilot-metrics) |
| {% data variables.copilot.copilot_coding_agent %} | Currently unavailable | [AUTOTITLE](/copilot/using-github-copilot/coding-agent/about-assigning-tasks-to-copilot) |
| Restricting {% data variables.product.prodname_actions %} policies to verified creators | Currently unavailable | [AUTOTITLE](/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-github-actions-in-your-enterprise#allow-enterprise-and-select-non-enterprise-actions-and-reusable-workflows) |
+| {% data variables.product.prodname_github_models %} | Currently unavailable | [AUTOTITLE](/github-models/about-github-models) |
| Some features currently in {% data variables.release-phases.public_preview %} or {% data variables.release-phases.private_preview %} | Certain features that are in a preview phase on {% data variables.product.prodname_dotcom_the_website %} may not be available on {% data variables.enterprise.data_residency_site %} | |
## Features that work differently
diff --git a/content/billing/managing-billing-for-your-products/about-billing-for-github-models.md b/content/billing/managing-billing-for-your-products/about-billing-for-github-models.md
new file mode 100644
index 000000000000..77421a9bc967
--- /dev/null
+++ b/content/billing/managing-billing-for-your-products/about-billing-for-github-models.md
@@ -0,0 +1,142 @@
+---
+title: About billing for GitHub Models
+shortTitle: GitHub Models
+intro: If you want to use {% data variables.product.prodname_github_models %} beyond the free usage included in your account, you can choose to opt in to paid usage.
+versions:
+ feature: github-models
+topics:
+ - Enterprise
+ - Billing
+allowTitleToDifferFromFilename: true
+---
+
+> [!NOTE]
+> * {% data variables.product.prodname_github_models %} for organizations and repositories is in {% data variables.release-phases.public_preview %} and subject to change.
+> * Billing for {% data variables.product.prodname_github_models %} is separate from billing for {% data variables.product.prodname_copilot %}. For more information about how models in {% data variables.product.prodname_copilot %} are billed, see [AUTOTITLE](/billing/managing-billing-for-your-products/about-billing-for-github-copilot).
+
+## About {% data variables.product.prodname_github_models %} billing on {% data variables.product.github %}
+
+Billing for {% data variables.product.prodname_github_models %} is designed to be flexible and to allow you to use your preferred model providers, while also providing the ability to control your spending. Model usage is powered by the Azure OpenAI Service and billed through {% data variables.product.github %} using the same global pay-as-you-go pricing as [Azure OpenAI Service](https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/).
+
+Alternatively, you can bring your own API keys (BYOK) from different providers, such as OpenAI or Azure AI, see [AUTOTITLE](/github-models/github-models-at-scale/set-up-custom-model-integration-models-byok). Model inference runs directly through your provider, and usage is billed and tracked through your provider account. For example, Azure API key usage is billed and tracked through your Azure Subscription ID.
+
+Each {% data variables.product.github %} account receives a certain amount of included free but rate-limited usage of {% data variables.product.prodname_github_models %}, see [Rate limits](/github-models/use-github-models/prototyping-with-ai-models#rate-limits).
+
+The use of {% data variables.product.prodname_github_models %} from the {% data variables.product.prodname_marketplace %} catalog is subject to the free rate limits, and is not billed.
+
+## Available models
+
+The table below lists the currently available models. Each model supported by {% data variables.product.prodname_github_models %} has an input and output multiplier that determines the number of token units for each request. For more information on token units, see [Pricing for paid usage](#pricing-for-paid-usage).
+
+| Model name | Input multiplier | Cached input multiplier | Output multiplier | Input price (per 1M token units) | Cached input price (per 1M token units) | Output price (per 1M token units) |
+| --------------------------------- | ---------------- | ----------------------- | ----------------- |----------------------------------|-----------------------------------------|-----------------------------------|
+| OpenAI GPT-4o | 0.25 | 0.125 | 1.0 | $2.50 | $1.25 | $10.00 |
+| OpenAI GPT-4o mini | 0.015 | 0.0075 | 0.06 | $0.15 | $0.08 | $0.60 |
+| OpenAI GPT-4.1-mini | 0.04 | 0.01 | 0.16 | $0.40 | $0.10 | $1.60 |
+| OpenAI GPT-4.1 | 0.2 | 0.05 | 0.8 | $2.00 | $0.50 | $8.00 |
+| Phi-3.5-MoE instruct (128k) | 0.016 | N/A | 0.064 | $0.16 | N/A | $0.64 |
+| Phi-3.5-mini instruct (128k) | 0.013 | N/A | 0.052 | $0.13 | N/A | $0.52 |
+| Phi-3.5-vision instruct (128k) | 0.013 | N/A | 0.052 | $0.13 | N/A | $0.52 |
+| Phi-3-medium instruct (4k) | 0.017 | N/A | 0.068 | $0.17 | N/A | $0.68 |
+| Phi-3-medium instruct (128k) | 0.017 | N/A | 0.068 | $0.17 | N/A | $0.68 |
+| Phi-3-mini instruct (4k) | 0.013 | N/A | 0.052 | $0.13 | N/A | $0.52 |
+| Phi-3-mini instruct (128k) | 0.013 | N/A | 0.052 | $0.13 | N/A | $0.52 |
+| Phi-3-small instruct (8k) | 0.015 | N/A | 0.06 | $0.15 | N/A | $0.60 |
+| Phi-3-small instruct (128k) | 0.015 | N/A | 0.06 | $0.15 | N/A | $0.60 |
+| Phi-4 | 0.0125 | N/A | 0.05 | $0.13 | N/A | $0.50 |
+| Phi-4-mini-instruct | 0.0075 | N/A | 0.03 | $0.08 | N/A | $0.30 |
+| Phi-4-multimodal-instruct | 0.008 | N/A | 0.032 | $0.08 | N/A | $0.32 |
+| DeepSeek-R1 | 0.135 | N/A | 0.54 | $1.35 | N/A | $5.40 |
+| DeepSeek-R1-0528 | 0.135 | N/A | 0.54 | $1.35 | N/A | $5.40 |
+| DeepSeek-V3-0324 | 0.114 | N/A | 0.456 | $1.14 | N/A | $4.56 |
+| MAI-DS-R1 | 0.135 | N/A | 0.54 | $1.35 | N/A | $5.40 |
+| Grok 3 Mini | 0.025 | N/A | 0.127 | $0.25 | N/A | $1.27 |
+| Grok 3 | 0.3 | N/A | 1.5 | $3.00 | N/A | $15.00 |
+| Llama 4 Maverick 17B Instruct FP8 | 0.025 | N/A | 0.1 | $0.25 | N/A | $1.00 |
+| Llama-3.3-70B-Instruct | 0.071 | N/A | 0.071 | $0.71 | N/A | $0.71 |
+
+For accounts that use a custom model with a third-party model provider, billing is managed through the provider and is subject to the provider’s own pricing. For more information, see the official pricing documentation for your chosen provider.
+
+## Opting in to paid usage
+
+> [!NOTE] Once you opt in to paid usage, you will have access to production grade rate limits and be billed for all usage thereafter. For more information about these rate limits, see [Azure AI Foundry Models quotas and limits](https://learn.microsoft.com/en-us/azure/ai-foundry/model-inference/quotas-limits) in the Azure documentation.
+
+Enterprises and organizations can opt in to paid usage to access expanded model capabilities, including increased request allowances and larger context windows. You can manage their spending by setting a budget.
+
+By default, organizations and personal accounts that opt in to paid usage have a spending limit of $0 US dollars (USD) until the budget is increased.
+
+For more information, see [AUTOTITLE](/billing/managing-your-billing/using-budgets-control-spending).
+
+### How do I pay for {% data variables.product.prodname_github_models %}?
+
+{% data variables.product.prodname_github_models %} usage can be paid for by one or more of the following methods:
+
+* For enterprises, organizations, or personal accounts directly billed by {% data variables.product.github %}, the billing of {% data variables.product.prodname_github_models %} is based on your metered usage for each billing period, and pricing varies by the number of model requests, tokens, and the model multiplier.
+ * For invoiced accounts, contact {% data variables.contact.contact_enterprise_sales %} to discuss billing for {% data variables.product.prodname_github_models %} usage.
+* When using custom models with your own API keys, the billing of {% data variables.product.prodname_github_models %} is based on the model provider's pricing, and not {% data variables.product.github %}.
+* Accounts with an existing Azure subscription can use that subscription to pay for model inference by bringing their own API key for custom models. In this case, billing is based on the model provider’s pricing and is managed through the Azure subscription. See [AUTOTITLE](/github-models/github-models-at-scale/set-up-custom-model-integration-models-byok).
+
+> [!TIP]
+> You are considered to be directly billed by {% data variables.product.github %} if you pay for {% data variables.product.github %} using a credit card, PayPal, or by invoice.
+
+## Pricing for paid usage
+
+{% data variables.product.prodname_github_models %} pricing is based on the number of token units used, at a fixed price of $0.00001 USD per token unit.
+
+A token unit is calculated by multiplying the number of input and output tokens by their respective model multipliers. All model usage, regardless of the underlying provider or model, is measured in token units. While some providers display prices per 1,000 or per 1,000,000 tokens, {% data variables.product.prodname_github_models %} standardizes billing to the token unit level. This means you are billed using a single SKU and a unified price per token unit, no matter which supported model you use.
+
+Your cost is calculated by multiplying the number of token units you use by the unified token unit price.
+
+At the end of your billing cycle, {% data variables.product.github %} calculates the cost of token units used, starting from your first request after opting in to paid usage.
+
+The number of model requests and tokens you have used are reset every billing cycle.
+
+### Sample models cost calculation
+
+The following table displays how the total cost is calculated for a request using OpenAI GPT-4o:
+
+| Model | Input tokens used | Output tokens used | Input multiplier | Output multiplier | Total token units | Price per token unit | Total cost |
+|---------------| ----------------- | ------------------ | ---------------- | ----------------- |-------------------|----------------------|------------|
+| OpenAI GPT-4o | 1,000,000 | 1,000,000 | 0.25 | 1 | 1,250,000 | $0.00001 | $12.50 |
+
+### Calculating model costs
+
+The following steps demonstrate how the total cost is calculated:
+
+1. **Calculate input tokens:**
+ Multiply the number of input tokens by the input multiplier.
+ `1,000,000 tokens × 0.25 = 250,000 input token units`
+
+1. **Calculate billable output tokens:**
+ Multiply the number of output tokens by the output multiplier.
+ `1,000,000 tokens × 1 = 1,000,000 output token units`
+
+1. **Add billable tokens:**
+ Add the billable input and output tokens.
+ `250,000 (input) + 1,000,000 (output) = 1,250,000 total token units`
+
+1. **Charges by type:**
+ * **Input charge:** `250,000 × $0.00001 = $2.50`
+ * **Output charge:** `1,000,000 × $0.00001 = $10.00`
+
+1. **Calculate the total cost:**
+ Multiply the total token units by the token unit price.
+ `1,250,000 × $0.00001 = $12.50 for this request`
+
+## Opting out of paid usage
+
+{% data variables.product.prodname_github_models %} billing is disabled by default for enterprises and organizations. An enterprise must enable paid usage before any organization within it can opt in to billing. Once an enterprise or organization has opted in to paid usage, the billing is enabled for all repositories owned by the enterprise or organization, including repositories owned by {% data variables.product.prodname_emus %} (EMUs).
+
+Enterprises control whether paid usage for {% data variables.product.prodname_github_models %} is available to organizations they own. An enterprise must opt in to paid usage before any organization within it can opt in and be billed. If the enterprise disables billing, no organizations in the enterprise can enable or use paid features.
+
+Organizations can opt out of paid usage by setting a budget, or instead choose to disable {% data variables.product.prodname_github_models %} entirely by disabling the product in the organization's settings, see [Controlling model usage in your organization](/github-models/github-models-at-scale/manage-models-at-scale#controlling-model-usage-in-your-organization).
+
+If an enterprise has opted in to billing for {% data variables.product.prodname_github_models %}, but an organization within the enterprise has opted out of billing, then paid {% data variables.product.prodname_github_models %} usage is disabled for the organization, including for repositories owned by {% data variables.product.prodname_emus %} and the enterprise.
+
+For personal repositories, a user's own settings determine whether paid usage is enabled, unless the user is managed by an enterprise (EMU). In that case, the enterprise’s settings apply.
+
+For more information about the billing hierarchy between enterprises and organizations, see [AUTOTITLE](/billing/managing-the-plan-for-your-github-account/about-billing-for-plans).
+
+## Further reading
+
+* [AUTOTITLE](/billing/managing-billing-for-your-products/viewing-your-product-usage)
diff --git a/content/billing/managing-billing-for-your-products/index.md b/content/billing/managing-billing-for-your-products/index.md
index b1a1561490b0..ab2bae3fa790 100644
--- a/content/billing/managing-billing-for-your-products/index.md
+++ b/content/billing/managing-billing-for-your-products/index.md
@@ -14,6 +14,7 @@ children:
- /about-billing-for-github-codespaces
- /about-billing-for-github-copilot
- /about-billing-for-github-packages
+ - /about-billing-for-github-models
- /managing-billing-for-github-advanced-security
- /managing-billing-for-git-large-file-storage
- /managing-billing-for-github-marketplace-apps
diff --git a/content/billing/managing-your-billing/about-the-new-billing-platform.md b/content/billing/managing-your-billing/about-the-new-billing-platform.md
index 385edfe5cfde..2f5cea15ab2c 100644
--- a/content/billing/managing-your-billing/about-the-new-billing-platform.md
+++ b/content/billing/managing-your-billing/about-the-new-billing-platform.md
@@ -32,6 +32,7 @@ The products shown in the new billing platform are determined by your {% data va
* {% data variables.product.prodname_registry %}
* {% data variables.large_files.product_name_long %}
* {% data variables.product.prodname_marketplace %}
+* {% data variables.product.prodname_github_models %}
* {% data variables.product.prodname_sponsors %}
{% endif %}
@@ -42,6 +43,7 @@ The products shown in the new billing platform are determined by your {% data va
* {% data variables.product.prodname_GH_cs_and_sp %} (only available with {% data variables.product.prodname_team %})
* {% data variables.product.prodname_github_codespaces %}
* {% data variables.product.prodname_copilot %}
+* {% data variables.product.prodname_github_models %}
* {% data variables.product.prodname_registry %}
* {% data variables.large_files.product_name_long %}
@@ -54,6 +56,7 @@ The products shown in the new billing platform are determined by your {% data va
* {% data variables.product.prodname_github_codespaces %}
* {% data variables.product.prodname_copilot %}
* {% data variables.product.prodname_enterprise %}
+* {% data variables.product.prodname_github_models %}
* {% data variables.product.prodname_registry %}
* {% data variables.large_files.product_name_long %}
diff --git a/content/github-models/github-models-at-scale/index.md b/content/github-models/github-models-at-scale/index.md
index db7c91c08e65..403015ee5829 100644
--- a/content/github-models/github-models-at-scale/index.md
+++ b/content/github-models/github-models-at-scale/index.md
@@ -6,4 +6,5 @@ versions:
children:
- /use-models-at-scale
- /manage-models-at-scale
+ - /set-up-custom-model-integration-models-byok
---
diff --git a/content/github-models/github-models-at-scale/manage-models-at-scale.md b/content/github-models/github-models-at-scale/manage-models-at-scale.md
index 009d0b3c6e99..cb73a5b2e7ac 100644
--- a/content/github-models/github-models-at-scale/manage-models-at-scale.md
+++ b/content/github-models/github-models-at-scale/manage-models-at-scale.md
@@ -47,4 +47,6 @@ While {% data variables.product.prodname_github_models %} for organizations and
{% data reusables.models.enterprise-change-models-settings %}
+{% data reusables.models.enable-select-models-intro %}
+
{% data reusables.models.enable-select-models-in-org %}
diff --git a/content/github-models/github-models-at-scale/set-up-custom-model-integration-models-byok.md b/content/github-models/github-models-at-scale/set-up-custom-model-integration-models-byok.md
new file mode 100644
index 000000000000..2834d22fa9b0
--- /dev/null
+++ b/content/github-models/github-models-at-scale/set-up-custom-model-integration-models-byok.md
@@ -0,0 +1,62 @@
+---
+title: Using your own API keys in GitHub Models
+shortTitle: Use custom models
+intro: Learn how to integrate your preferred custom models with {% data variables.product.prodname_github_models %} by using your own LLM API keys.
+versions:
+ feature: github-models
+permissions: Organization owners can add custom models to {% data variables.product.prodname_github_models %} for their organization
+topics:
+ - Enterprise
+allowTitleToDifferFromFilename: true
+---
+
+{% data reusables.models.byok-preview-note %}
+
+You can bring your own API keys (BYOK) to {% data variables.product.prodname_github_models %}, and enable teams to use your preferred large language model (LLM) providers across tools like Prompts, Playground, and Models in Actions. For more information about {% data variables.product.prodname_github_models %}, see [AUTOTITLE](/github-models/about-github-models).
+
+To learn about billing and pricing, see [AUTOTITLE](/billing/managing-billing-for-your-products/about-billing-for-github-models).
+
+>[!NOTE] Model support is currently limited to OpenAI and AzureAI.
+
+## Why bring your own API keys?
+
+As an organization owner, you may have specific requirements for governance, data security, and compliance. By setting up your own API keys, you can:
+
+* **Governance and compliance:** Choose LLM providers that comply with your organization's policies and regulatory requirements.
+* **Cost management:** Align with your existing payment methods, contracts, credits, or negotiated rates, and avoid usage overages.
+* **Visibility and control:** Manage which models your team can access, and monitor usage through your provider's existing dashboards and billing.
+* **Flexibility:** Support custom or specialized models that your organization already uses.
+
+## Setting up your API keys to add custom models
+
+> [!IMPORTANT] We highly recommend adhering to the principle of least privilege by assigning only the minimum necessary scopes to your API keys.
+
+You must first add the relevant API keys for the organization. After that, your can specify or enable the custom models you wish to make available to users.
+
+### Adding API keys
+
+{% data reusables.profile.access_org %}
+{% data reusables.profile.org_settings %}
+{% data reusables.organizations.custom-models %}
+1. Click **Add custom key**.
+1. In the "Add a custom key" dialog, provide details about your key. **Name** and **Key** are compulsory fields.
+1. Click **Save**.
+
+## Enabling custom models
+
+{% data reusables.profile.access_org %}
+{% data reusables.profile.org_settings %}
+{% data reusables.organizations.models-development %}
+1. Under "Models permissions", select **All publishers** to enable models added by custom keys.
+ * If this option isn't available, you need to allow the use of the model in the organization. See [AUTOTITLE](/github-models/github-models-at-scale/manage-models-at-scale#controlling-model-usage-in-your-organization).
+1. Optionally, select **Only select models** to create a custom list of enabled or disabled models. This allows you to control which models are available to your organization.
+
+## Next steps
+
+Now that you've enabled your custom models in {% data variables.product.prodname_github_models %}, learn how to:
+
+* Experiment with your custom model in the playground. See [Experimenting with AI models in the playground](/github-models/use-github-models/prototyping-with-ai-models#experimenting-with-ai-models-in-the-playground).
+
+* Store prompts on {% data variables.product.github %} so that you can iterate to fine-tune your prompts, and share them with stakeholders. See [AUTOTITLE](/github-models/use-github-models/storing-prompts-in-github-repositories).
+
+* Launch your AI application. See [Going to production](/github-models/use-github-models/prototyping-with-ai-models#going-to-production)
diff --git a/content/github-models/github-models-at-scale/use-models-at-scale.md b/content/github-models/github-models-at-scale/use-models-at-scale.md
index 837fd3b1b121..b518c7a670a0 100644
--- a/content/github-models/github-models-at-scale/use-models-at-scale.md
+++ b/content/github-models/github-models-at-scale/use-models-at-scale.md
@@ -20,12 +20,13 @@ allowTitleToDifferFromFilename: true
* **Centralized model management:** Control which AI models and providers are available to developers across your organization.
* **AI development at speed:** Quickly prototype, evaluate, and optimize prompts and models.
+* **API access:** Use the {% data variables.product.prodname_github_models %} REST API to automate and integrate with enterprise workflows.
+* **Custom model integration:** Bring your own LLM API keys to connect external or custom models, giving your organization greater flexibility and control over which models are available in {% data variables.product.prodname_github_models %}, whilst keeping aligned with your existing payment methods, credits, and providers.
* **Governance and compliance controls:** Enforce your organization's standards and monitor model usage.
* **Cost optimization:** Avoid unexpected costs from high-priced models.
* **Collaboration:** Share prompts and results using standard {% data variables.product.github %} development practices.
* **Security-focused architecture:** Rest assured that your data remains within {% data variables.product.github %} and Azure and is not shared with model providers.
* **Visual interface:** Allow non-technical team members to contribute alongside developers.
-* **API access:** Use the {% data variables.product.prodname_github_models %} REST API to automate and integrate with enterprise workflows.
* **Version control:** All prompt and model changes go through a standard {% data variables.product.github %} commit and pull request flow so you know when and why a prompt changed.
See [AUTOTITLE](/github-models/about-github-models).
diff --git a/content/github-models/use-github-models/prototyping-with-ai-models.md b/content/github-models/use-github-models/prototyping-with-ai-models.md
index 7f228ed6a2c7..eaf1aa919666 100644
--- a/content/github-models/use-github-models/prototyping-with-ai-models.md
+++ b/content/github-models/use-github-models/prototyping-with-ai-models.md
@@ -10,6 +10,8 @@ redirect_from:
If you want to develop a generative AI application, you can use {% data variables.product.prodname_github_models %} to find and experiment with AI models for free. Once you are ready to bring your application to production, you can switch to a token from a paid Azure account. See the [Azure AI](https://aka.ms/azureai/github-models) documentation.
+Organization owners can integrate their preferred custom models into {% data variables.product.prodname_github_models %}, by using an organization's own LLM API keys. See [AUTOTITLE](/github-models/github-models-at-scale/set-up-custom-model-integration-models-byok).
+
See also [AUTOTITLE](/github-models/responsible-use-of-github-models).
## Finding AI models
@@ -415,5 +417,5 @@ These limits are subject to change without notice.
## Leaving feedback
-To ask questions and share feedback, see this [GitHub Models discussion post](https://github.com/orgs/community/discussions/159087).
+To ask questions and share feedback, see this [GitHub Models discussion post](https://github.com/orgs/community/discussions/159087).
To learn how others are using {% data variables.product.prodname_github_models %}, visit the [GitHub Community discussions for Models](https://github.com/orgs/community/discussions/categories/models).
diff --git a/content/organizations/managing-organization-settings/managing-or-restricting-github-models-for-your-organization.md b/content/organizations/managing-organization-settings/managing-or-restricting-github-models-for-your-organization.md
index 35f377fa9501..60364c9abe39 100644
--- a/content/organizations/managing-organization-settings/managing-or-restricting-github-models-for-your-organization.md
+++ b/content/organizations/managing-organization-settings/managing-or-restricting-github-models-for-your-organization.md
@@ -24,4 +24,6 @@ allowTitleToDifferFromFilename: true
## Enabling or restricting models in your organization
+{% data reusables.models.enable-select-models-intro %}
+
{% data reusables.models.enable-select-models-in-org %}
diff --git a/data/learning-tracks/actions.yml b/data/learning-tracks/actions.yml
index 8ab939c60280..2199e63bb823 100644
--- a/data/learning-tracks/actions.yml
+++ b/data/learning-tracks/actions.yml
@@ -88,9 +88,9 @@ create_actions:
guides:
- /actions/concepts/workflows-and-actions/about-custom-actions
- >-
- /actions/sharing-automations/creating-actions/creating-a-docker-container-action
- - /actions/sharing-automations/creating-actions/creating-a-javascript-action
- - /actions/sharing-automations/creating-actions/creating-a-composite-action
+ /actions/tutorials/creating-a-docker-container-action
+ - /actions/tutorials/creating-a-javascript-action
+ - /actions/tutorials/creating-a-composite-action
- >-
/actions/reference/metadata-syntax-for-github-actions
- >-
diff --git a/data/reusables/models/byok-preview-note.md b/data/reusables/models/byok-preview-note.md
new file mode 100644
index 000000000000..3deb76107c7a
--- /dev/null
+++ b/data/reusables/models/byok-preview-note.md
@@ -0,0 +1,2 @@
+> [!NOTE]
+> The ability to bring your own keys (BYOK) to use custom models with {% data variables.product.prodname_github_models %} for organizations on {% data variables.product.github %} is in {% data variables.release-phases.public_preview %} and subject to change.
diff --git a/data/reusables/models/enable-select-models-in-org.md b/data/reusables/models/enable-select-models-in-org.md
index 998a97d1b29a..34d004440e2f 100644
--- a/data/reusables/models/enable-select-models-in-org.md
+++ b/data/reusables/models/enable-select-models-in-org.md
@@ -1,8 +1,6 @@
-You can choose to enable or disable {% data variables.product.prodname_github_models %} for your organization. You can also choose to only allow the use of selected models or model publishers.
-
{% data reusables.profile.access_org %}
{% data reusables.profile.org_settings %}
-{% data reusables.organizations.models %}
+{% data reusables.organizations.models-development %}
1. Under "Models", in the "Models in your organization" section, click {% octicon "chevron-down" aria-label="the down arrow" %} beside **Disabled** and select **Enabled** from the dropdown.
> [!NOTE]
diff --git a/data/reusables/models/enable-select-models-intro.md b/data/reusables/models/enable-select-models-intro.md
new file mode 100644
index 000000000000..0b7bd7554858
--- /dev/null
+++ b/data/reusables/models/enable-select-models-intro.md
@@ -0,0 +1,3 @@
+You can choose to enable or disable {% data variables.product.prodname_github_models %} for your organization. You can also choose to only allow the use of selected models or model publishers. For more information, see the instructions below.
+
+You can also integrate your preferred external LLM models by bringing your own keys (BYOK) to {% data variables.product.prodname_github_models %}. See [AUTOTITLE](/github-models/github-models-at-scale/set-up-custom-model-integration-models-byok)
diff --git a/data/reusables/organizations/custom-models.md b/data/reusables/organizations/custom-models.md
new file mode 100644
index 000000000000..549e2954e2f5
--- /dev/null
+++ b/data/reusables/organizations/custom-models.md
@@ -0,0 +1 @@
+1. In the "Code, planning, and automation" section of the sidebar, click **{% octicon "ai-model" aria-hidden="true" aria-label="ai-model" %} Models** dropdown. Then click **Custom models**. The "Custom Models" page displays the API keys and custom models added to the organization.
diff --git a/data/reusables/organizations/models-development.md b/data/reusables/organizations/models-development.md
new file mode 100644
index 000000000000..731e98c9db3a
--- /dev/null
+++ b/data/reusables/organizations/models-development.md
@@ -0,0 +1 @@
+1. In the "Code, planning, and automation" section of the sidebar, click the **{% octicon "ai-model" aria-hidden="true" aria-label="ai-model" %} Models** dropdown. Then click **Development**.
diff --git a/data/reusables/organizations/models.md b/data/reusables/organizations/models.md
deleted file mode 100644
index d5aaef5186c8..000000000000
--- a/data/reusables/organizations/models.md
+++ /dev/null
@@ -1 +0,0 @@
-1. In the "Code, planning, and automation" section of the sidebar, click **{% octicon "ai-model" aria-hidden="true" aria-label="ai-model" %} Models**.
diff --git a/src/content-linter/lib/helpers/schema-utils.js b/src/content-linter/lib/helpers/schema-utils.js
index 867084314ba9..95b302bf3eec 100644
--- a/src/content-linter/lib/helpers/schema-utils.js
+++ b/src/content-linter/lib/helpers/schema-utils.js
@@ -33,7 +33,7 @@ export function formatAjvErrors(errors = []) {
// The two most common errors are required and additionalProperties.
// This catches any other with a generic detail that uses the AJV wording.
error.detail = `Frontmatter ${errorObj.message}.`
- error.context = Object.values(errorObj.params.join(''))
+ error.context = Object.values(errorObj.params).join('')
error.errorProperty = error.context
error.searchProperty = error.errorProperty
return error
diff --git a/src/content-linter/lib/linting-rules/early-access-references.js b/src/content-linter/lib/linting-rules/early-access-references.js
index bfcc76e36b65..67839f9e1dc1 100644
--- a/src/content-linter/lib/linting-rules/early-access-references.js
+++ b/src/content-linter/lib/linting-rules/early-access-references.js
@@ -62,7 +62,9 @@ export const frontmatterEarlyAccessReferences = {
// The landing page must link to early-access content so the
// children property doesn't need to be checked in that case.
- if (filepath === 'content/index.md') delete fm.children
+ // Also exclude fixture index files.
+ if (filepath === 'content/index.md' || filepath.includes('fixtures/content/index.md'))
+ delete fm.children
// Convert updated frontmatter back to a string
// to search for 'early-access'.'
diff --git a/src/content-linter/tests/category-pages.ts b/src/content-linter/tests/category-pages.ts
index ff042317614e..fb25caf74868 100644
--- a/src/content-linter/tests/category-pages.ts
+++ b/src/content-linter/tests/category-pages.ts
@@ -139,8 +139,8 @@ describe.skip('category pages', () => {
const articleContents = await fs.promises.readFile(articlePath, 'utf8')
const data = getFrontmatterData(articleContents)
- // Do not include map topics in list of published articles
- if (data.mapTopic || data.hidden) return null
+ // Do not include subcategories in list of published articles
+ if (data.subcategory || data.hidden) return null
// ".../content/github/{category}/{article}.md" => "/{article}"
return `/${path.relative(categoryDir, articlePath).replace(/\.md$/, '')}`
@@ -161,8 +161,8 @@ describe.skip('category pages', () => {
const articleContents = await fs.promises.readFile(articlePath, 'utf8')
const data = getFrontmatterData(articleContents)
- // Do not include map topics nor hidden pages in list of available articles
- if (data.mapTopic || data.hidden) return null
+ // Do not include subcategories nor hidden pages in list of available articles
+ if (data.subcategory || data.hidden) return null
// ".../content/github/{category}/{article}.md" => "/{article}"
return `/${path.relative(categoryDir, articlePath).replace(/\.md$/, '')}`
@@ -195,7 +195,7 @@ describe.skip('category pages', () => {
expect(unexpectedArticles.length, errorMessage).toBe(0)
})
- test('contains only articles and map topics with versions that are also available in the parent category', () => {
+ test('contains only articles and subcategories with versions that are also available in the parent category', () => {
Object.entries(articleVersions).forEach(([articleName, articleVersions]) => {
const unexpectedVersions = difference(articleVersions, categoryVersions)
const errorMessage = `${articleName} has versions that are not available in parent category`
diff --git a/src/content-render/scripts/reconcile-category-dirs-with-ids.js b/src/content-render/scripts/reconcile-category-dirs-with-ids.js
index de9c069a0a8b..9381a17c171b 100755
--- a/src/content-render/scripts/reconcile-category-dirs-with-ids.js
+++ b/src/content-render/scripts/reconcile-category-dirs-with-ids.js
@@ -23,13 +23,13 @@ const slugger = new GithubSlugger()
const contentDir = path.join(ROOT, 'content')
-const INCLUDE_MAP_TOPICS = Boolean(JSON.parse(process.env.INCLUDE_MAP_TOPICS || 'false'))
+const INCLUDE_SUBCATEGORIES = Boolean(JSON.parse(process.env.INCLUDE_SUBCATEGORIES || 'false'))
main()
async function main() {
const englishCategoryIndices = getEnglishCategoryIndices().filter((name) => {
- return INCLUDE_MAP_TOPICS || name.split(path.sep).length < 5
+ return INCLUDE_SUBCATEGORIES || name.split(path.sep).length < 5
})
const shouldRename = []
diff --git a/src/content-render/tests/render-changed-and-deleted-files.js b/src/content-render/tests/render-changed-and-deleted-files.js
index 5a38365df751..73e4029270cd 100644
--- a/src/content-render/tests/render-changed-and-deleted-files.js
+++ b/src/content-render/tests/render-changed-and-deleted-files.js
@@ -127,7 +127,7 @@ describe('deleted-content', () => {
? `The deleted file ${file} did not set up a redirect when deleted.`
: ''
// Certain articles that are deleted and moved under a directory with the same article name
- // should just route to the map topic page instead of redirecting (docs content team confirmed).
+ // should just route to the subcategory page instead of redirecting (docs content team confirmed).
// So, in this scenario, we'd get a 200 status code.
expect(res.statusCode === 301 || res.statusCode === 200, error).toBe(true)
})
diff --git a/src/dev-toc/layout.html b/src/dev-toc/layout.html
index 0aa79e197159..2ecec00cb739 100644
--- a/src/dev-toc/layout.html
+++ b/src/dev-toc/layout.html
@@ -39,11 +39,11 @@
TOC for {{ allVersions[currentVersion].versio
{{ categoryPage.renderedFullTitle }}
- {% for maptopicPage in categoryPage.childPages %}
+ {% for subcategoryPage in categoryPage.childPages %}
-
- {{ maptopicPage.renderedFullTitle }}
+ {{ subcategoryPage.renderedFullTitle }}
- {% for articlePage in maptopicPage.childPages %}
+ {% for articlePage in subcategoryPage.childPages %}
-
{{ articlePage.renderedFullTitle }}
diff --git a/src/events/lib/get-document-type.ts b/src/events/lib/get-document-type.ts
index 1c04ae367c5b..1fca81649eba 100644
--- a/src/events/lib/get-document-type.ts
+++ b/src/events/lib/get-document-type.ts
@@ -1,7 +1,7 @@
/**
* Document types used by the system
*/
-type DocumentType = 'homepage' | 'product' | 'category' | 'mapTopic' | 'article' | 'early-access'
+type DocumentType = 'homepage' | 'product' | 'category' | 'subcategory' | 'article' | 'early-access'
/**
* This function derives the document type from the *relative path* segment length,
@@ -20,17 +20,17 @@ export default function getDocumentType(relativePath: string): DocumentType {
// Early Access has an extra tree segment, so it has a different number of segments.
const isEarlyAccess = relativePath.startsWith('early-access')
- const publicDocs: DocumentType[] = ['homepage', 'product', 'category', 'mapTopic']
+ const publicDocs: DocumentType[] = ['homepage', 'product', 'category', 'subcategory']
const earlyAccessDocs: DocumentType[] = [
'homepage',
'early-access',
'product',
'category',
- 'mapTopic',
+ 'subcategory',
]
- // Anything beyond the largest depth is assumed to be a mapTopic
+ // Anything beyond the largest depth is assumed to be a subcategory
return isEarlyAccess
? earlyAccessDocs[Math.min(segmentLength, earlyAccessDocs.length) - 1]
: publicDocs[Math.min(segmentLength, publicDocs.length) - 1]
diff --git a/src/events/lib/schema.ts b/src/events/lib/schema.ts
index 9446440d99e9..2c4efa7b92ed 100644
--- a/src/events/lib/schema.ts
+++ b/src/events/lib/schema.ts
@@ -89,7 +89,7 @@ const context = {
page_document_type: {
type: 'string',
description: 'The generic page document type based on URL path.',
- enum: ['homepage', 'early-access', 'product', 'category', 'mapTopic', 'article'], // get-document-type.js
+ enum: ['homepage', 'early-access', 'product', 'category', 'subcategory', 'article'], // get-document-type.js
},
page_type: {
type: 'string',
diff --git a/src/fixtures/fixtures/article-with-mislocalized-frontmatter.md b/src/fixtures/fixtures/article-with-mislocalized-frontmatter.md
index 41127d2a182f..48f7ca2513bd 100644
--- a/src/fixtures/fixtures/article-with-mislocalized-frontmatter.md
+++ b/src/fixtures/fixtures/article-with-mislocalized-frontmatter.md
@@ -1,7 +1,7 @@
---
title: GitHub 上でプロジェクトを探索する
intro: ''
-mapTopic: verdadero
+subcategory: true
versions:
free-pro-team: '*'
enterprise-server: '*'
diff --git a/src/fixtures/fixtures/content/actions/category/index.md b/src/fixtures/fixtures/content/actions/category/index.md
index b03662c1dc50..5c55b65eab5f 100644
--- a/src/fixtures/fixtures/content/actions/category/index.md
+++ b/src/fixtures/fixtures/content/actions/category/index.md
@@ -8,5 +8,5 @@ versions:
ghes: '*'
ghec: '*'
children:
- - /map-topic
+ - /subcategory
---
diff --git a/src/fixtures/fixtures/content/actions/category/map-topic/article.md b/src/fixtures/fixtures/content/actions/category/subcategory/article.md
similarity index 100%
rename from src/fixtures/fixtures/content/actions/category/map-topic/article.md
rename to src/fixtures/fixtures/content/actions/category/subcategory/article.md
diff --git a/src/fixtures/fixtures/content/actions/category/map-topic/index.md b/src/fixtures/fixtures/content/actions/category/subcategory/index.md
similarity index 71%
rename from src/fixtures/fixtures/content/actions/category/map-topic/index.md
rename to src/fixtures/fixtures/content/actions/category/subcategory/index.md
index 8b2bac282eac..2992f71351a4 100644
--- a/src/fixtures/fixtures/content/actions/category/map-topic/index.md
+++ b/src/fixtures/fixtures/content/actions/category/subcategory/index.md
@@ -1,6 +1,6 @@
---
-title: Map Topic page about Actions
-shortTitle: Map & Topic
+title: Subcategory page about Actions
+shortTitle: Subcategory
intro: "Here's the intro for {% data variables.product.prodname_actions %}."
versions:
fpt: '*'
diff --git a/src/fixtures/fixtures/content/index.md b/src/fixtures/fixtures/content/index.md
index 85e3fe6f9798..1111d6611fad 100644
--- a/src/fixtures/fixtures/content/index.md
+++ b/src/fixtures/fixtures/content/index.md
@@ -10,7 +10,7 @@ featuredLinks:
- /get-started/foo/bar
- /pages
- '{% ifversion ghec %}/get-started/versioning/only-ghec{% endif %}'
- - /actions/category/map-topic
+ - /actions/category/subcategory
redirect_from:
- /olden-days
versions:
diff --git a/src/fixtures/tests/breadcrumbs.ts b/src/fixtures/tests/breadcrumbs.ts
index 41bc836ea1d5..4a69e64c139b 100644
--- a/src/fixtures/tests/breadcrumbs.ts
+++ b/src/fixtures/tests/breadcrumbs.ts
@@ -26,7 +26,7 @@ describe('breadcrumbs', () => {
expect(links.text()).toBe('Bar')
})
- test('article pages have breadcrumbs in article with product, category, maptopic, and article and last breadcrumb is not viewable', async () => {
+ test('article pages have breadcrumbs in article with product, category, subcategory, and article and last breadcrumb is not viewable', async () => {
const $ = await getDOM('/get-started/start-your-journey/hello-world')
const links = $('[data-testid=breadcrumbs-in-article] a')
expect(links.length).toBe(3)
diff --git a/src/fixtures/tests/categories-and-map-topic.js b/src/fixtures/tests/categories-and-subcategory.js
similarity index 88%
rename from src/fixtures/tests/categories-and-map-topic.js
rename to src/fixtures/tests/categories-and-subcategory.js
index 2db95734cd56..00755bc4c3d5 100644
--- a/src/fixtures/tests/categories-and-map-topic.js
+++ b/src/fixtures/tests/categories-and-subcategory.js
@@ -2,8 +2,8 @@ import { describe, expect, test } from 'vitest'
import { getDOM, head } from '#src/tests/helpers/e2etest.js'
-describe('map topics', () => {
- test('get-started/start-your-journey map-topic', async () => {
+describe('subcategories', () => {
+ test('get-started/start-your-journey subcategory', async () => {
const $ = await getDOM('/get-started/start-your-journey')
const lead = $('[data-search=lead]').text()
expect(lead).toMatch('Get started using HubGit to manage Git repositories')
@@ -20,8 +20,8 @@ describe('map topics', () => {
expect(responses.every((r) => r.statusCode === 200)).toBeTruthy()
})
- test('actions/category/map-topic map-topic has its articles intro', async () => {
- const $ = await getDOM('/actions/category/map-topic')
+ test('actions/category/subcategory subcategory has its articles intro', async () => {
+ const $ = await getDOM('/actions/category/subcategory')
const lead = $('[data-search=lead]').text()
expect(lead).toMatch("Here's the intro for HubGit Actions.")
@@ -41,7 +41,7 @@ describe('map topics', () => {
})
describe('categories', () => {
- test('actions/category map-topic', async () => {
+ test('actions/category subcategory', async () => {
const $ = await getDOM('/actions/category')
const lead = $('[data-search=lead]').text()
expect(lead).toMatch('Learn how to migrate your existing CI/CD')
diff --git a/src/fixtures/tests/internal-links.js b/src/fixtures/tests/internal-links.js
index 549678ceceb1..88a3ad9b1c51 100644
--- a/src/fixtures/tests/internal-links.js
+++ b/src/fixtures/tests/internal-links.js
@@ -129,7 +129,7 @@ describe('link-rewriting', () => {
})
})
-describe('map-topic links', () => {
+describe('subcategory links', () => {
test('no free-pro-team prefix', async () => {
const $ = await getDOM('/rest/actions')
const links = $('[data-testid="table-of-contents"] a[href]')
diff --git a/src/fixtures/tests/page-titles.js b/src/fixtures/tests/page-titles.js
index 6cdd1b703092..6428e2bfed4f 100644
--- a/src/fixtures/tests/page-titles.js
+++ b/src/fixtures/tests/page-titles.js
@@ -21,7 +21,7 @@ describe('page titles', () => {
)
})
- test('fpt map topic page', async () => {
+ test('fpt subcategory page', async () => {
const $ = await getDOM('/en/get-started/start-your-journey')
expect($('title').text()).toBe('Start your journey - GitHub Docs')
})
diff --git a/src/fixtures/tests/playwright-a11y.spec.ts b/src/fixtures/tests/playwright-a11y.spec.ts
index 673aeb5ec370..c24dd9554e32 100644
--- a/src/fixtures/tests/playwright-a11y.spec.ts
+++ b/src/fixtures/tests/playwright-a11y.spec.ts
@@ -9,7 +9,7 @@ const pages: { [key: string]: string } = {
homepage: '/',
learningPath:
'/code-security/getting-started/quickstart?learn=foo_bar&learnProduct=code-security',
- mapAndTopic: '/actions/category/map-topic',
+ mapAndTopic: '/actions/category/subcategory',
procedural: '/get-started/images/images-in-lists',
productLanding: '/code-security',
restCategory: '/rest/actions/artifacts',
diff --git a/src/fixtures/tests/playwright-rendering.spec.ts b/src/fixtures/tests/playwright-rendering.spec.ts
index 660869fd07bd..a190562b72ee 100644
--- a/src/fixtures/tests/playwright-rendering.spec.ts
+++ b/src/fixtures/tests/playwright-rendering.spec.ts
@@ -302,18 +302,18 @@ test.describe('tool picker', () => {
})
})
-test('navigate with side bar into article inside a map-topic inside a category', async ({
+test('navigate with side bar into article inside a subcategory inside a category', async ({
page,
}) => {
// Our TreeView sidebar only shows "2 levels". If you click and expand
- // the category, you'll be able to see the map-topic and the article
+ // the category, you'll be able to see the subcategory and the article
// within.
await page.goto('/actions')
- await page.getByTestId('sidebar').getByText('Category').click()
- await page.getByText('Map & Topic').click()
+ await page.getByTestId('sidebar').getByText('Category', { exact: true }).click()
+ await page.getByTestId('sidebar').getByText('Subcategory').click()
await page.getByText('').click()
await expect(page.getByRole('heading', { name: 'Article title' })).toBeVisible()
- await expect(page).toHaveURL(/actions\/category\/map-topic\/article/)
+ await expect(page).toHaveURL(/actions\/category\/subcategory\/article/)
})
test.describe('hover cards', () => {
diff --git a/src/frame/components/context/MainContext.tsx b/src/frame/components/context/MainContext.tsx
index 38e1639e799a..7d7765e133cc 100644
--- a/src/frame/components/context/MainContext.tsx
+++ b/src/frame/components/context/MainContext.tsx
@@ -90,7 +90,7 @@ export type MainContextT = {
breadcrumbs: {
product: BreadcrumbT
category?: BreadcrumbT
- maptopic?: BreadcrumbT
+ subcategory?: BreadcrumbT
article?: BreadcrumbT
}
communityRedirect: {
diff --git a/src/frame/lib/frontmatter.js b/src/frame/lib/frontmatter.js
index a5aabd505dc8..79f8e55b3bac 100644
--- a/src/frame/lib/frontmatter.js
+++ b/src/frame/lib/frontmatter.js
@@ -53,7 +53,7 @@ export const schema = {
minimum: 2,
maximum: 4,
},
- mapTopic: {
+ subcategory: {
type: 'boolean',
},
// allow hidden articles under `early-access`
@@ -65,7 +65,7 @@ export const schema = {
type: 'boolean',
},
// specify whether an Early Access product should have a table of contents
- // (EA categories and map topics have them by default, but products don't)
+ // (EA categories and subcategories have them by default, but products don't)
earlyAccessToc: {
type: 'boolean',
},
diff --git a/src/frame/lib/get-toc-items.js b/src/frame/lib/get-toc-items.js
index 78a04ba53d6e..6d000be55419 100644
--- a/src/frame/lib/get-toc-items.js
+++ b/src/frame/lib/get-toc-items.js
@@ -6,7 +6,7 @@ const productTOCs = Object.values(productMap)
const linkString = /{% [^}]*?link.*? \/(.*?) ?%}/m
const linksArray = new RegExp(linkString.source, 'gm')
-// return an array of objects like { type: 'category|maptopic|article', href: 'path' }
+// return an array of objects like { type: 'category|subcategory|article', href: 'path' }
export default function getTocItems(page) {
// only process product and category tocs
if (!page.relativePath.endsWith('index.md')) return
@@ -27,11 +27,11 @@ export default function getTocItems(page) {
const tocItem = {}
// a product's toc items are always categories
- // whereas a category's toc items can be either maptopics or articles
+ // whereas a category's toc items can be either subcategories or articles
tocItem.type = productTOCs.includes(page.relativePath)
? 'category'
: item.includes('topic_')
- ? 'maptopic'
+ ? 'subcategory'
: 'article'
tocItem.href = item.match(linkString)[1]
diff --git a/src/frame/middleware/context/current-product-tree.ts b/src/frame/middleware/context/current-product-tree.ts
index ac357799d0ad..e2e160e63b18 100644
--- a/src/frame/middleware/context/current-product-tree.ts
+++ b/src/frame/middleware/context/current-product-tree.ts
@@ -43,7 +43,7 @@ export default async function currentProductTree(
// First make a slim tree of just the 'href', 'title', 'shortTitle'
// 'documentType' and 'childPages' (which is recursive).
- // This gets used for map topic and category pages.
+ // This gets used for subcategory and category pages.
req.context.currentProductTreeTitles = await getCurrentProductTreeTitles(
req.context.currentProductTree,
req.context,
diff --git a/src/frame/middleware/context/generic-toc.ts b/src/frame/middleware/context/generic-toc.ts
index 5298f254b426..ccabe2470cc7 100644
--- a/src/frame/middleware/context/generic-toc.ts
+++ b/src/frame/middleware/context/generic-toc.ts
@@ -4,7 +4,7 @@ import type { ExtendedRequest, Context, Tree, ToC } from '@/types'
import findPageInSiteTree from '@/frame/lib/find-page-in-site-tree.js'
// This module adds either flatTocItems or nestedTocItems to the context object for
-// product, category, and map topic TOCs that don't have other layouts specified.
+// product, category, and subcategory TOCs that don't have other layouts specified.
// They are rendered by includes/generic-toc-flat.html or includes/generic-toc-nested.html.
export default async function genericToc(req: ExtendedRequest, res: Response, next: NextFunction) {
if (!req.context) throw new Error('request not contextualized')
@@ -14,7 +14,7 @@ export default async function genericToc(req: ExtendedRequest, res: Response, ne
req.context.currentLayoutName !== 'category-landing'
)
return next()
- // This middleware can only run on product, category, and map topics.
+ // This middleware can only run on product, category, and subcategories.
if (
req.context.page.documentType === 'homepage' ||
req.context.page.documentType === 'article' ||
@@ -29,7 +29,7 @@ export default async function genericToc(req: ExtendedRequest, res: Response, ne
const tocTypes: Record = {
product: 'flat',
category: 'nested',
- mapTopic: 'flat',
+ subcategory: 'flat',
}
// Frontmatter can optionally be set on an Early Access product to show hidden child items.
@@ -47,32 +47,32 @@ export default async function genericToc(req: ExtendedRequest, res: Response, ne
req.pagePath,
)
- let fauxMapTopic = false
+ let fauxSubcategory = false
if (req.context.page.documentType === 'category' && req.context.page.autogenerated !== 'rest') {
// But does *have* children?
const hasGrandchildren = (treePage.childPages || []).some((child) => child.children)
- fauxMapTopic = !hasGrandchildren
+ fauxSubcategory = !hasGrandchildren
}
// Find the current TOC type based on the current document type.
const currentTocType = earlyAccessToc
? 'nested'
- : fauxMapTopic
+ : fauxSubcategory
? 'flat'
: tocTypes[req.context.page.documentType]
// By default, only include hidden child items on a TOC page if it's an Early Access category or
- // map topic page, not a product or 'articles' fake category page (e.g., /early-access/github/articles).
+ // subcategory page, not a product or 'articles' fake category page (e.g., /early-access/github/articles).
// This is because we don't want entire EA product TOCs to be publicly browseable, but anything at the category
// or below level is fair game because that content is scoped to specific features.
- const isCategoryOrMapTopic =
- req.context.page.documentType === 'category' || req.context.page.documentType === 'mapTopic'
+ const isCategoryOrSubcategory =
+ req.context.page.documentType === 'category' || req.context.page.documentType === 'subcategory'
if (!req.context.currentPath) throw new Error('currentPath not in context')
const isEarlyAccess = req.context.currentPath.includes('/early-access/')
const isArticlesCategory = req.context.currentPath.endsWith('/articles')
const includeHidden =
- earlyAccessToc || (isCategoryOrMapTopic && isEarlyAccess && !isArticlesCategory)
+ earlyAccessToc || (isCategoryOrSubcategory && isEarlyAccess && !isArticlesCategory)
// Conditionally run getTocItems() recursively.
let isRecursive
@@ -90,7 +90,7 @@ export default async function genericToc(req: ExtendedRequest, res: Response, ne
})
}
- // Get an array of child map topics and their child articles and add it to the context object.
+ // Get an array of child subcategories and their child articles and add it to the context object.
if (currentTocType === 'nested' || isOneOffProductToc) {
isRecursive = !isOneOffProductToc
renderIntros = false
diff --git a/src/frame/tests/page.js b/src/frame/tests/page.js
index b95a50b730ad..3619c7678655 100644
--- a/src/frame/tests/page.js
+++ b/src/frame/tests/page.js
@@ -63,7 +63,7 @@ describe('Page class', () => {
expect(articleWithFM.showMiniToc).toBe(false)
})
- // products, categories, and map topics have index.md pages
+ // products, categories, and subcategories have index.md pages
test('is undefined by default on index.md pages', () => {
expect(tocPage.showMiniToc).toBeUndefined()
})
diff --git a/src/frame/tests/server.js b/src/frame/tests/server.js
index 91c1a8617383..57d926bec414 100644
--- a/src/frame/tests/server.js
+++ b/src/frame/tests/server.js
@@ -122,7 +122,7 @@ describe('server', () => {
})
// see issue 9678
- test('does not use cached intros in map topics', async () => {
+ test('does not use cached intros in subcategories', async () => {
let $ = await getDOM(
'/en/get-started/importing-your-projects-to-github/importing-source-code-to-github/importing-a-git-repository-using-the-command-line',
)
@@ -130,8 +130,8 @@ describe('server', () => {
$ = await getDOM(
'/en/enterprise/2.16/user/importing-your-projects-to-github/importing-source-code-to-github',
)
- const mapTopicIntro = $('.map-topic').first().next().text()
- expect(articleIntro).not.toEqual(mapTopicIntro)
+ const subcategoryIntro = $('.subcategory').first().next().text()
+ expect(articleIntro).not.toEqual(subcategoryIntro)
})
test('serves /categories.json for support team usage', async () => {
diff --git a/src/graphql/data/fpt/changelog.json b/src/graphql/data/fpt/changelog.json
index cbbee13e43bd..f3084d580318 100644
--- a/src/graphql/data/fpt/changelog.json
+++ b/src/graphql/data/fpt/changelog.json
@@ -1,4 +1,17 @@
[
+ {
+ "schemaChanges": [
+ {
+ "title": "The GraphQL schema includes these changes:",
+ "changes": [
+ "Field name was added to object type Mannequin
"
+ ]
+ }
+ ],
+ "previewChanges": [],
+ "upcomingChanges": [],
+ "date": "2025-06-25"
+ },
{
"schemaChanges": [
{
diff --git a/src/graphql/data/fpt/schema.docs.graphql b/src/graphql/data/fpt/schema.docs.graphql
index 61070e97eb39..2df1fabe9e6b 100644
--- a/src/graphql/data/fpt/schema.docs.graphql
+++ b/src/graphql/data/fpt/schema.docs.graphql
@@ -21452,6 +21452,11 @@ type Mannequin implements Actor & Node & UniformResourceLocatable {
"""
login: String!
+ """
+ The display name of the imported mannequin.
+ """
+ name: String
+
"""
The HTML path to this resource.
"""
diff --git a/src/graphql/data/fpt/schema.json b/src/graphql/data/fpt/schema.json
index d10420bd042a..700fc07d11fd 100644
--- a/src/graphql/data/fpt/schema.json
+++ b/src/graphql/data/fpt/schema.json
@@ -32705,6 +32705,14 @@
"kind": "scalars",
"href": "/graphql/reference/scalars#string"
},
+ {
+ "name": "name",
+ "description": "The display name of the imported mannequin.
",
+ "type": "String",
+ "id": "string",
+ "kind": "scalars",
+ "href": "/graphql/reference/scalars#string"
+ },
{
"name": "resourcePath",
"description": "The HTML path to this resource.
",
diff --git a/src/graphql/data/ghec/schema.docs.graphql b/src/graphql/data/ghec/schema.docs.graphql
index 61070e97eb39..2df1fabe9e6b 100644
--- a/src/graphql/data/ghec/schema.docs.graphql
+++ b/src/graphql/data/ghec/schema.docs.graphql
@@ -21452,6 +21452,11 @@ type Mannequin implements Actor & Node & UniformResourceLocatable {
"""
login: String!
+ """
+ The display name of the imported mannequin.
+ """
+ name: String
+
"""
The HTML path to this resource.
"""
diff --git a/src/graphql/data/ghec/schema.json b/src/graphql/data/ghec/schema.json
index d10420bd042a..700fc07d11fd 100644
--- a/src/graphql/data/ghec/schema.json
+++ b/src/graphql/data/ghec/schema.json
@@ -32705,6 +32705,14 @@
"kind": "scalars",
"href": "/graphql/reference/scalars#string"
},
+ {
+ "name": "name",
+ "description": "The display name of the imported mannequin.
",
+ "type": "String",
+ "id": "string",
+ "kind": "scalars",
+ "href": "/graphql/reference/scalars#string"
+ },
{
"name": "resourcePath",
"description": "The HTML path to this resource.
",
diff --git a/src/search/lib/get-elasticsearch-results/general-search.ts b/src/search/lib/get-elasticsearch-results/general-search.ts
index 263a7b787739..8b687f336e54 100644
--- a/src/search/lib/get-elasticsearch-results/general-search.ts
+++ b/src/search/lib/get-elasticsearch-results/general-search.ts
@@ -314,7 +314,7 @@ function getMatchQueries(
],
)
// If the content is short, it is given a disproportionate advantage
- // in search ranking. For example, our category and map-topic pages
+ // in search ranking. For example, our category and subcategory pages
// often includes a list of other document titles but because it's so
// short it thinks that content is really relevant. This only applies
// when you use `match_phrase_prefix` which first makes a search
diff --git a/src/search/scripts/scrape/lib/parse-page-sections-into-records.ts b/src/search/scripts/scrape/lib/parse-page-sections-into-records.ts
index 8bee4c2a237b..847c8b817113 100644
--- a/src/search/scripts/scrape/lib/parse-page-sections-into-records.ts
+++ b/src/search/scripts/scrape/lib/parse-page-sections-into-records.ts
@@ -26,7 +26,7 @@ export default function parsePageSectionsIntoRecords(page: any): Record {
// On an article page, we the breadcrumbs array will be something
// like:
//
- // ['Product short title', 'Map topic', 'Article title']
+ // ['Product short title', 'Subcategory', 'Article title']
//
// But on a product landing page, it'll just be:
//
diff --git a/src/search/tests/fixtures/page-without-body.html b/src/search/tests/fixtures/page-without-body.html
index 2ce35a0b4f7b..071edeac015c 100644
--- a/src/search/tests/fixtures/page-without-body.html
+++ b/src/search/tests/fixtures/page-without-body.html
@@ -6,7 +6,7 @@
@@ -14,5 +14,5 @@
A page without body
-
This is an introduction to the article.
+
This is an introduction to the article.
diff --git a/src/search/tests/fixtures/page-without-sections.html b/src/search/tests/fixtures/page-without-sections.html
index d337a9deb14d..3835276909fd 100644
--- a/src/search/tests/fixtures/page-without-sections.html
+++ b/src/search/tests/fixtures/page-without-sections.html
@@ -6,7 +6,7 @@
@@ -14,11 +14,11 @@
A page without sections
-
This is an introduction to the article.
+
This is an introduction to the article.
-
First paragraph.
+
First paragraph.
-
Second paragraph.
+
Second paragraph.
diff --git a/src/search/tests/parse-page-sections-into-records.ts b/src/search/tests/parse-page-sections-into-records.ts
index 7367ccf749f2..0e2e0741fc9d 100644
--- a/src/search/tests/parse-page-sections-into-records.ts
+++ b/src/search/tests/parse-page-sections-into-records.ts
@@ -73,7 +73,7 @@ describe('search parsePageSectionsIntoRecords module', () => {
const record: Record = parsePageSectionsIntoRecords({ href, $, languageCode: 'en' })
const expected: Record = {
objectID: '/example/href',
- breadcrumbs: 'Education / map topic',
+ breadcrumbs: 'Education / subcategory',
title: 'A page without sections',
headings: '',
content: 'This is an introduction to the article.\nFirst paragraph.\nSecond paragraph.',
@@ -91,7 +91,7 @@ describe('search parsePageSectionsIntoRecords module', () => {
const record: Record = parsePageSectionsIntoRecords({ href, $, languageCode: 'en' })
const expected: Record = {
objectID: '/example/href',
- breadcrumbs: 'Education / map topic',
+ breadcrumbs: 'Education / subcategory',
title: 'A page without body',
headings: '',
content: 'This is an introduction to the article.',
diff --git a/src/types.ts b/src/types.ts
index 2c7b79cb1421..8a83f4970353 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -26,7 +26,7 @@ export type PageFrontmatter = {
permissions?: string
showMiniToc?: boolean
miniTocMaxHeadingLevel?: number
- mapTopic?: boolean
+ subcategory?: boolean
hidden?: boolean
noEarlyAccessBanner?: boolean
earlyAccessToc?: string
@@ -461,6 +461,6 @@ export type MarkdownFrontmatter = {
children: string[]
allowTitleToDifferFromFilename?: boolean
versions: FrontmatterVersions
- mapTopic?: boolean
+ subcategory?: boolean
hidden?: boolean
}