1
0
mirror of synced 2025-12-19 18:10:59 -05:00

Merge pull request #39417 from github/repo-sync

Repo sync
This commit is contained in:
docs-bot
2025-07-21 08:36:48 -07:00
committed by GitHub
45 changed files with 488 additions and 493 deletions

View File

@@ -1,158 +1,58 @@
--- ---
title: About larger runners title: About larger runners
shortTitle: About larger runners shortTitle: Larger runners
intro: '{% data variables.product.prodname_dotcom %} offers runners with advanced features to support more customized use cases.' intro: 'Learn about the types and uses of {% data variables.product.prodname_dotcom %}-hosted {% data variables.actions.hosted_runners %}.'
permissions: '{% data reusables.actions.larger-runner-permissions %}' permissions: '{% data reusables.actions.larger-runner-permissions %}'
versions: versions:
feature: actions-hosted-runners fpt: '*'
ghes: '*'
ghec: '*'
redirect_from: redirect_from:
- /actions/using-github-hosted-runners/about-larger-runners/about-larger-runners - /actions/using-github-hosted-runners/about-larger-runners/about-larger-runners
- /actions/using-github-hosted-runners/using-larger-runners/about-larger-runners - /actions/using-github-hosted-runners/using-larger-runners/about-larger-runners
--- ---
## Overview of {% data variables.actions.hosted_runners %} {% ifversion ghes %}
{% data reusables.actions.enterprise-github-hosted-runners %}
To learn about larger runners, see [the {% data variables.product.prodname_ghe_cloud %} documentation](/enterprise-cloud@latest/actions/concepts/runners/about-larger-runners).
{% else %}
## About {% data variables.actions.hosted_runners %}
{% data reusables.actions.about-larger-runners %} {% data reusables.actions.about-larger-runners %}
{% data variables.product.prodname_dotcom %} offers {% data variables.actions.hosted_runners %} with macOS, Ubuntu, or Windows operating systems, and different features and sizes are available depending on which operating system you use. For more information, see [Additional features for {% data variables.actions.hosted_runners %}](#additional-features-for-larger-runners). {% data variables.product.prodname_dotcom %} offers {% data variables.actions.hosted_runners %} with macOS, Ubuntu, or Windows operating systems, and different features and sizes are available depending on which operating system you use.
### About Ubuntu and Windows {% data variables.actions.hosted_runners %} ## About Ubuntu and Windows {% data variables.actions.hosted_runners %}
{% data variables.actions.hosted_runner_caps %}s with Ubuntu or Windows operating systems are configured in your organization or enterprise. When you add a {% data variables.actions.hosted_runner %}, you are defining a type of machine from a selection of available hardware specifications and operating system images. {% data variables.product.prodname_dotcom %} will then create multiple instances of this runner that scale up and down to match the job demands of your organization, based on the autoscaling limits you define. For more information, see [AUTOTITLE](/actions/using-github-hosted-runners/managing-larger-runners). {% data variables.actions.hosted_runner_caps %}s with Ubuntu or Windows operating systems are configured in your organization or enterprise. When you add a {% data variables.actions.hosted_runner %}, you are defining a type of machine from a selection of available hardware specifications and operating system images.
Ubuntu and Windows {% data variables.actions.hosted_runners %} offer autoscaling capabilities and the ability to assign the runners static IP addresses from a specific range. They can also be managed using runner groups, which enables you to control access to the {% data variables.actions.hosted_runners %}. For more information, see [Additional features for {% data variables.actions.hosted_runners %}](#additional-features-for-larger-runners). With Ubuntu and Windows {% data variables.actions.hosted_runners %}, you can:
* Assign runners static IP addresses from a specific range, allowing you to use this range to configure a firewall allowlist
* Control access to your resources by assigning runners to runner groups
* Use autoscaling to simplify runner management and control your costs
* Use your runners with Azure private networking
### About macOS {% data variables.actions.hosted_runners %} ## About macOS {% data variables.actions.hosted_runners %}
{% data variables.actions.hosted_runner_caps %}s with a macOS operating system are used by updating the YAML workflow label to the desired runner image. To run your workflows on a macOS {% data variables.actions.hosted_runner %}, update the `runs-on` key to use one of the {% data variables.product.company_short %}-defined macOS {% data variables.actions.hosted_runner %} labels. No additional configuration is required. For more information, see [AUTOTITLE](/actions/using-github-hosted-runners/running-jobs-on-larger-runners?platform=mac). {% data variables.actions.hosted_runner_caps %}s with a macOS operating system are not manually added to your organization or enterprise, but are instead used by updating the `runs-on` key of a workflow file to one of the {% data variables.product.company_short %}-defined macOS {% data variables.actions.hosted_runner %} labels.
The following machines sizes are available for macOS {% data variables.actions.hosted_runners %}. Since macOS {% data variables.actions.hosted_runners %} are not preconfigured, they have limitations that Ubuntu and Windows {% data variables.actions.hosted_runners %} do not. For more information, see [AUTOTITLE](/actions/reference/larger-runners-reference#limitations-for-macos-larger-runners).
{% data reusables.actions.larger-runners-table %} ## Billing
>[!NOTE] The XLarge macOS runner is in public preview and subject to change.
#### Limitations for macOS {% data variables.actions.hosted_runners %}
{% data reusables.actions.macos-runner-limitations %}
### Additional features for {% data variables.actions.hosted_runners %}
Compared to standard {% data variables.product.prodname_dotcom %}-hosted runners, {% data variables.actions.hosted_runners %} have additional features, and their availability varies depending on the {% data variables.actions.hosted_runner %}'s operating system.
{% rowheaders %}
| | Ubuntu | Windows | macOS |
| ------------------- | ------ | ------- | ----- |
| Static IP addresses | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} | {% octicon "x" aria-label="Not supported" %} |
| Azure private networking | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} | {% octicon "x" aria-label="Not supported" %} |
| Autoscaling | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} | {% octicon "x" aria-label="Not supported" %} |
| Runner groups | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} | {% octicon "x" aria-label="Not supported" %} |
{% endrowheaders %}
These features can enhance your CI/CD pipelines in the following ways.
* Assigning {% data variables.actions.hosted_runners %} static IP addresses from a specific range enables you to use this range to configure a firewall allowlist. For more information, see [Networking for {% data variables.actions.hosted_runners %}](#networking-for-larger-runners).
* Autoscaling enables {% data variables.actions.hosted_runners %} to scale up to a maximum limit set by you, so your workflows can run concurrently. For more information, see [Autoscaling {% data variables.actions.hosted_runners %}](#autoscaling-larger-runners).
* Runner groups allow you to control access to {% data variables.actions.hosted_runners %} for your organizations, repositories, and workflows. For more information, see [AUTOTITLE](/actions/using-github-hosted-runners/controlling-access-to-larger-runners).
### Runner images
{% data variables.actions.hosted_runner_caps %}s run on virtual machines (VMs), and {% data variables.product.prodname_dotcom %} installs a virtual hard disk (VHD) on this machine during the VM creation process. You can choose from different VM images to install on your runners.
**{% data variables.product.prodname_dotcom %}-owned images:** These images are maintained by {% data variables.product.prodname_dotcom %} and are available for Linux x64, Windows x64, and macOS (x64 and arm) runners. For more information on these images and a full list of included tools for each runner operating system, see the [{% data variables.product.prodname_actions %} Runner Images](https://github.com/actions/runner-images) repository.
**Partner Images:** Partner images are not managed by {% data variables.product.prodname_dotcom %} and are pulled from the Azure Marketplace. See below for resources on where to find more information and to report issues for partner images.
* [Base Windows 11 desktop image](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/microsoftwindowsdesktop.windows-11?tab=Overview).
* [NVIDIA GPU-Optimized VMI](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/nvidia.ngc_azure_17_11)
* [Data Science Virtual Machine - Windows 2019](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/microsoft-dsvm.dsvm-win-2019?tab=overview).
* arm64 images: [`actions/partner-runner-images` repository](https://github.com/actions/partner-runner-images).
### Understanding billing
> [!NOTE] > [!NOTE]
> {% data variables.actions.hosted_runner_caps %}s are not eligible for the use of included minutes on private repositories. For both private and public repositories, when {% data variables.actions.hosted_runners %} are in use, they will always be billed at the per-minute rate. > {% data variables.actions.hosted_runner_caps %}s are not eligible for the use of included minutes on private repositories. For both private and public repositories, when {% data variables.actions.hosted_runners %} are in use, they will always be billed at the per-minute rate.
Compared to standard {% data variables.product.prodname_dotcom %}-hosted runners, {% data variables.actions.hosted_runners %} are billed differently. {% data reusables.actions.about-larger-runners-billing %} For more information, see [AUTOTITLE](/billing/managing-billing-for-github-actions/about-billing-for-github-actions#per-minute-rates). Compared to standard {% data variables.product.prodname_dotcom %}-hosted runners, {% data variables.actions.hosted_runners %} are billed differently. {% data reusables.actions.about-larger-runners-billing %} For more information, see [AUTOTITLE](/billing/managing-billing-for-github-actions/about-billing-for-github-actions#per-minute-rates).
## Machine sizes for {% data variables.actions.hosted_runners %} ## Next steps
You can choose from several specifications for {% data variables.actions.hosted_runners %}. To start using Windows or Ubuntu {% data variables.actions.hosted_runners %}, see [AUTOTITLE](/actions/how-tos/using-github-hosted-runners/using-larger-runners/managing-larger-runners).
### Specifications for general {% data variables.actions.hosted_runners %} To start using macOS {% data variables.actions.hosted_runners %}, see [AUTOTITLE](/actions/how-tos/using-github-hosted-runners/using-larger-runners/running-jobs-on-larger-runners?platform=mac).
| CPU | Memory (RAM) | Storage (SSD) | Architecture | Operating system (OS) | To find reference information about using {% data variables.actions.hosted_runners %}, see [AUTOTITLE](/actions/reference/larger-runners-reference).
| --- | ------------- | ------------- | ------------ | --------------------- |
| 5 | 14 GB | 14 GB | arm64 | macOS |
| 12 | 30 GB | 14 GB | x64 | macOS |
| 2 | 8 GB | 75 GB | x64, arm64 | Ubuntu |
| 4 | 16 GB | 150 GB | x64, arm64 | Ubuntu, Windows |
| 8 | 32 GB | 300 GB | x64, arm64 | Ubuntu, Windows |
| 16 | 64 GB | 600 GB | x64, arm64 | Ubuntu, Windows |
| 32 | 128 GB | 1200 GB | x64, arm64 | Ubuntu, Windows |
| 64 | 208 GB | 2040 GB | arm64 | Ubuntu, Windows |
| 64 | 256 GB | 2040 GB | x64 | Ubuntu, Windows |
| 96 | 384 GB | 2040 GB | x64 | Ubuntu, Windows |
>[!NOTE] The 4-vCPU Windows runner only works with the Windows Server 2025 or the Base Windows 11 Desktop image. {% endif %}
>[!NOTE] The 5-vCPU macOS runner is in public preview and subject to change.
### Specifications for GPU {% data variables.actions.hosted_runners %}
| CPU | GPU | GPU card | Memory (RAM) | GPU memory (VRAM) | Storage (SSD) | Operating system (OS) |
| --- | --- | -------- | ------------ | ----------------- | ------------- | --------------------- |
| 4 | 1 | Tesla T4 | 28 GB | 16 GB | 176 GB | Ubuntu, Windows |
## About runner groups
> [!NOTE]
> Only {% data variables.actions.hosted_runners %} with Linux or Windows operating systems can be assigned to runner groups.
Runner groups enable administrators to control access to runners at the organization and enterprise levels. With runner groups, you can collect sets of runners and create a security boundary around them. You can then decide which organizations or repositories are permitted to run jobs on those sets of machines. During the {% data variables.actions.hosted_runner %} deployment process, the runner can be added to an existing group, otherwise it will join a default group. You can create a group by following the steps in [AUTOTITLE](/actions/using-github-hosted-runners/controlling-access-to-larger-runners).
## Architectural overview of {% data variables.actions.hosted_runners %}
> [!NOTE]
> This architecture diagram only applies to {% data variables.actions.hosted_runners %} with Linux or Windows operating systems.
{% data variables.actions.hosted_runner_caps %}s are managed at the organization level, where they are arranged into groups that can contain multiple instances of the runner. They can also be created at the enterprise level and shared with organizations in the hierarchy. Once you've created a group, you can then add a runner to the group and update your workflows to target either the group name or the label assigned to the {% data variables.actions.hosted_runner %}. You can also control which repositories are permitted to send jobs to the group for processing. For more information about groups, see [AUTOTITLE](/actions/using-github-hosted-runners/controlling-access-to-larger-runners).
In the following diagram, a class of hosted runner named `ubuntu-20.04-16core` has been defined with customized hardware and operating system configuration.
![Diagram showing a larger runner being used by a workflow because of the runner's label.](/assets/images/help/actions/hosted-runner.png)
1. Instances of this runner are automatically created and added to a group called `grp-ubuntu-20.04-16core`.
1. The runners have been assigned the label `ubuntu-20.04-16core`.
1. Workflow jobs use the `ubuntu-20.04-16core` label in their `runs-on` key to indicate the type of runner they need to execute the job.
1. {% data variables.product.prodname_actions %} checks the runner group to see if your repository is authorized to send jobs to the runner.
1. The job runs on the next available instance of the `ubuntu-20.04-16core` runner.
## Autoscaling {% data variables.actions.hosted_runners %}
> [!NOTE]
> Autoscaling is only available for {% data variables.actions.hosted_runners %} with Linux or Windows operating systems.
{% data variables.actions.hosted_runner_caps %}s can automatically scale to suit your needs. You can provision machines to run a specified maximum number of jobs when jobs are submitted for processing. Each machine only handles one job at a time, so these settings effectively determine the number of jobs that can be run concurrently.
You can configure the maximum job concurrency, which allows you to control your costs by setting the maximum parallel number of jobs that can be run using this set. A higher value here can help avoid workflows being blocked due to parallelism. For more information on how to set limits, see [AUTOTITLE](/actions/using-github-hosted-runners/managing-larger-runners#configuring-autoscaling-for-larger-runners). For more information on the maximum auto-scaling limits for {% data variables.product.company_short %}-hosted runners, see [AUTOTITLE](/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits).
## Assigning static IP addresses to {% data variables.actions.hosted_runners %}
You can assign static IP addresses only to {% data variables.actions.hosted_runners %} that use Linux or Windows operating systems.
Static IP addresses assigned are all usable and are not in CIDR notation.
{% data reusables.actions.static-ip-limitation-vnet %} For more information about private networking for {% data variables.product.company_short %}-hosted runners, see [AUTOTITLE](/admin/configuration/configuring-private-networking-for-hosted-compute-products/about-azure-private-networking-for-github-hosted-runners-in-your-enterprise).
## Networking for {% data variables.actions.hosted_runners %}
By default, {% data variables.actions.hosted_runners %} receive a dynamic IP address that changes for each job run. Optionally, {% data variables.product.prodname_ghe_cloud %} customers can configure their {% data variables.actions.hosted_runners %} to receive static IP addresses from {% data variables.product.prodname_dotcom %}'s IP address pool. For more information, see [AUTOTITLE](/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses).
When enabled, instances of the {% data variables.actions.hosted_runner %} will receive IP addresses from specific ranges that are unique to the runner, allowing you to use the ranges to configure a firewall allowlist. {% ifversion fpt %}You can use up to 10 {% data variables.actions.hosted_runners %} with static IP address ranges in total across all your {% data variables.actions.hosted_runners %}{% endif %}{% ifversion ghec %}You can use up to 10 {% data variables.actions.hosted_runners %} with static IP address ranges for the {% data variables.actions.hosted_runners %} created at the enterprise level. In addition, you can use up to 10 {% data variables.actions.hosted_runners %} with static IP address ranges for the {% data variables.actions.hosted_runners %} created at the organization level, for each organization in your enterprise{% endif %}. For more information, see [AUTOTITLE](/actions/using-github-hosted-runners/managing-larger-runners#networking-for-larger-runners).
{% data reusables.actions.larger-runner-static-ip-contact-support %}
> [!NOTE]
> If runners are unused for more than 30 days, their IP address ranges are automatically removed and cannot be recovered.

View File

@@ -1,5 +1,6 @@
--- ---
title: About custom actions title: About custom actions
shortTitle: Custom actions
intro: 'Actions are individual tasks that you can combine to create jobs and customize your workflow. You can create your own actions, or use and customize actions shared by the {% data variables.product.prodname_dotcom %} community.' intro: 'Actions are individual tasks that you can combine to create jobs and customize your workflow. You can create your own actions, or use and customize actions shared by the {% data variables.product.prodname_dotcom %} community.'
redirect_from: redirect_from:
- /articles/about-actions - /articles/about-actions

View File

@@ -1,61 +0,0 @@
---
title: About monitoring workflows
intro: 'You can use the tools in {% data variables.product.prodname_actions %} to monitor your workflows, metrics, and self-hosted runners.'
versions:
fpt: '*'
ghes: '*'
ghec: '*'
shortTitle: About monitoring
redirect_from:
- /actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/about-monitoring-workflows
---
{% data reusables.actions.enterprise-github-hosted-runners %}
## Monitoring your workflows
{% ifversion github-runner-dashboard %}
### Monitoring your current jobs in your organization or enterprise
{% data reusables.actions.github-hosted-runners-check-concurrency %}
{% endif %}
### Using the visualization graph
Every workflow run generates a real-time graph that illustrates the run progress. You can use this graph to monitor and debug workflows. For example:
![Screenshot of the visualization graph of a workflow run.](/assets/images/help/actions/workflow-graph.png)
For more information, see [AUTOTITLE](/actions/monitoring-and-troubleshooting-workflows/using-the-visualization-graph).
### Adding a workflow status badge
{% data reusables.repositories.actions-workflow-status-badge-intro %}
For more information, see [AUTOTITLE](/actions/monitoring-and-troubleshooting-workflows/adding-a-workflow-status-badge).
{% ifversion fpt or ghec %}
### Viewing job execution time
To identify how long a job took to run, you can view its execution time. For more information, see [AUTOTITLE](/actions/monitoring-and-troubleshooting-workflows/viewing-job-execution-time).
{% endif %}
### Viewing workflow run history
You can view the status of each job and step in a workflow. For more information, see [AUTOTITLE](/actions/monitoring-and-troubleshooting-workflows/viewing-workflow-run-history).
{% ifversion actions-metrics %}
## Monitoring {% data variables.product.prodname_actions %} metrics
To analyze the efficiency and reliability of your workflows using metrics, see [AUTOTITLE](/actions/administering-github-actions/viewing-github-actions-metrics).
{% endif %}
## Monitoring self-hosted runners
If you use self-hosted runners, you can view their activity and diagnose common issues.
For more information, see [AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/monitoring-and-troubleshooting-self-hosted-runners).

View File

@@ -17,7 +17,5 @@ children:
- /concurrency - /concurrency
- /workflow-artifacts - /workflow-artifacts
- /dependency-caching - /dependency-caching
- /about-monitoring-workflows
- /notifications-for-workflow-runs - /notifications-for-workflow-runs
- /about-troubleshooting-workflows
--- ---

View File

@@ -36,7 +36,15 @@ Reusable workflows and composite actions both help you to avoid duplication. Whe
## Reusable workflows and workflow templates ## Reusable workflows and workflow templates
Workflow templates allow everyone in your organization who has permission to create workflows to do so more quickly and easily. When people create a new workflow, they can choose a workflow template and some or all of the work of writing the workflow will be done for them. Within a workflow template, you can also reference reusable workflows to make it easy for people to benefit from reusing centrally managed workflow code. If you use a commit SHA when referencing the reusable workflow, you can ensure that everyone who reuses that workflow will always be using the same YAML code. However, if you reference a reusable workflow by a tag or branch, be sure that you can trust that version of the workflow. For more information, see [AUTOTITLE](/actions/security-guides/security-hardening-for-github-actions#reusing-third-party-workflows). Workflow templates allow everyone in your organization who has permission to create workflows to do so more quickly and easily. When people create a new workflow, they can choose a workflow template and some or all of the work of writing the workflow will be done for them. Within a workflow template, you can also reference reusable workflows to make it easy for people to benefit from reusing centrally managed workflow code.
If you use a commit SHA when referencing the reusable workflow, you can ensure that everyone who reuses that workflow will always be using the same YAML code. However, if you reference a reusable workflow by a tag or branch, be sure that you can trust that version of the workflow. For more information, see [AUTOTITLE](/actions/security-guides/security-hardening-for-github-actions#reusing-third-party-workflows).
{% data variables.product.github %} offers workflow templates for a variety of languages and tooling. When you set up workflows in your repository, {% data variables.product.github %} analyzes the code in your repository and recommends workflows based on the language and framework in your repository. For example, if you use Node.js, {% data variables.product.github %} will suggest a workflow template file that installs your Node.js packages and runs your tests. You can search and filter to find relevant workflow templates.
{% data reusables.actions.workflow-templates-categories %}
{% data reusables.actions.workflow-templates-repo-link %}
For more information, see [AUTOTITLE](/actions/using-workflows/creating-starter-workflows-for-your-organization). For more information, see [AUTOTITLE](/actions/using-workflows/creating-starter-workflows-for-your-organization).

View File

@@ -1,5 +1,6 @@
--- ---
title: Working with Support for GitHub Actions title: Getting help from GitHub Support about GitHub Actions
shortTitle: Get support
intro: 'Learn how {% data variables.contact.github_support %} can assist with {% data variables.product.prodname_actions %}' intro: 'Learn how {% data variables.contact.github_support %} can assist with {% data variables.product.prodname_actions %}'
versions: versions:
fpt: '*' fpt: '*'
@@ -9,10 +10,10 @@ type: reference
topics: topics:
- Actions - Actions
- Support - Support
shortTitle: 'Working with {% data variables.contact.github_support %}'
redirect_from: redirect_from:
- /actions/monitoring-and-troubleshooting-workflows/working-with-support-for-github-actions - /actions/monitoring-and-troubleshooting-workflows/working-with-support-for-github-actions
- /actions/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/working-with-support-for-github-actions - /actions/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/working-with-support-for-github-actions
- /actions/how-tos/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/working-with-support-for-github-actions
--- ---
You can [contact {% data variables.contact.github_support %}](/support/contacting-github-support) for assistance with {% data variables.product.prodname_actions %}. You can [contact {% data variables.contact.github_support %}](/support/contacting-github-support) for assistance with {% data variables.product.prodname_actions %}.

View File

@@ -10,11 +10,17 @@ children:
- /writing-workflows - /writing-workflows
- /managing-workflow-runs-and-deployments - /managing-workflow-runs-and-deployments
- /sharing-automations - /sharing-automations
- /monitoring-and-troubleshooting-workflows
- /using-github-hosted-runners - /using-github-hosted-runners
- /using-larger-runners
- /hosting-your-own-runners - /hosting-your-own-runners
- /security-for-github-actions - /security-for-github-actions
- /use-cases-and-examples - /use-cases-and-examples
- /administering-github-actions - /administering-github-actions
- /monitor-workflows
- /troubleshooting-workflows
- /getting-help-from-github-support-about-github-actions
redirect_from:
- /actions/how-tos/monitoring-and-troubleshooting-workflows
- /actions/monitoring-and-troubleshooting-workflows/about-monitoring-and-troubleshooting
--- ---

View File

@@ -13,11 +13,8 @@ versions:
ghec: '*' ghec: '*'
--- ---
{% data reusables.actions.enterprise-github-hosted-runners %} > [!NOTE]
> Re-run workflows use the privileges of the actor who initially triggered the workflow, not the privileges of the actor who initiated the re-run. The workflow will also use the same `GITHUB_SHA` (commit SHA) and `GITHUB_REF` (git ref) of the original event that triggered the workflow run.
## About re-running workflows and jobs
Re-running a workflow or jobs in a workflow uses the same `GITHUB_SHA` (commit SHA) and `GITHUB_REF` (Git ref) of the original event that triggered the workflow run. The workflow will use the privileges of the actor who initially triggered the workflow, not the privileges of the actor who initiated the re-run. You can re-run a workflow or jobs in a workflow for up to 30 days after the initial run. You cannot re-run jobs in a workflow once its logs have passed their retention limits. For more information, see [AUTOTITLE](/actions/learn-github-actions/usage-limits-billing-and-administration#artifact-and-log-retention-policy). When you re-run a workflow or jobs in a workflow, you can enable debug logging for the re-run. This will enable runner diagnostic logging and step debug logging for the re-run. For more information about debug logging, see [AUTOTITLE](/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging)
## Re-running all the jobs in a workflow ## Re-running all the jobs in a workflow
@@ -30,7 +27,6 @@ Re-running a workflow or jobs in a workflow uses the same `GITHUB_SHA` (commit S
1. In the upper-right corner of the workflow, re-run jobs. 1. In the upper-right corner of the workflow, re-run jobs.
* If any jobs failed, select the **{% octicon "sync" aria-hidden="true" aria-label="sync" %} Re-run jobs** dropdown menu and click **Re-run all jobs**. * If any jobs failed, select the **{% octicon "sync" aria-hidden="true" aria-label="sync" %} Re-run jobs** dropdown menu and click **Re-run all jobs**.
* If no jobs failed, click **Re-run all jobs**. * If no jobs failed, click **Re-run all jobs**.
{% data reusables.actions.enable-debug-logging %} {% data reusables.actions.enable-debug-logging %}
@@ -39,32 +35,28 @@ Re-running a workflow or jobs in a workflow uses the same `GITHUB_SHA` (commit S
{% cli %} {% cli %}
{% data reusables.cli.cli-learn-more %} 1. To re-run a failed workflow run, use the `run rerun` subcommand, replacing `RUN_ID` with the ID of the failed run that you want to re-run. If you don't specify a `run-id`, {% data variables.product.prodname_cli %} returns an interactive menu for you to choose a recent failed run.
To re-run a failed workflow run, use the `run rerun` subcommand. Replace `run-id` with the ID of the failed run that you want to re-run. If you don't specify a `run-id`, {% data variables.product.prodname_cli %} returns an interactive menu for you to choose a recent failed run. ```shell copy
gh run rerun RUN_ID
```
```shell {% data reusables.actions.enable-debug-logging-cli %}
gh run rerun RUN_ID
```
{% data reusables.actions.enable-debug-logging-cli %} ```shell copy
gh run rerun RUN_ID --debug
```
```shell 1. To view the progress of the workflow run, use the `run watch` subcommand and select the run from the interactive list.
gh run rerun RUN_ID --debug
```
To view the progress of the workflow run, use the `run watch` subcommand and select the run from the interactive list. ```shell copy
gh run watch
```shell ```
gh run watch
```
{% endcli %} {% endcli %}
## Re-running failed jobs in a workflow ## Re-running failed jobs in a workflow
If any jobs in a workflow run failed, you can re-run just the jobs that failed. When you re-run failed jobs in a workflow, a new workflow run will start for all failed jobs and their dependents. Any outputs for any successful jobs in the previous workflow run will be used for the re-run. Any artifacts that were created in the initial run will be available in the re-run. Any deployment protection rules that passed in the previous run will automatically pass in the re-run.
{% webui %} {% webui %}
{% data reusables.repositories.navigate-to-repo %} {% data reusables.repositories.navigate-to-repo %}
@@ -78,7 +70,7 @@ If any jobs in a workflow run failed, you can re-run just the jobs that failed.
{% cli %} {% cli %}
To re-run failed jobs in a workflow run, use the `run rerun` subcommand with the `--failed` flag. Replace `run-id` with the ID of the run for which you want to re-run failed jobs. If you don't specify a `run-id`, {% data variables.product.prodname_cli %} returns an interactive menu for you to choose a recent failed run. To re-run failed jobs in a workflow run, use the `run rerun` subcommand with the `--failed` flag. Replace `RUN_ID` with the ID of the run for which you want to re-run failed jobs. If you don't specify a `run-id`, {% data variables.product.prodname_cli %} returns an interactive menu for you to choose a recent failed run.
```shell ```shell
gh run rerun RUN_ID --failed gh run rerun RUN_ID --failed
@@ -94,8 +86,6 @@ gh run rerun RUN_ID --failed --debug
## Re-running a specific job in a workflow ## Re-running a specific job in a workflow
When you re-run a specific job in a workflow, a new workflow run will start for the job and any dependents. Any outputs for any other jobs in the previous workflow run will be used for the re-run. Any artifacts that were created in the initial run will be available in the re-run. Any deployment protection rules that passed in the previous run will automatically pass in the re-run.
{% webui %} {% webui %}
{% data reusables.repositories.navigate-to-repo %} {% data reusables.repositories.navigate-to-repo %}
@@ -110,7 +100,7 @@ When you re-run a specific job in a workflow, a new workflow run will start for
{% cli %} {% cli %}
To re-run a specific job in a workflow run, use the `run rerun` subcommand with the `--job` flag. Replace `job-id` with the ID of the job that you want to re-run. To re-run a specific job in a workflow run, use the `run rerun` subcommand with the `--job` flag. Replace `JOB_ID` with the ID of the job that you want to re-run.
```shell ```shell
gh run rerun --job JOB_ID gh run rerun --job JOB_ID
@@ -124,14 +114,8 @@ gh run rerun --job JOB_ID --debug
{% endcli %} {% endcli %}
## Re-running workflows and jobs with reusable workflows
{% data reusables.actions.partial-reruns-with-reusable %}
## Reviewing previous workflow runs ## Reviewing previous workflow runs
You can view the results from your previous attempts at running a workflow. You can also view previous workflow runs using the API. For more information, see [AUTOTITLE](/rest/actions/workflow-runs#get-a-workflow-run).
{% data reusables.repositories.navigate-to-repo %} {% data reusables.repositories.navigate-to-repo %}
{% data reusables.repositories.actions-tab %} {% data reusables.repositories.actions-tab %}
{% data reusables.repositories.navigate-to-workflow %} {% data reusables.repositories.navigate-to-workflow %}

View File

@@ -1,6 +1,5 @@
--- ---
title: Monitoring workflows title: Monitor workflows
shortTitle: Monitor
intro: 'You can monitor {% data variables.product.prodname_actions %} workflows by using tools like the visualization graph and run logs.' intro: 'You can monitor {% data variables.product.prodname_actions %} workflows by using tools like the visualization graph and run logs.'
versions: versions:
fpt: '*' fpt: '*'
@@ -12,6 +11,8 @@ children:
- /viewing-job-execution-time - /viewing-job-execution-time
- /adding-a-workflow-status-badge - /adding-a-workflow-status-badge
- /using-workflow-run-logs - /using-workflow-run-logs
- /enabling-debug-logging
redirect_from: redirect_from:
- /actions/monitoring-and-troubleshooting-workflows/monitoring-workflows - /actions/monitoring-and-troubleshooting-workflows/monitoring-workflows
- /actions/concepts/workflows-and-actions/about-monitoring-workflows
--- ---

View File

@@ -1,6 +1,6 @@
--- ---
title: Using the visualization graph title: Using the visualization graph
shortTitle: Visualization graph shortTitle: Use the visualization graph
intro: Every workflow run generates a real-time graph that illustrates the run progress. You can use this graph to monitor and debug workflows. intro: Every workflow run generates a real-time graph that illustrates the run progress. You can use this graph to monitor and debug workflows.
redirect_from: redirect_from:
- /actions/managing-workflow-runs/using-the-visualization-graph - /actions/managing-workflow-runs/using-the-visualization-graph

View File

@@ -1,6 +1,6 @@
--- ---
title: Using workflow run logs title: Using workflow run logs
shortTitle: Workflow run logs shortTitle: Use workflow run logs
intro: 'You can view, search, and download the logs for each job in a workflow run.' intro: 'You can view, search, and download the logs for each job in a workflow run.'
redirect_from: redirect_from:
- /actions/managing-workflow-runs/using-workflow-run-logs - /actions/managing-workflow-runs/using-workflow-run-logs

View File

@@ -1,6 +1,6 @@
--- ---
title: Viewing workflow run history title: Viewing workflow run history
shortTitle: Workflow run history shortTitle: View workflow run history
intro: You can view logs for each run of a workflow. Logs include the status for each job and step in a workflow. intro: You can view logs for each run of a workflow. Logs include the status for each job and step in a workflow.
redirect_from: redirect_from:
- /actions/managing-workflow-runs/viewing-workflow-run-history - /actions/managing-workflow-runs/viewing-workflow-run-history

View File

@@ -1,19 +0,0 @@
---
title: Monitoring and troubleshooting workflows
shortTitle: Monitor & troubleshoot
intro: 'You can view the status and results of each step in your workflow, debug a failed workflow, and search and download logs.'
redirect_from:
- /articles/viewing-your-repository-s-workflows
- /articles/viewing-your-repositorys-workflows
- /actions/monitoring-and-troubleshooting-workflows/about-monitoring-and-troubleshooting
- /actions/monitoring-and-troubleshooting-workflows
versions:
fpt: '*'
ghes: '*'
ghec: '*'
children:
- /monitoring-workflows
- /troubleshooting-workflows
---
{% data reusables.actions.enterprise-github-hosted-runners %}

View File

@@ -1,15 +0,0 @@
---
title: Troubleshooting workflows
shortTitle: Troubleshoot
intro: 'You can troubleshoot {% data variables.product.prodname_actions %} workflows by using tools like debug logging.'
versions:
fpt: '*'
ghes: '*'
ghec: '*'
children:
- /using-copilot-to-troubleshoot-workflows
- /enabling-debug-logging
- /working-with-support-for-github-actions
redirect_from:
- /actions/monitoring-and-troubleshooting-workflows/troubleshooting-workflows
---

View File

@@ -1,21 +0,0 @@
---
title: Using Copilot to troubleshoot workflows
intro: 'You can use {% data variables.product.prodname_copilot %} to help resolve failed workflow runs.'
versions:
feature: copilot
shortTitle: Use Copilot
permissions: This feature is available for users on all {% data variables.product.prodname_copilot %} subscription tiers.
redirect_from:
- /actions/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/using-copilot-to-troubleshoot-workflows
---
If a workflow run fails, you can open a chat with {% data variables.product.prodname_copilot %} for assistance resolving the error.
To open a chat about a failed workflow run, you can either:
* Next to the failed check in the merge box, click **{% octicon "kebab-horizontal" aria-hidden="true" aria-label="kebab-horizontal" %}**, then click **{% octicon "copilot" aria-hidden="true" aria-label="copilot" %} Explain error**.
* In the merge box, click on the failed check. At the top of the workflow run summary page, click **{% octicon "copilot" aria-hidden="true" aria-label="copilot" %} Explain error**.
This opens a chat window with {% data variables.product.prodname_copilot %}, where it will provide instructions to resolve the issue.
>[!NOTE] If you are on a {% data variables.copilot.copilot_free %} subscription, this will count towards your monthly chat message limit.

View File

@@ -1,13 +1,15 @@
--- ---
title: About troubleshooting workflows title: Troubleshooting workflows
shortTitle: Troubleshoot workflows
intro: 'You can use the tools in {% data variables.product.prodname_actions %} to debug your workflows.' intro: 'You can use the tools in {% data variables.product.prodname_actions %} to debug your workflows.'
versions: versions:
fpt: '*' fpt: '*'
ghes: '*' ghes: '*'
ghec: '*' ghec: '*'
shortTitle: About troubleshooting
redirect_from: redirect_from:
- /actions/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/about-troubleshooting-workflows - /actions/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/about-troubleshooting-workflows
- /actions/how-tos/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/using-copilot-to-troubleshoot-workflows
- /actions/how-tos/monitoring-and-troubleshooting-workflows/troubleshooting-workflows
--- ---
{% data reusables.actions.enterprise-github-hosted-runners %} {% data reusables.actions.enterprise-github-hosted-runners %}
@@ -18,9 +20,16 @@ There are several ways you can troubleshoot failed workflow runs.
{% ifversion copilot %} {% ifversion copilot %}
>[!NOTE] If you are on a {% data variables.copilot.copilot_free %} subscription, this will count towards your monthly chat message limit.
### Using {% data variables.product.prodname_copilot %} ### Using {% data variables.product.prodname_copilot %}
If a workflow run fails, you can open a chat with {% data variables.product.prodname_copilot %} for assistance resolving the error. See [AUTOTITLE](/actions/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/using-copilot-to-troubleshoot-workflows). To open a chat with {% data variables.product.prodname_copilot %} about a failed workflow run, you can either:
* Next to the failed check in the merge box, click **{% octicon "kebab-horizontal" aria-hidden="true" aria-label="kebab-horizontal" %}**, then click **{% octicon "copilot" aria-hidden="true" aria-label="copilot" %} Explain error**.
* In the merge box, click on the failed check. At the top of the workflow run summary page, click **{% octicon "copilot" aria-hidden="true" aria-label="copilot" %} Explain error**.
This opens a chat window with {% data variables.product.prodname_copilot %}, where it will provide instructions to resolve the issue.
{% endif %} {% endif %}

View File

@@ -9,8 +9,7 @@ versions:
children: children:
- /using-github-hosted-runners - /using-github-hosted-runners
- /customizing-github-hosted-runners - /customizing-github-hosted-runners
- /monitoring-your-current-jobs - /viewing-your-current-jobs
- /using-larger-runners
- /connecting-to-a-private-network - /connecting-to-a-private-network
redirect_from: redirect_from:
- /actions/using-github-hosted-runners - /actions/using-github-hosted-runners

View File

@@ -1,6 +1,6 @@
--- ---
title: Monitoring your current jobs title: Viewing your current jobs
shortTitle: Monitor current jobs shortTitle: View current jobs
intro: 'Monitor how {% data variables.product.prodname_dotcom %}-hosted runners are processing jobs in your organization or enterprise, and identify any related constraints.' intro: 'Monitor how {% data variables.product.prodname_dotcom %}-hosted runners are processing jobs in your organization or enterprise, and identify any related constraints.'
versions: versions:
feature: github-runner-dashboard feature: github-runner-dashboard
@@ -9,6 +9,8 @@ redirect_from:
- /actions/using-github-hosted-runners/about-github-hosted-runners/monitoring-your-current-jobs - /actions/using-github-hosted-runners/about-github-hosted-runners/monitoring-your-current-jobs
- /actions/using-github-hosted-runners/using-github-hosted-runners/monitoring-your-current-jobs - /actions/using-github-hosted-runners/using-github-hosted-runners/monitoring-your-current-jobs
- /actions/how-tos/using-github-hosted-runners/using-github-hosted-runners/monitoring-your-current-jobs - /actions/how-tos/using-github-hosted-runners/using-github-hosted-runners/monitoring-your-current-jobs
- /actions/how-tos/using-github-hosted-runners/monitoring-your-current-jobs#viewing-active-jobs-in-your-organization-or-enterprise
- /actions/how-tos/using-github-hosted-runners/monitoring-your-current-jobs
--- ---
{% data reusables.actions.enterprise-github-hosted-runners %} {% data reusables.actions.enterprise-github-hosted-runners %}

View File

@@ -10,6 +10,7 @@ redirect_from:
- /actions/using-github-hosted-runners/controlling-access-to-larger-runners - /actions/using-github-hosted-runners/controlling-access-to-larger-runners
- /actions/using-github-hosted-runners/about-larger-runners/controlling-access-to-larger-runners - /actions/using-github-hosted-runners/about-larger-runners/controlling-access-to-larger-runners
- /actions/using-github-hosted-runners/using-larger-runners/controlling-access-to-larger-runners - /actions/using-github-hosted-runners/using-larger-runners/controlling-access-to-larger-runners
- /actions/how-tos/using-github-hosted-runners/using-larger-runners/controlling-access-to-larger-runners
--- ---
> [!NOTE] > [!NOTE]

View File

@@ -1,6 +1,5 @@
--- ---
title: Using larger runners title: Using larger runners
shortTitle: Using larger runners
intro: '{% data variables.product.prodname_dotcom %} offers runners with more RAM, CPU, and disk space.' intro: '{% data variables.product.prodname_dotcom %} offers runners with more RAM, CPU, and disk space.'
versions: versions:
feature: actions-hosted-runners feature: actions-hosted-runners
@@ -11,6 +10,7 @@ children:
redirect_from: redirect_from:
- /actions/using-github-hosted-runners/about-larger-runners - /actions/using-github-hosted-runners/about-larger-runners
- /actions/using-github-hosted-runners/using-larger-runners - /actions/using-github-hosted-runners/using-larger-runners
- /actions/how-tos/using-github-hosted-runners/using-larger-runners
--- ---
{% data reusables.actions.enterprise-github-hosted-runners %} {% data reusables.actions.enterprise-github-hosted-runners %}

View File

@@ -9,6 +9,7 @@ redirect_from:
- /actions/using-github-hosted-runners/managing-larger-runners - /actions/using-github-hosted-runners/managing-larger-runners
- /actions/using-github-hosted-runners/about-larger-runners/managing-larger-runners - /actions/using-github-hosted-runners/about-larger-runners/managing-larger-runners
- /actions/using-github-hosted-runners/using-larger-runners/managing-larger-runners - /actions/using-github-hosted-runners/using-larger-runners/managing-larger-runners
- /actions/how-tos/using-github-hosted-runners/using-larger-runners/managing-larger-runners
--- ---
> [!NOTE] > [!NOTE]

View File

@@ -10,6 +10,7 @@ redirect_from:
- /actions/using-github-hosted-runners/running-jobs-on-larger-runners - /actions/using-github-hosted-runners/running-jobs-on-larger-runners
- /actions/using-github-hosted-runners/about-larger-runners/running-jobs-on-larger-runners - /actions/using-github-hosted-runners/about-larger-runners/running-jobs-on-larger-runners
- /actions/using-github-hosted-runners/using-larger-runners/running-jobs-on-larger-runners - /actions/using-github-hosted-runners/using-larger-runners/running-jobs-on-larger-runners
- /actions/how-tos/using-github-hosted-runners/using-larger-runners/running-jobs-on-larger-runners
--- ---
## Running jobs on your runner ## Running jobs on your runner

View File

@@ -26,18 +26,6 @@ topics:
{% data reusables.actions.enterprise-github-hosted-runners %} {% data reusables.actions.enterprise-github-hosted-runners %}
## About workflow templates
Workflow templates are templates that help you to create your own {% data variables.product.prodname_actions %} workflows for a repository. They offer an alternative to starting from a blank workflow file and are useful because some of the work will already have been done for you.
{% data variables.product.github %} offers workflow templates for a variety of languages and tooling. When you set up workflows in your repository, {% data variables.product.github %} analyzes the code in your repository and recommends workflows based on the language and framework in your repository. For example, if you use Node.js, {% data variables.product.github %} will suggest a workflow template file that installs your Node.js packages and runs your tests. You can search and filter to find relevant workflow templates.
{% data reusables.actions.workflow-templates-categories %}
{% data reusables.actions.workflow-templates-repo-link %}
You can also create your own workflow template to share with your organization. These workflow templates will appear alongside the {% data variables.product.github %}-provided workflow templates. Anyone with write access to the organization's `.github` repository can set up a workflow template. For more information, see [AUTOTITLE](/actions/using-workflows/creating-starter-workflows-for-your-organization).
## Choosing and using a workflow template ## Choosing and using a workflow template
{% data reusables.repositories.navigate-to-repo %} {% data reusables.repositories.navigate-to-repo %}

View File

@@ -12,7 +12,6 @@ featuredLinks:
- /actions/concepts/overview/continuous-integration - /actions/concepts/overview/continuous-integration
- /actions/tutorials/deploying-with-github-actions - /actions/tutorials/deploying-with-github-actions
- /packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions - /packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions
- /actions/how-tos/monitoring-and-troubleshooting-workflows
guideCards: guideCards:
- /actions/how-tos/writing-workflows/using-workflow-templates - /actions/how-tos/writing-workflows/using-workflow-templates
- /actions/tutorials/publishing-packages/publishing-nodejs-packages - /actions/tutorials/publishing-packages/publishing-nodejs-packages

View File

@@ -23,6 +23,7 @@ children:
- /openid-connect-reference - /openid-connect-reference
- /dockerfile-support-for-github-actions - /dockerfile-support-for-github-actions
- /github-hosted-runners-reference - /github-hosted-runners-reference
- /larger-runners-reference
- /self-hosted-runners-reference - /self-hosted-runners-reference
- /supplemental-arguments-and-settings - /supplemental-arguments-and-settings
- /extending-github-actions-importer-with-custom-transformers - /extending-github-actions-importer-with-custom-transformers

View File

@@ -0,0 +1,83 @@
---
title: Larger runners reference
shortTitle: Larger runners reference
intro: 'Find information about larger runners, including their specifications and customization options.'
versions:
fpt: '*'
ghes: '*'
ghec: '*'
---
{% ifversion ghes %}
{% data reusables.actions.enterprise-github-hosted-runners %}
For reference information about larger runners, see [the {% data variables.product.prodname_ghe_cloud %} documentation](/enterprise-cloud@latest/actions/reference/larger-runners-reference).
{% else %}
## Machine sizes for {% data variables.actions.hosted_runners %}
You can choose from several specifications for {% data variables.actions.hosted_runners %}.
### Specifications for general {% data variables.actions.hosted_runners %}
| CPU | Memory (RAM) | Storage (SSD) | Architecture | Operating system (OS) |
| --- | ------------- | ------------- | ------------ | --------------------- |
| 5 | 14 GB | 14 GB | arm64 (M2) | macOS |
| 12 | 30 GB | 14 GB | x64 (Intel) | macOS |
| 2 | 8 GB | 75 GB | x64, arm64 | Ubuntu |
| 4 | 16 GB | 150 GB | x64, arm64 | Ubuntu, Windows |
| 8 | 32 GB | 300 GB | x64, arm64 | Ubuntu, Windows |
| 16 | 64 GB | 600 GB | x64, arm64 | Ubuntu, Windows |
| 32 | 128 GB | 1200 GB | x64, arm64 | Ubuntu, Windows |
| 64 | 208 GB | 2040 GB | arm64 | Ubuntu, Windows |
| 64 | 256 GB | 2040 GB | x64 | Ubuntu, Windows |
| 96 | 384 GB | 2040 GB | x64 | Ubuntu, Windows |
>[!NOTE] The 4-vCPU Windows runner only works with the Windows Server 2025 or the Base Windows 11 Desktop image.
>[!NOTE] The 5-vCPU macOS runner is in public preview and subject to change.
### Specifications for GPU {% data variables.actions.hosted_runners %}
| CPU | GPU | GPU card | Memory (RAM) | GPU memory (VRAM) | Storage (SSD) | Operating system (OS) |
| --- | --- | -------- | ------------ | ----------------- | ------------- | --------------------- |
| 4 | 1 | Tesla T4 | 28 GB | 16 GB | 176 GB | Ubuntu, Windows |
## Runner images
{% data variables.actions.hosted_runner_caps %}s run on virtual machines (VMs), and {% data variables.product.prodname_dotcom %} installs a virtual hard disk (VHD) on this machine during the VM creation process. You can choose from different VM images to install on your runners.
**{% data variables.product.prodname_dotcom %}-owned images:** These images are maintained by {% data variables.product.prodname_dotcom %} and are available for Linux x64, Windows x64, and macOS (x64 and arm) runners. For more information on these images and a full list of included tools for each runner operating system, see the [{% data variables.product.prodname_actions %} Runner Images](https://github.com/actions/runner-images) repository.
**Partner Images:** Partner images are not managed by {% data variables.product.prodname_dotcom %} and are pulled from the Azure Marketplace. See below for resources on where to find more information and to report issues for partner images.
* [Base Windows 11 desktop image](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/microsoftwindowsdesktop.windows-11?tab=Overview).
* [NVIDIA GPU-Optimized VMI](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/nvidia.ngc_azure_17_11)
* [Data Science Virtual Machine - Windows 2019](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/microsoft-dsvm.dsvm-win-2019?tab=overview).
* arm64 images: [`actions/partner-runner-images` repository](https://github.com/actions/partner-runner-images).
## Available macOS {% data variables.actions.hosted_runners %} and labels
The following machines are available for macOS {% data variables.actions.hosted_runners %}.
{% data reusables.actions.larger-runners-table %}
>[!NOTE] The XLarge macOS runner is in public preview and subject to change.
## Limitations for macOS {% data variables.actions.hosted_runners %}
{% data reusables.actions.macos-runner-limitations %}
## Networking for {% data variables.actions.hosted_runners %}
By default, {% data variables.actions.hosted_runners %} receive a dynamic IP address that changes for each job run. Optionally, {% data variables.product.prodname_ghe_cloud %} customers can configure their {% data variables.actions.hosted_runners %} to receive static IP addresses from {% data variables.product.prodname_dotcom %}'s IP address pool. For more information, see [AUTOTITLE](/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses).
When enabled, instances of the {% data variables.actions.hosted_runner %} will receive IP addresses from specific ranges that are unique to the runner, allowing you to use the ranges to configure a firewall allowlist. {% ifversion fpt %}You can use up to 10 {% data variables.actions.hosted_runners %} with static IP address ranges in total across all your {% data variables.actions.hosted_runners %}{% endif %}{% ifversion ghec %}You can use up to 10 {% data variables.actions.hosted_runners %} with static IP address ranges for the {% data variables.actions.hosted_runners %} created at the enterprise level. In addition, you can use up to 10 {% data variables.actions.hosted_runners %} with static IP address ranges for the {% data variables.actions.hosted_runners %} created at the organization level, for each organization in your enterprise{% endif %}. For more information, see [AUTOTITLE](/actions/using-github-hosted-runners/managing-larger-runners#networking-for-larger-runners).
{% data reusables.actions.larger-runner-static-ip-contact-support %}
> [!NOTE]
> If runners are unused for more than 30 days, their IP address ranges are automatically removed and cannot be recovered.
{% endif %}

View File

@@ -1,7 +1,7 @@
--- ---
title: About customizing GitHub Copilot Chat responses title: About customizing GitHub Copilot Chat responses
shortTitle: Customize Copilot responses shortTitle: Customize Copilot responses
intro: 'Learn about customizing {% data variables.copilot.copilot_chat %} responses to fit with your preferences and requirements.' intro: 'Learn about customizing the behavior of {% data variables.product.prodname_copilot %} to fit with your preferences and requirements.'
versions: versions:
feature: copilot feature: copilot
topics: topics:
@@ -50,6 +50,16 @@ redirect_from:
* **Repository custom instructions** apply to conversations within the context of a specific repository. They are useful for defining project-specific coding standards, frameworks, or tools. For example, you can specify that a repository uses TypeScript and a particular library, ensuring consistent responses for all contributors. * **Repository custom instructions** apply to conversations within the context of a specific repository. They are useful for defining project-specific coding standards, frameworks, or tools. For example, you can specify that a repository uses TypeScript and a particular library, ensuring consistent responses for all contributors.
* **Organization custom instructions (public preview)** apply to conversations within the context of an organization on the {% data variables.product.github %} website. They are ideal for enforcing organization-wide preferences, such as a common language or security guidelines. Organization custom instructions can only be set by organization owners for organizations with a {% data variables.copilot.copilot_enterprise_short %} subscription. * **Organization custom instructions (public preview)** apply to conversations within the context of an organization on the {% data variables.product.github %} website. They are ideal for enforcing organization-wide preferences, such as a common language or security guidelines. Organization custom instructions can only be set by organization owners for organizations with a {% data variables.copilot.copilot_enterprise_short %} subscription.
### About repository custom instructions
Repository custom instructions consist of a single file, `.github/copilot-instructions.md`, that you create in a repository.
Repository custom instructions files are used for chat responses, for code review, and also by {% data variables.product.prodname_copilot_short %} when you assign it to an issue or ask it to create a pull request. Instructions included in this file can help {% data variables.product.prodname_copilot_short %} to work on files in a way that matches your team's working practices and conforms to coding standards for your project. See [AUTOTITLE](/copilot/using-github-copilot/coding-agent/about-assigning-tasks-to-copilot).
{% data reusables.copilot.repository-custom-instructions-example %}
{% data reusables.copilot.repository-cust-instr-code-review %}
{% endwebui %} {% endwebui %}
{% vscode %} {% vscode %}
@@ -122,6 +132,8 @@ Common use cases include:
{% data reusables.copilot.repository-custom-instructions-example %} {% data reusables.copilot.repository-custom-instructions-example %}
{% data reusables.copilot.repository-cust-instr-code-review %}
## About prompt files ## About prompt files
> [!NOTE] Prompt files are {% data variables.release-phases.public_preview %} and subject to change. > [!NOTE] Prompt files are {% data variables.release-phases.public_preview %} and subject to change.
@@ -189,6 +201,8 @@ Common use cases include:
{% data reusables.copilot.repository-custom-instructions-example %} {% data reusables.copilot.repository-custom-instructions-example %}
{% data reusables.copilot.repository-cust-instr-code-review %}
{% endvisualstudio %} {% endvisualstudio %}
{% jetbrains %} {% jetbrains %}
@@ -227,6 +241,28 @@ Common use cases include:
{% endxcode %} {% endxcode %}
## Writing effective custom instructions
The instructions you add should ideally be short, self-contained statements provide {% data variables.product.prodname_copilot_short %} with relevant information to help it work.
When writing custom instructions for a repository, you should also consider the size and complexity of your repository. The following types of instructions may work for a small repository with only a few contributors, but for a large and diverse repository, **these may cause problems**:
* Requests to refer to external resources when formulating a response
* Instructions to answer in a particular style
* Requests to always respond with a certain level of detail
For example, the following instructions **may not have the intended results**:
```markdown
Always conform to the coding styles defined in styleguide.md in repo my-org/my-repo when generating code.
Use @terminal when answering questions about Git.
Answer all questions in the style of a friendly colleague, using informal language.
Answer all questions in less than 1000 characters, and words of no more than 12 characters.
```
## Next steps ## Next steps
{% webui %} {% webui %}

View File

@@ -55,7 +55,7 @@ AI-generated text completion to help you write pull request descriptions quickly
### {% data variables.product.prodname_copilot_short %} custom instructions ### {% data variables.product.prodname_copilot_short %} custom instructions
Enhance {% data variables.copilot.copilot_chat_short %} responses by providing contextual details on your preferences, tools, and requirements. See [AUTOTITLE](/copilot/customizing-copilot/about-customizing-github-copilot-chat-responses). Enhance {% data variables.copilot.copilot_chat_short %} responses by providing contextual details on your preferences, tools, and requirements. See [AUTOTITLE](/copilot/concepts/about-customizing-github-copilot-chat-responses).
### {% data variables.copilot.copilot_desktop_short %} ### {% data variables.copilot.copilot_desktop_short %}

View File

@@ -14,7 +14,7 @@ redirect_from:
{% data reusables.copilot.organization-instructions-note %} {% data reusables.copilot.organization-instructions-note %}
For an overview of the methods you can use to customize {% data variables.copilot.copilot_chat %} responses, see [AUTOTITLE](/copilot/customizing-copilot/about-customizing-github-copilot-chat-responses?tool=webui). For an overview of the methods you can use to customize {% data variables.copilot.copilot_chat %} responses, see [AUTOTITLE](/copilot/concepts/about-customizing-github-copilot-chat-responses?tool=webui).
## About organization custom instructions for {% data variables.copilot.copilot_chat %} ## About organization custom instructions for {% data variables.copilot.copilot_chat %}

View File

@@ -12,7 +12,7 @@ redirect_from:
{% data reusables.copilot.personal-instructions-note %} {% data reusables.copilot.personal-instructions-note %}
For an overview of the methods you can use to customize {% data variables.copilot.copilot_chat %} responses, see [AUTOTITLE](/copilot/customizing-copilot/about-customizing-github-copilot-chat-responses?tool=webui). For an overview of the methods you can use to customize {% data variables.copilot.copilot_chat %} responses, see [AUTOTITLE](/copilot/concepts/about-customizing-github-copilot-chat-responses?tool=webui).
## About personal custom instructions for {% data variables.copilot.copilot_chat %} ## About personal custom instructions for {% data variables.copilot.copilot_chat %}

View File

@@ -19,80 +19,25 @@ This version of this article is for using repository custom instructions on the
## About repository custom instructions for {% data variables.product.prodname_copilot_short %} ## About repository custom instructions for {% data variables.product.prodname_copilot_short %}
Repository custom instructions let you provide {% data variables.product.prodname_copilot_short %} with repository-specific guidance and preferences. For a full introduction to custom instructions, see [AUTOTITLE](/copilot/concepts/about-customizing-github-copilot-chat-responses?tool=webui).
{% data reusables.copilot.repository-custom-instructions-support %} {% data reusables.copilot.repository-custom-instructions-support %}
* **{% data variables.copilot.copilot_coding_agent %}** * **{% data variables.copilot.copilot_coding_agent %}**
* **{% data variables.copilot.copilot_code-review_short %}** * **{% data variables.copilot.copilot_code-review_short %}**
{% data reusables.copilot.repository-custom-instructions-intro %} You can do this by creating a file in your repository that provides {% data variables.product.prodname_copilot_short %} with the contextual information it needs to generate higher quality responses.
The custom instructions file is used for chat responses, for code review, and also by {% data variables.product.prodname_copilot_short %} when you assign it to an issue or ask it to create a pull request. Instructions included in this file can help {% data variables.product.prodname_copilot_short %} to work on files in a way that matches your team's working practices and conforms to coding standards for your project. See [AUTOTITLE](/copilot/using-github-copilot/coding-agent/about-assigning-tasks-to-copilot).
### Example
{% data reusables.copilot.repository-custom-instructions-example %}
{% data reusables.copilot.repository-cust-instr-code-review %}
{% endwebui %} {% endwebui %}
{% vscode %} {% vscode %}
This version of this article is for using repository custom instructions in {% data variables.product.prodname_vscode_shortname %}. Click the tabs above for instructions on using custom instructions in other environments. This version of this article is for using repository custom instructions and prompt files in {% data variables.product.prodname_vscode_shortname %}. Click the tabs above for instructions on using custom instructions in other environments.
## About repository custom instructions and prompt files for {% data variables.product.prodname_copilot_short %} ## About repository custom instructions and prompt files for {% data variables.product.prodname_copilot_short %}
You can provide {% data variables.product.prodname_copilot_short %} with repository-wide instructions or reusable prompt files to give it context and guidance when working in {% data variables.product.prodname_vscode_shortname %}. For a full introduction to repository custom instructions and prompt files, including examples, see [AUTOTITLE](/copilot/concepts/about-customizing-github-copilot-chat-responses?tool=vscode).
{% data reusables.copilot.repository-custom-instructions-support %} {% data reusables.copilot.repository-custom-instructions-support %}
* **{% data variables.copilot.copilot_code-review_short %}** * **{% data variables.copilot.copilot_code-review_short %}**
{% data reusables.copilot.repository-custom-instructions-intro %} You can do this by creating a file in your repositories that provides {% data variables.product.prodname_copilot_short %} with the contextual information it needs to generate higher quality responses.
There are two types of files you can use to provide context and instructions to {% data variables.product.prodname_copilot_short %} in {% data variables.product.prodname_vscode_shortname %}:
* **Repository custom instructions** allow you to specify repository-wide instructions and preferences, in a single file, that {% data variables.product.prodname_copilot_short %} will use when working in this repository.
* **Prompt files** (public preview) allow you to save common prompt instructions and relevant context in Markdown files (`*.prompt.md`) that you can then reuse in your {% data variables.copilot.copilot_chat_short %} prompts. Prompt files are only available in {% data variables.product.prodname_vscode_shortname %}.
While custom instructions help to add codebase-wide context to each AI workflow, prompt files let you add instructions to a specific {% data variables.product.prodname_copilot_short %} interaction.
### Repository custom instructions example
{% data reusables.copilot.repository-custom-instructions-example %}
{% data reusables.copilot.repository-cust-instr-code-review %}
### Prompt file examples
The following examples demonstrate how to use prompt files.
* `New React form.prompt.md` - contains instructions for a reusable task to generate a form using React.
```markdown
Your goal is to generate a new React form component.
Ask for the form name and fields if not provided.
Requirements for the form:
- Use form design system components: [design-system/Form.md](../docs/design-system/Form.md)
- Use `react-hook-form` for form state management:
- Always define TypeScript types for your form data
- Prefer *uncontrolled* components using register
- Use `defaultValues` to prevent unnecessary rerenders
- Use `yup` for validation:
- Create reusable validation schemas in separate files
- Use TypeScript types to ensure type safety
- Customize UX-friendly validation rules
```
* `API security review.prompt.md` - contains reusable information about security practices for REST APIs, which can be used to do security reviews of REST APIs.
```markdown
Secure REST API review:
- Ensure all endpoints are protected by authentication and authorization
- Validate all user inputs and sanitize data
- Implement rate limiting and throttling
- Implement logging and monitoring for security events
```
{% endvscode %} {% endvscode %}
{% visualstudio %} {% visualstudio %}
@@ -103,17 +48,11 @@ This version of this article is for using repository custom instructions in {% d
## About repository custom instructions for {% data variables.product.prodname_copilot_short %} ## About repository custom instructions for {% data variables.product.prodname_copilot_short %}
Repository custom instructions let you provide {% data variables.product.prodname_copilot_short %} with repository-specific guidance and preferences. For a full introduction to custom instructions, see [AUTOTITLE](/copilot/concepts/about-customizing-github-copilot-chat-responses?tool=visualstudio).
{% data reusables.copilot.repository-custom-instructions-support %} {% data reusables.copilot.repository-custom-instructions-support %}
* **{% data variables.copilot.copilot_code-review_short %}** * **{% data variables.copilot.copilot_code-review_short %}**
{% data reusables.copilot.repository-custom-instructions-intro %} You can do this by creating a file in your repositories that provides {% data variables.product.prodname_copilot_short %} with the contextual information it needs to generate higher quality responses.
### Example
{% data reusables.copilot.repository-custom-instructions-example %}
{% data reusables.copilot.repository-cust-instr-code-review %}
{% endvisualstudio %} {% endvisualstudio %}
{% jetbrains %} {% jetbrains %}
@@ -124,13 +63,9 @@ This version of this article is for using repository custom instructions in JetB
## About repository custom instructions for {% data variables.copilot.copilot_chat %} ## About repository custom instructions for {% data variables.copilot.copilot_chat %}
Repository custom instructions are currently supported for {% data variables.copilot.copilot_chat_short %} in JetBrains IDEs, {% data variables.product.prodname_vs %}, {% data variables.product.prodname_vscode_shortname %}, Xcode, and on the {% data variables.product.github %} website. Repository custom instructions let you provide {% data variables.product.prodname_copilot_short %} with repository-specific guidance and preferences. For a full introduction to custom instructions, see [AUTOTITLE](/copilot/concepts/about-customizing-github-copilot-chat-responses?tool=jetbrains).
{% data variables.product.prodname_copilot %} can provide chat responses that are tailored to the way your team works, the tools you use, or the specifics of your project, if you provide it with enough context to do so. Instead of repeatedly adding this contextual detail to your chat questions, you can create a file in your repository that automatically adds this information for you. The additional information is not displayed in the chat, but is available to {% data variables.product.prodname_copilot_short %} to allow it to generate higher quality responses. {% data reusables.copilot.repository-custom-instructions-support %}
### Example
{% data reusables.copilot.repository-custom-instructions-example %}
{% endjetbrains %} {% endjetbrains %}
@@ -142,13 +77,9 @@ This version of this article is for using repository custom instructions in Xcod
## About repository custom instructions for {% data variables.copilot.copilot_chat %} ## About repository custom instructions for {% data variables.copilot.copilot_chat %}
Repository custom instructions are currently supported for {% data variables.copilot.copilot_chat_short %} in Xcode, {% data variables.product.prodname_vs %}, {% data variables.product.prodname_vscode_shortname %} JetBrains IDEs, and on the {% data variables.product.github %} website. Repository custom instructions let you provide {% data variables.product.prodname_copilot_short %} with repository-specific guidance and preferences. For a full introduction to custom instructions, see [AUTOTITLE](/copilot/concepts/about-customizing-github-copilot-chat-responses?tool=xcode).
{% data variables.product.prodname_copilot %} can provide chat responses that are tailored to the way your team works, the tools you use, or the specifics of your project, if you provide it with enough context to do so. Instead of repeatedly adding this contextual detail to your chat questions, you can create a file in your repository that automatically adds this information for you. The additional information is not displayed in the chat, but is available to {% data variables.product.prodname_copilot_short %} to allow it to generate higher quality responses. {% data reusables.copilot.repository-custom-instructions-support %}
### Example
{% data reusables.copilot.repository-custom-instructions-example %}
{% endxcode %} {% endxcode %}
@@ -289,28 +220,6 @@ Did you successfully add a custom instructions file to your repository?
{% endnote %} {% endnote %}
## Writing effective repository custom instructions
The instructions you add to the `.github/copilot-instructions.md` file should be short, self-contained statements provide {% data variables.product.prodname_copilot_short %} with relevant information to help it work in this repository.
You should also consider the size and complexity of your repository. The following types of instructions may work for a small repository with only a few contributors, but for a large and diverse repository, **these may cause problems**:
* Requests to refer to external resources when formulating a response
* Instructions to answer in a particular style
* Requests to always respond with a certain level of detail
For example, the following instructions **may not have the intended results**:
```markdown
Always conform to the coding styles defined in styleguide.md in repo my-org/my-repo when generating code.
Use @terminal when answering questions about Git.
Answer all questions in the style of a friendly colleague, using informal language.
Answer all questions in less than 1000 characters, and words of no more than 12 characters.
```
## Repository custom instructions in use ## Repository custom instructions in use
The instructions in the `.github/copilot-instructions.md` file are available for use by {% data variables.copilot.copilot_chat_short %} as soon as you save the file. The complete set of instructions will be automatically added to requests that you submit to {% data variables.product.prodname_copilot_short %} in the context of that repository. For example, they are added to the prompt you submit to {% data variables.copilot.copilot_chat_short %}. The instructions in the `.github/copilot-instructions.md` file are available for use by {% data variables.copilot.copilot_chat_short %} as soon as you save the file. The complete set of instructions will be automatically added to requests that you submit to {% data variables.product.prodname_copilot_short %} in the context of that repository. For example, they are added to the prompt you submit to {% data variables.copilot.copilot_chat_short %}.

View File

@@ -39,7 +39,7 @@ Follow these best practices to get the most out of combining MCP servers with ag
* **Provide context**: Include relevant background information about your project and requirements, including links to external resources that {% data variables.product.prodname_copilot_short %} can access. * **Provide context**: Include relevant background information about your project and requirements, including links to external resources that {% data variables.product.prodname_copilot_short %} can access.
* **Set boundaries**: Specify any constraints or limitations for the task. For example, if you want {% data variables.product.prodname_copilot_short %} to only plan a new feature and not make any changes yet, specify that. You can also limit which MCP tools are enabled. * **Set boundaries**: Specify any constraints or limitations for the task. For example, if you want {% data variables.product.prodname_copilot_short %} to only plan a new feature and not make any changes yet, specify that. You can also limit which MCP tools are enabled.
* **Request confirmations**: Ask {% data variables.product.prodname_copilot_short %} to confirm its understanding before proceeding with significant changes. * **Request confirmations**: Ask {% data variables.product.prodname_copilot_short %} to confirm its understanding before proceeding with significant changes.
* **Use prompt files or custom instructions**: You can create prompt files or custom instructions files to guide {% data variables.product.prodname_copilot_short %} on how to behave for different MCP servers. See [AUTOTITLE](/copilot/customizing-copilot/about-customizing-github-copilot-chat-responses). * **Use prompt files or custom instructions**: You can create prompt files or custom instructions files to guide {% data variables.product.prodname_copilot_short %} on how to behave for different MCP servers. See [AUTOTITLE](/copilot/concepts/about-customizing-github-copilot-chat-responses).
### MCP server use ### MCP server use

View File

@@ -1,4 +1,4 @@
Customers on {% data variables.product.prodname_team %} and {% data variables.product.prodname_ghe_cloud %} plans can choose from a range of managed virtual machines that have more resources than the [standard {% data variables.product.prodname_dotcom %}-hosted runners](/actions/how-tos/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources). These machines are referred to as "{% data variables.actions.hosted_runner %}." They offer the following advanced features: Customers on {% data variables.product.prodname_team %} and {% data variables.product.prodname_ghe_cloud %} plans can choose from a range of managed virtual machines that have more resources than the [standard {% data variables.product.prodname_dotcom %}-hosted runners](/actions/how-tos/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources). These machines are referred to as "{% data variables.actions.hosted_runners %}." They offer the following advanced features:
* More RAM, CPU, and disk space * More RAM, CPU, and disk space
* Static IP addresses * Static IP addresses

View File

@@ -2,4 +2,4 @@
* **Opt in to preview features:** If enabled, users can test new {% data variables.product.prodname_copilot_short %} features that are not yet generally available. Be aware that previews of features may have flaws, and the features may be changed or discontinued at any time. Current previews of {% data variables.product.prodname_copilot_short %} features include: * **Opt in to preview features:** If enabled, users can test new {% data variables.product.prodname_copilot_short %} features that are not yet generally available. Be aware that previews of features may have flaws, and the features may be changed or discontinued at any time. Current previews of {% data variables.product.prodname_copilot_short %} features include:
* {% data variables.copilot.copilot_spaces %}. See [AUTOTITLE](/copilot/using-github-copilot/copilot-spaces/about-organizing-and-sharing-context-with-copilot-spaces). * {% data variables.copilot.copilot_spaces %}. See [AUTOTITLE](/copilot/using-github-copilot/copilot-spaces/about-organizing-and-sharing-context-with-copilot-spaces).
* Repository custom instructions for {% data variables.copilot.copilot_code-review_short %}. See [AUTOTITLE](/copilot/customizing-copilot/about-customizing-github-copilot-chat-responses). * Repository custom instructions for {% data variables.copilot.copilot_code-review_short %}. See [AUTOTITLE](/copilot/concepts/about-customizing-github-copilot-chat-responses).

View File

@@ -1 +1 @@
For examples of custom instructions used to configure {% data variables.copilot.copilot_code-review_short %}, see [AUTOTITLE](/copilot/how-tos/agents/copilot-code-review/using-copilot-code-review?tool=webui#customizing-copilots-reviews-with-custom-instructions). For examples of repository custom instructions used to configure {% data variables.copilot.copilot_code-review_short %}, see [AUTOTITLE](/copilot/how-tos/agents/copilot-code-review/using-copilot-code-review?tool=webui#customizing-copilots-reviews-with-custom-instructions).

View File

@@ -1,2 +1,2 @@
Repository custom instructions are currently supported for: Repository custom instructions are currently supported for:
* **{% data variables.copilot.copilot_chat_short %}** in {% data variables.product.prodname_vs %}, {% data variables.product.prodname_vscode_shortname %} and on the {% data variables.product.github %} website * **{% data variables.copilot.copilot_chat_short %}** in {% data variables.product.prodname_vs %}, {% data variables.product.prodname_vscode_shortname %}, JetBrains IDEs, Xcode, and on the {% data variables.product.github %} website

View File

@@ -19,27 +19,111 @@ import { renderContentWithFallback } from '@/languages/lib/render-with-fallback'
import { deprecated, supported } from '@/versions/lib/enterprise-server-releases' import { deprecated, supported } from '@/versions/lib/enterprise-server-releases'
import { allPlatforms } from '@/tools/lib/all-platforms' import { allPlatforms } from '@/tools/lib/all-platforms'
import type { Context, FrontmatterVersions } from '@/types'
// We're going to check a lot of pages' "ID" (the first part of // We're going to check a lot of pages' "ID" (the first part of
// the relativePath) against `productMap` to make sure it's valid. // the relativePath) against `productMap` to make sure it's valid.
// To avoid having to do `Object.keys(productMap).includes(id)` // To avoid having to do `Object.keys(productMap).includes(id)`
// every single time, we turn it into a Set once. // every single time, we turn it into a Set once.
const productMapKeysAsSet = new Set(Object.keys(productMap)) const productMapKeysAsSet = new Set(Object.keys(productMap))
type ReadFileContentsResult = {
data?: any
content?: string
errors?: any[]
}
type PageInitOptions = {
languageCode: string
relativePath: string
basePath: string
}
type PageReadResult = PageInitOptions & {
fullPath: string
markdown: string
frontmatterErrors?: any[]
} & any
type RenderOptions = {
preferShort?: boolean
unwrap?: boolean
textOnly?: boolean
throwIfEmpty?: boolean
}
type CommunityRedirect = {
name: string
href: string
}
type GuideWithType = {
href: string
title: string
type?: string
topics?: string[]
}
export class FrontmatterErrorsError extends Error { export class FrontmatterErrorsError extends Error {
constructor(message, frontmatterErrors) { public frontmatterErrors: string[]
constructor(message: string, frontmatterErrors: string[]) {
super(message) super(message)
this.frontmatterErrors = frontmatterErrors this.frontmatterErrors = frontmatterErrors
} }
} }
class Page { class Page {
static async init(opts) { // Core properties from PageFrontmatter
opts = await Page.read(opts) public title: string = ''
if (!opts) return public rawTitle: string = ''
return new Page(opts) public shortTitle?: string
public rawShortTitle?: string
public intro: string = ''
public rawIntro?: string
public product?: string
public rawProduct?: string
public permissions?: string
public rawPermissions?: string
public versions: FrontmatterVersions = {}
public showMiniToc?: boolean
public hidden?: boolean
public redirect_from?: string[]
public learningTracks?: any[]
public rawLearningTracks?: string[]
public includeGuides?: GuideWithType[]
public rawIncludeGuides?: string[]
public introLinks?: Record<string, string>
public rawIntroLinks?: Record<string, string>
// Derived properties
public languageCode!: string
public relativePath!: string
public basePath!: string
public fullPath!: string
public markdown!: string
public documentType: string
public applicableVersions: string[]
public permalinks: Permalink[]
public tocItems?: any[]
public communityRedirect?: CommunityRedirect
public detectedPlatforms: string[] = []
public includesPlatformSpecificContent: boolean = false
public detectedTools: string[] = []
public includesToolSpecificContent: boolean = false
public allToolsParsed: typeof allTools = allTools
public introPlainText?: string
// Bound method
public render: (context: Context) => Promise<string>
static async init(opts: PageInitOptions): Promise<Page | undefined> {
const readResult = await Page.read(opts)
if (!readResult) return
return new Page(readResult)
} }
static async read(opts) { static async read(opts: PageInitOptions): Promise<PageReadResult | false> {
assert(opts.languageCode, 'languageCode is required') assert(opts.languageCode, 'languageCode is required')
assert(opts.relativePath, 'relativePath is required') assert(opts.relativePath, 'relativePath is required')
assert(opts.basePath, 'basePath is required') assert(opts.basePath, 'basePath is required')
@@ -50,7 +134,11 @@ class Page {
// Per https://nodejs.org/api/fs.html#fs_fs_exists_path_callback // Per https://nodejs.org/api/fs.html#fs_fs_exists_path_callback
// its better to read and handle errors than to check access/stats first // its better to read and handle errors than to check access/stats first
try { try {
const { data, content, errors: frontmatterErrors } = await readFileContents(fullPath) const {
data,
content,
errors: frontmatterErrors,
}: ReadFileContentsResult = await readFileContents(fullPath)
// The `|| ''` is for pages that are purely frontmatter. // The `|| ''` is for pages that are purely frontmatter.
// So the `content` property will be `undefined`. // So the `content` property will be `undefined`.
@@ -72,11 +160,11 @@ class Page {
// where as notations like `__GHES_DEPRECATED__[3]` // where as notations like `__GHES_DEPRECATED__[3]`
// or `__GHES_SUPPORTED__[0]` are static. // or `__GHES_SUPPORTED__[0]` are static.
if (opts.basePath.split(path.sep).includes('fixtures')) { if (opts.basePath.split(path.sep).includes('fixtures')) {
supported.forEach((version, i, arr) => { supported.forEach((version: string, i: number, arr: string[]) => {
markdown = markdown.replaceAll(`__GHES_SUPPORTED__[${i}]`, version) markdown = markdown.replaceAll(`__GHES_SUPPORTED__[${i}]`, version)
markdown = markdown.replaceAll(`__GHES_SUPPORTED__[-${arr.length - i}]`, version) markdown = markdown.replaceAll(`__GHES_SUPPORTED__[-${arr.length - i}]`, version)
}) })
deprecated.forEach((version, i, arr) => { deprecated.forEach((version: string, i: number, arr: string[]) => {
markdown = markdown.replaceAll(`__GHES_DEPRECATED__[${i}]`, version) markdown = markdown.replaceAll(`__GHES_DEPRECATED__[${i}]`, version)
markdown = markdown.replaceAll(`__GHES_DEPRECATED__[-${arr.length - i}]`, version) markdown = markdown.replaceAll(`__GHES_DEPRECATED__[-${arr.length - i}]`, version)
}) })
@@ -86,25 +174,29 @@ class Page {
...opts, ...opts,
relativePath, relativePath,
fullPath, fullPath,
...data, ...(data || {}),
markdown, markdown,
frontmatterErrors, frontmatterErrors,
} } as PageReadResult
} catch (err) { } catch (err: any) {
if (err.code === 'ENOENT') return false if (err.code === 'ENOENT') return false
console.error(err) console.error(err)
return false
} }
} }
constructor(opts) { constructor(opts: PageReadResult) {
if (opts.frontmatterErrors && opts.frontmatterErrors.length) { if (opts.frontmatterErrors && opts.frontmatterErrors.length) {
throw new FrontmatterErrorsError( throw new FrontmatterErrorsError(
`${opts.frontmatterErrors.length} frontmatter errors trying to load ${opts.fullPath}`, `${opts.frontmatterErrors.length} frontmatter errors trying to load ${opts.fullPath}`,
opts.frontmatterErrors, opts.frontmatterErrors,
) )
} }
delete opts.frontmatterErrors
Object.assign(this, { ...opts }) // Remove frontmatter errors before assignment
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { frontmatterErrors: _, ...cleanOpts } = opts
Object.assign(this, cleanOpts)
// Store raw data so we can cache parsed versions // Store raw data so we can cache parsed versions
this.rawIntro = this.intro this.rawIntro = this.intro
@@ -113,7 +205,7 @@ class Page {
this.rawProduct = this.product this.rawProduct = this.product
this.rawPermissions = this.permissions this.rawPermissions = this.permissions
this.rawLearningTracks = this.learningTracks this.rawLearningTracks = this.learningTracks
this.rawIncludeGuides = this.includeGuides this.rawIncludeGuides = this.includeGuides as any
this.rawIntroLinks = this.introLinks this.rawIntroLinks = this.introLinks
// Is this the Homepage or a Product, Category, Topic, or Article? // Is this the Homepage or a Product, Category, Topic, or Article?
@@ -130,7 +222,7 @@ class Page {
const versionsParentProductIsNotAvailableIn = this.applicableVersions const versionsParentProductIsNotAvailableIn = this.applicableVersions
// only the homepage will not have this.parentProduct // only the homepage will not have this.parentProduct
.filter( .filter(
(availableVersion) => (availableVersion: string) =>
this.parentProduct && !this.parentProduct.versions.includes(availableVersion), this.parentProduct && !this.parentProduct.versions.includes(availableVersion),
) )
@@ -164,12 +256,15 @@ class Page {
return this return this
} }
buildRedirects() { buildRedirects(): Record<string, string> {
return generateRedirectsForPermalinks(this.permalinks, this.redirect_from || []) return generateRedirectsForPermalinks(this.permalinks, this.redirect_from || []) as Record<
string,
string
>
} }
// Infer the parent product ID from the page's relative file path // Infer the parent product ID from the page's relative file path
get parentProductId() { get parentProductId(): string | null {
// Each page's top-level content directory matches its product ID // Each page's top-level content directory matches its product ID
const id = this.relativePath.split('/')[0] const id = this.relativePath.split('/')[0]
@@ -184,17 +279,21 @@ class Page {
return id return id
} }
get parentProduct() { get parentProduct(): any {
return productMap[this.parentProductId] const id = this.parentProductId
return id ? productMap[id] : undefined
} }
async renderTitle(context, opts = { preferShort: true }) { async renderTitle(
context: Context,
opts: RenderOptions = { preferShort: true },
): Promise<string> {
return opts.preferShort && this.shortTitle return opts.preferShort && this.shortTitle
? this.renderProp('shortTitle', context, opts) ? this.renderProp('shortTitle', context, opts)
: this.renderProp('title', context, opts) : this.renderProp('title', context, opts)
} }
async _render(context) { private async _render(context: Context): Promise<string> {
// use English IDs/anchors for translated headings, so links don't break (see #8572) // use English IDs/anchors for translated headings, so links don't break (see #8572)
if (this.languageCode !== 'en') { if (this.languageCode !== 'en') {
const englishHeadings = getEnglishHeadings(this, context) const englishHeadings = getEnglishHeadings(this, context)
@@ -246,7 +345,7 @@ class Page {
// introLinks may contain Liquid and need to have versioning processed. // introLinks may contain Liquid and need to have versioning processed.
if (this.rawIntroLinks) { if (this.rawIntroLinks) {
const introLinks = {} const introLinks: Record<string, string> = {}
for (const [rawKey, value] of Object.entries(this.rawIntroLinks)) { for (const [rawKey, value] of Object.entries(this.rawIntroLinks)) {
introLinks[rawKey] = await renderContent(value, context, { introLinks[rawKey] = await renderContent(value, context, {
textOnly: true, textOnly: true,
@@ -257,8 +356,8 @@ class Page {
} }
if (this.rawIncludeGuides) { if (this.rawIncludeGuides) {
this.includeGuides = await getLinkData(this.rawIncludeGuides, context) this.includeGuides = (await getLinkData(this.rawIncludeGuides, context)) as GuideWithType[]
this.includeGuides.map((guide) => { this.includeGuides?.map((guide: any) => {
const { page } = guide const { page } = guide
guide.type = page.type guide.type = page.type
if (page.topics) { if (page.topics) {
@@ -272,7 +371,7 @@ class Page {
// set a flag so layout knows whether to render a mac/windows/linux switcher element // set a flag so layout knows whether to render a mac/windows/linux switcher element
// Remember, the values of platform is matched in // Remember, the values of platform is matched in
// the handleInvalidQuerystringValues shielding middleware. // the handleInvalidQuerystringValues shielding middleware.
this.detectedPlatforms = allPlatforms.filter((platform) => { this.detectedPlatforms = allPlatforms.filter((platform: string) => {
// This matches `ghd-tool mac` but not `ghd-tool macos` // This matches `ghd-tool mac` but not `ghd-tool macos`
// Whereas `html.includes('ghd-tool mac')` would match both. // Whereas `html.includes('ghd-tool mac')` would match both.
const regex = new RegExp(`ghd-tool ${platform}\\b|platform-${platform}\\b`) const regex = new RegExp(`ghd-tool ${platform}\\b|platform-${platform}\\b`)
@@ -281,7 +380,7 @@ class Page {
this.includesPlatformSpecificContent = this.detectedPlatforms.length > 0 this.includesPlatformSpecificContent = this.detectedPlatforms.length > 0
// set flags for webui, cli, etc switcher element // set flags for webui, cli, etc switcher element
this.detectedTools = Object.keys(allTools).filter((tool) => { this.detectedTools = Object.keys(allTools).filter((tool: string) => {
// This matches `ghd-tool jetbrain` but not `ghd-tool jetbrain_beta` // This matches `ghd-tool jetbrain` but not `ghd-tool jetbrain_beta`
// Whereas `html.includes('ghd-tool jetbrain')` would match both. // Whereas `html.includes('ghd-tool jetbrain')` would match both.
const regex = new RegExp(`ghd-tool ${tool}\\b|tool-${tool}\\b`) const regex = new RegExp(`ghd-tool ${tool}\\b|tool-${tool}\\b`)
@@ -298,8 +397,12 @@ class Page {
// Allow other modules (like custom liquid tags) to make one-off requests // Allow other modules (like custom liquid tags) to make one-off requests
// for a page's rendered properties like `title` and `intro` // for a page's rendered properties like `title` and `intro`
async renderProp(propName, context, opts = { unwrap: false }) { async renderProp(
let prop propName: string,
context: Context,
opts: RenderOptions = { unwrap: false },
): Promise<string> {
let prop: string
if (propName === 'title') { if (propName === 'title') {
prop = 'rawTitle' prop = 'rawTitle'
} else if (propName === 'shortTitle') { } else if (propName === 'shortTitle') {
@@ -316,13 +419,13 @@ class Page {
// The unwrap option removes surrounding tags from a string, preserving any inner HTML // The unwrap option removes surrounding tags from a string, preserving any inner HTML
const $ = cheerio.load(html, { xmlMode: true }) const $ = cheerio.load(html, { xmlMode: true })
return $.root().contents().html() return $.root().contents().html() || ''
} }
// infer current page's corresponding homepage // infer current page's corresponding homepage
// /en/articles/foo -> /en // /en/articles/foo -> /en
// /en/enterprise/2.14/user/articles/foo -> /en/enterprise/2.14/user // /en/enterprise/2.14/user/articles/foo -> /en/enterprise/2.14/user
static getHomepage(requestPath) { static getHomepage(requestPath: string): string {
return requestPath.replace(/\/articles.*/, '') return requestPath.replace(/\/articles.*/, '')
} }
} }

View File

@@ -60,7 +60,7 @@ export default async function glossaries(req: ExtendedRequest, res: Response, ne
) )
} }
description = await executeWithFallback( description = await executeWithFallback(
req.context, req.context!,
() => liquid.parseAndRender(description, req.context), () => liquid.parseAndRender(description, req.context),
(enContext: Context) => { (enContext: Context) => {
const { term } = glossary const { term } = glossary

View File

@@ -62,15 +62,30 @@ All previously archived content lives in its own repository. For example, GHES 3
1. Update all translation directories to the latest `main` branch. 1. Update all translation directories to the latest `main` branch.
1. Hide search component temporarily while scraping docs in `src/search/components/Search.tsx`, by adding the `visually-hidden` class to the `form` element: 1. Hide search components temporarily while scraping docs by adding the `visually-hidden` class to the search components:
**In `src/search/components/input/SearchBarButton.tsx`**, wrap the return statement content:
```javascript ```javascript
return ( return (
<div data-testid="search"> <div className="visually-hidden">
<div className="position-relative z-2"> {/* existing search button content */}
<form </div>
role="search" )
className="width-full d-flex visually-hidden" ```
**In `src/search/components/input/SearchOverlayContainer.tsx`**, wrap the return statement content:
```javascript
if (isSearchOpen) {
return (
<div className="visually-hidden">
<SearchOverlay
// ... existing props
/>
</div>
)
}
``` ```
1. Ensure your build is up to date: 1. Ensure your build is up to date:
@@ -93,7 +108,7 @@ All previously archived content lives in its own repository. For example, GHES 3
npm run deprecate-ghes-archive npm run deprecate-ghes-archive
``` ```
1. Revert changes to `src/search/components/Search.tsx`. 1. Revert changes to `src/search/components/input/SearchBarButton.tsx` and `src/search/components/input/SearchOverlayContainer.tsx`.
1. Check in any change to `src/ghes-releases/lib/enterprise-dates.json`. 1. Check in any change to `src/ghes-releases/lib/enterprise-dates.json`.
@@ -158,7 +173,7 @@ All previously archived content lives in its own repository. For example, GHES 3
1. Poke around several deprecated pages by navigating to `docs.github.com/enterprise/<DEPRECATED VERSION>`, and ensure that: 1. Poke around several deprecated pages by navigating to `docs.github.com/enterprise/<DEPRECATED VERSION>`, and ensure that:
- Stylesheets are working properly - Stylesheets are working properly
- Images are rendering properly - Images are rendering properly
- The search functionality was disabled - The search functionality was disabled during scraping
- Look at any console errors to ensure that no new unexpected errors were introduced. You can look at previous errors by viewing a previously completed deprecation page. - Look at any console errors to ensure that no new unexpected errors were introduced. You can look at previous errors by viewing a previously completed deprecation page.
- You should see a banner on the top of every deprecated page with the date that the version was deprecated. - You should see a banner on the top of every deprecated page with the date that the version was deprecated.
- You should see a banner at the top of every page for the oldes currently supported version with the date that it will be deprecated in the ~3 months. - You should see a banner at the top of every page for the oldes currently supported version with the date that it will be deprecated in the ~3 months.

View File

@@ -7,7 +7,19 @@
* It looks for easy "low hanging fruit" that we can correct for. * It looks for easy "low hanging fruit" that we can correct for.
* *
*/ */
export function correctTranslatedContentStrings(content, englishContent, context = {}) {
interface CorrectionContext {
code?: string
dottedPath?: string
relativePath?: string
[key: string]: any
}
export function correctTranslatedContentStrings(
content: string,
englishContent: string,
context: CorrectionContext = {},
): string {
// A lot of translations have corruptions around the AUTOTITLE links. // A lot of translations have corruptions around the AUTOTITLE links.
// We've requested that these are corrected back but as a temporary // We've requested that these are corrected back but as a temporary
// solution we'll manually recover now. // solution we'll manually recover now.
@@ -294,7 +306,7 @@ export function correctTranslatedContentStrings(content, englishContent, context
const keyString = '5DE3 E050 9C47 EA3C F04A 42D3 4AEE 18F8 3AFD EB23' const keyString = '5DE3 E050 9C47 EA3C F04A 42D3 4AEE 18F8 3AFD EB23'
const translatedSentences = [ const translatedSentences = [
// ru // ru
'Полный отпечаток ключа — `' + keyString + '`.', 'Полный отпечаток ключа\u00A0\u2014 `' + keyString + '`.',
// ko // ko
`키의 전체 지문은 \`${keyString}\`입니다.`, `키의 전체 지문은 \`${keyString}\`입니다.`,
// es // es
@@ -306,7 +318,7 @@ export function correctTranslatedContentStrings(content, englishContent, context
// ja // ja
`キーの完全な指紋は、\`${keyString}\` です。`, `キーの完全な指紋は、\`${keyString}\` です。`,
// fr // fr
`Lempreinte digitale complète de la clé est \`${keyString}\`.`, `L\u2019empreinte digitale complète de la clé est \`${keyString}\`.`,
// de // de
`Der vollständige Fingerabdruck des Schlüssels ist \`${keyString}\`.`, `Der vollständige Fingerabdruck des Schlüssels ist \`${keyString}\`.`,
] ]

View File

@@ -1,20 +1,40 @@
import { fromMarkdown } from 'mdast-util-from-markdown' import { fromMarkdown } from 'mdast-util-from-markdown'
import { toString } from 'mdast-util-to-string' import { toString } from 'mdast-util-to-string'
import { visit } from 'unist-util-visit' import { visit } from 'unist-util-visit'
import type { Node } from 'unist'
import findPage from '@/frame/lib/find-page' import findPage from '@/frame/lib/find-page'
import { getDataByLanguage } from '@/data-directory/lib/get-data' import { getDataByLanguage } from '@/data-directory/lib/get-data'
import type { Context } from '@/types'
interface HeadingNode extends Node {
type: 'heading'
depth: number
}
interface PageWithMarkdown {
relativePath: string
markdown: string
}
interface GlossaryTerm {
term: string
slug: string
}
// for any translated page, first get corresponding English markdown // for any translated page, first get corresponding English markdown
// then get the headings on both the translated and English pageMap // then get the headings on both the translated and English pageMap
// finally, create a map of translation:English for all headings on the page // finally, create a map of translation:English for all headings on the page
export default function getEnglishHeadings(page, context) { export default function getEnglishHeadings(
page: PageWithMarkdown,
context: Context,
): Record<string, string> | undefined {
// Special handling for glossaries, because their headings are // Special handling for glossaries, because their headings are
// generated programmatically. // generated programmatically.
if (page.relativePath.endsWith('/github-glossary.md')) { if (page.relativePath.endsWith('/github-glossary.md')) {
// Return an object of `{ localized-term: english-slug }` // Return an object of `{ localized-term: english-slug }`
const languageGlossary = getDataByLanguage('glossaries.external', 'en') const languageGlossary = getDataByLanguage('glossaries.external', 'en') as GlossaryTerm[]
return languageGlossary.reduce((prev, curr) => { return languageGlossary.reduce((prev: Record<string, string>, curr: GlossaryTerm) => {
prev[curr.term] = curr.slug prev[curr.term] = curr.slug
return prev return prev
}, {}) }, {})
@@ -35,20 +55,21 @@ export default function getEnglishHeadings(page, context) {
if (!englishHeadings.length) return if (!englishHeadings.length) return
// return a map from translation:English // return a map from translation:English
return Object.assign( const headingMap: Record<string, string> = {}
...translatedHeadings.map((k, i) => ({ translatedHeadings.forEach((k: string, i: number) => {
[k]: englishHeadings[i], headingMap[k] = englishHeadings[i]
})), })
) return headingMap
} }
function getHeadings(markdown) { function getHeadings(markdown: string): string[] {
const ast = fromMarkdown(markdown) const ast = fromMarkdown(markdown)
const headings = [] const headings: string[] = []
visit(ast, (node) => { visit(ast, (node: Node) => {
if (node.type !== 'heading') return if (node.type !== 'heading') return
if (![2, 3, 4].includes(node.depth)) return const headingNode = node as HeadingNode
if (![2, 3, 4].includes(headingNode.depth)) return
headings.push(toString(node)) headings.push(toString(node))
}) })

View File

@@ -1,26 +1,47 @@
import { renderContent } from '@/content-render/index' import { renderContent } from '@/content-render/index'
import Page from '@/frame/lib/page' import Page from '@/frame/lib/page'
import { TitleFromAutotitleError } from '@/content-render/unified/rewrite-local-links' import { TitleFromAutotitleError } from '@/content-render/unified/rewrite-local-links'
import type { Context } from '@/types'
export class EmptyTitleError extends Error {} export class EmptyTitleError extends Error {}
interface LiquidToken {
file?: string
getPosition?: () => [number, number]
}
interface LiquidError extends Error {
token?: LiquidToken
originalError?: Error
}
interface RenderOptions {
throwIfEmpty?: boolean
textOnly?: boolean
cache?: boolean | ((template: string, context: any) => string)
[key: string]: any
}
const LIQUID_ERROR_NAMES = new Set(['RenderError', 'ParseError', 'TokenizationError']) const LIQUID_ERROR_NAMES = new Set(['RenderError', 'ParseError', 'TokenizationError'])
export const isLiquidError = (error) => export const isLiquidError = (error: unknown): error is LiquidError =>
error instanceof Error && error.name && LIQUID_ERROR_NAMES.has(error.name) error instanceof Error && error.name !== undefined && LIQUID_ERROR_NAMES.has(error.name)
const isAutotitleError = (error) => error instanceof TitleFromAutotitleError const isAutotitleError = (error: unknown): error is TitleFromAutotitleError =>
const isEmptyTitleError = (error) => error instanceof EmptyTitleError error instanceof TitleFromAutotitleError
const isFallbackableError = (error) => const isEmptyTitleError = (error: unknown): error is EmptyTitleError =>
error instanceof EmptyTitleError
const isFallbackableError = (error: unknown): boolean =>
isLiquidError(error) || isAutotitleError(error) || isEmptyTitleError(error) isLiquidError(error) || isAutotitleError(error) || isEmptyTitleError(error)
/** /**
* Creates an HTML comment with translation fallback error information * Creates an HTML comment with translation fallback error information
* Includes detailed debugging information for translators * Includes detailed debugging information for translators
*/ */
export function createTranslationFallbackComment(error, property) { export function createTranslationFallbackComment(error: Error, property: string): string {
const errorType = error.name || 'UnknownError' const errorType = error.name || 'UnknownError'
let errorDetails = [] const errorDetails: string[] = []
// Add basic error information // Add basic error information
errorDetails.push(`TRANSLATION_FALLBACK`) errorDetails.push(`TRANSLATION_FALLBACK`)
@@ -82,14 +103,21 @@ export function createTranslationFallbackComment(error, property) {
// function. This means, we can know, in the middleware (which is a // function. This means, we can know, in the middleware (which is a
// higher level than `lib/`) how to use the URL to figure out the // higher level than `lib/`) how to use the URL to figure out the
// equivalent English page instance. // equivalent English page instance.
export async function renderContentWithFallback(page, property, context, options) { export async function renderContentWithFallback(
// Using `any` type for page because the actual Page class from @/frame/lib/page
// has more properties than the Page interface defined in @/types, causing type conflicts
page: any,
property: string,
context: Context,
options?: RenderOptions,
): Promise<string> {
if (!(page instanceof Page)) { if (!(page instanceof Page)) {
throw new Error(`The first argument has to be Page instance (not ${typeof page})`) throw new Error(`The first argument has to be Page instance (not ${typeof page})`)
} }
if (typeof property !== 'string') { if (typeof property !== 'string') {
throw new Error(`The second argument has to be a string (not ${typeof property})`) throw new Error(`The second argument has to be a string (not ${typeof property})`)
} }
const template = page[property] const template = (page as any)[property] as string
try { try {
const output = await renderContent(template, context, options) const output = await renderContent(template, context, options)
if (options && options.throwIfEmpty && !output.trim()) { if (options && options.throwIfEmpty && !output.trim()) {
@@ -101,7 +129,7 @@ export async function renderContentWithFallback(page, property, context, options
// on English for. // on English for.
if (isFallbackableError(error) && context.getEnglishPage) { if (isFallbackableError(error) && context.getEnglishPage) {
const enPage = context.getEnglishPage(context) const enPage = context.getEnglishPage(context)
const englishTemplate = enPage[property] const englishTemplate = (enPage as any)[property] as string
// If you don't change the context, it'll confuse the liquid plugins // If you don't change the context, it'll confuse the liquid plugins
// like `data.js` that uses `environment.scope.currentLanguage` // like `data.js` that uses `environment.scope.currentLanguage`
const enContext = Object.assign({}, context, { currentLanguage: 'en' }) const enContext = Object.assign({}, context, { currentLanguage: 'en' })
@@ -112,7 +140,7 @@ export async function renderContentWithFallback(page, property, context, options
// Add HTML comment with error details for non-English languages // Add HTML comment with error details for non-English languages
// Skip for textOnly rendering to avoid breaking plain text output // Skip for textOnly rendering to avoid breaking plain text output
if (context.currentLanguage !== 'en' && !options?.textOnly) { if (context.currentLanguage !== 'en' && !options?.textOnly) {
const errorComment = createTranslationFallbackComment(error, property) const errorComment = createTranslationFallbackComment(error as Error, property)
return errorComment + '\n' + fallbackContent return errorComment + '\n' + fallbackContent
} }
@@ -137,19 +165,23 @@ export async function renderContentWithFallback(page, property, context, options
// (enContext) => renderContent(enTrack.title, enContext, renderOpts) // (enContext) => renderContent(enTrack.title, enContext, renderOpts)
// ) // )
// //
export async function executeWithFallback(context, callable, fallback) { export async function executeWithFallback<T>(
context: Context,
callable: (context: Context) => T | Promise<T>,
fallback: (enContext: Context) => T | Promise<T>,
): Promise<T> {
try { try {
return await callable(context) return await Promise.resolve(callable(context))
} catch (error) { } catch (error) {
if (isFallbackableError(error) && context.currentLanguage !== 'en') { if (isFallbackableError(error) && context.currentLanguage !== 'en') {
const enContext = Object.assign({}, context, { currentLanguage: 'en' }) const enContext = Object.assign({}, context, { currentLanguage: 'en' })
const fallbackContent = await fallback(enContext) const fallbackContent = await Promise.resolve(fallback(enContext))
// Add HTML comment with error details for non-English languages // Add HTML comment with error details for non-English languages
// Only for HTML content (detected by presence of HTML tags) // Only for HTML content (detected by presence of HTML tags)
if (typeof fallbackContent === 'string' && /<[^>]+>/.test(fallbackContent)) { if (typeof fallbackContent === 'string' && /<[^>]+>/.test(fallbackContent)) {
const errorComment = createTranslationFallbackComment(error, 'content') const errorComment = createTranslationFallbackComment(error as Error, 'content')
return errorComment + '\n' + fallbackContent return (errorComment + '\n' + fallbackContent) as T
} }
return fallbackContent return fallbackContent